source: AE/Installer/trunk/source/installer.cpp@ 545

Last change on this file since 545 was 543, checked in by gumby, 15 years ago

Fixed PC issues, added exception handling.

File size: 65.6 KB
RevLine 
[487]1/***************************************************************************\
2| Project: AE Installer |
3| By: Gumby & Iritscen |
4| File: Installer.cpp |
5| Function: Contains the real meat of the installation process. Mm, beefy. |
6| Created: 24/05/2009 19:39:00 |
7\***************************************************************************/
[325]8
[487]9// TODO: Load credits from text resource file
10// TODO: Clear mod info fields when mod is de-selected
11
[489]12//#define DEBUG
13#ifdef WIN32
14//#include <windows.h>
15#define popen _popen
16#endif
[487]17#include "boost/date_time/gregorian/gregorian.hpp"
18#include "boost/date_time/date_parsing.hpp"
19#include "boost/date_time/posix_time/posix_time.hpp"
[504]20#include <boost/algorithm/string.hpp>
[325]21#include "installer.h"
[487]22#include "aeinstallerapp.h"
[325]23
[487]24using namespace boost::gregorian;
25using namespace boost::posix_time;
[325]26
[487]27// externs declared in installer.h
28string strInstallCfg = "../GameDataFolder/Add.cfg";
29string strEUFN = "Edition"; // GetUpdateStatus() may set this to "Edition-patch" later, but this is the assumed name of the new Edition folder in Updates/
[511]30extern MainWindow* TheWindow;
[325]31
[487]32int globalizeData(void)
[325]33{
[487]34 busy = 1;
35 using boost::lexical_cast;
36 using boost::bad_lexical_cast;
37 using namespace boost::gregorian;
38 using namespace boost::posix_time;
39 ptime start_time(second_clock::local_time());
40
41 setStatusArea("Globalizing!");
[325]42 int err = 0;
[487]43 int parts_done = 0;
44 remove("Globalize.log");
45 ofstream logfile("Globalize.log");
46 logfile << "Globalization started " << to_simple_string(start_time) << endl;
47
48 try { // the levels Oni has...probably should have made a string array. Oops.
49 char levels_cstr[15][3] = {"0", "1", "2", "3", "4", "6", "8", "9", "10", "11", "12", "13", "14", "18", "19"};
50 vector<string> levels;
51 for (int f = 0; f < 15; f++) {
52 levels.push_back(levels_cstr[f]);
[325]53 }
54
55 path Characters = "../GameDataFolder/level0_Characters";
56 path Particles = "../GameDataFolder/level0_Particles";
57 path Archive = "../GameDataFolder/Archive";
58 path Textures = "../GameDataFolder/level0_Textures";
59 path Sounds = "../GameDataFolder/level0_Sounds";
60 path Animations = "../GameDataFolder/level0_Animations";
61 path TRAC = Animations / "level0_TRAC";
62 path TRAM = Animations / "level0_TRAM";
[487]63
64 vector<path> GDFPaths;
65 GDFPaths.push_back(Particles);
66 GDFPaths.push_back(Textures);
67 GDFPaths.push_back(Sounds);
68 GDFPaths.push_back(TRAC);
69 GDFPaths.push_back(TRAM);
70
71 path VanillaCharacters = "VanillaDats/level0_Final/level0_Characters/level0_Characters.oni";
72 path VanillaParticles = "VanillaDats/level0_Final/level0_Particles/level0_Particles.oni";
73 path VanillaTextures = "VanillaDats/level0_Final/level0_Textures/level0_Textures.oni";
74 path VanillaSounds = "VanillaDats/level0_Final/level0_Sounds/level0_Sounds.oni";
75 path VanillaAnimations = "VanillaDats/level0_Final/level0_Animations/level0_Animations.oni";
76 path VanillaTRAC = "VanillaDats/level0_Final/level0_Animations/level0_TRAC.oni";
77 path VanillaTRAM = "VanillaDats/level0_Final/level0_Animations/level0_TRAM.oni";
78
79 vector<path> VanillaPaths;
80
81 VanillaPaths.push_back(VanillaParticles);
82 VanillaPaths.push_back(VanillaTextures);
83 VanillaPaths.push_back(VanillaSounds);
84 VanillaPaths.push_back(VanillaTRAC);
85 VanillaPaths.push_back(VanillaTRAM);
86
87 setStatusArea("Removing old GameDataFolder...\n");
88 logfile << "Removing old GameDataFolder...\n";
89 remove_all( "../GameDataFolder/" );
90 setStatusArea("Creating needed directories...");
91 logfile << "Creating needed directories...\n";
[325]92 create_directory( "../GameDataFolder/" );
[487]93
[325]94 create_directory( "packages" );
95
[487]96 if (exists("VanillaDats")) remove_all("VanillaDats");
97 create_directory( "VanillaDats" );
98 create_directory( "VanillaDats/level0_Final/" );
[325]99 create_directory( Characters );
100 create_directory( Particles );
101 create_directory( Archive );
102 create_directory( Textures );
103 create_directory( Sounds );
104 create_directory( Animations );
105 create_directory( TRAC );
106 create_directory( TRAM );
[487]107 int num_levels = 0;
108 for(int i = 1; i < 15; i++)
109 {
110 if (exists("../../GameDataFolder/level" + levels[i] + "_Final.dat")) {
111 num_levels++;
112
113 }
114 }
115 logfile << "Exporting and moving...\n\n";
116 int total_steps = 8 + 2 * num_levels;
[325]117
118 for(int i = 0; i < 15; i++)
[487]119 {
120 if (exists("../../GameDataFolder/level" + levels[i] + "_Final.dat")) {
121 logfile << "level" << levels[i] << "_Final\n";
122 logfile << "\tExporting level" << levels[i] << "_Final.dat\n";
123 setStatusArea("Step " + lexical_cast<std::string>(parts_done + 1) + "/" + lexical_cast<std::string>(total_steps) + " exporting level" + levels[i]+"_Final.dat");
124 create_directory( "../GameDataFolder/level" + levels[i] + "_Final" );
125 system((strOniSplit + " -export ../GameDataFolder/level" + levels[i] + "_Final ../../GameDataFolder/level" + levels[i] + "_Final.dat").c_str());
126 create_directory( "VanillaDats/level" + levels[i] + "_Final" );
127 create_directory( "VanillaDats/level" + levels[i] + "_Final/level" + levels[i] + "_Final" );
128
129 //Moves the AKEV and other files into a safe directory so that level specific textures are not globalized...
130 if ( strcmp(levels[i].c_str(), "0") ){
131 create_directory( "../GameDataFolder/level" + levels[i] + "_Final/AKEV" );
132 system((strOniSplit + " -move:overwrite ../GameDataFolder/level" + levels[i] + "_Final/AKEV ../GameDataFolder/level" + levels[i] + "_Final/AKEV*.oni").c_str());
133
134 }
135
136 directory_iterator end_iter;
137 for ( directory_iterator dir_itr( "../GameDataFolder/level" + levels[i] + "_Final" ); dir_itr != end_iter; ++dir_itr )
[325]138 {
[487]139 if ( is_regular_file( dir_itr->status() ) )
[325]140 {
[487]141 if ( dir_itr->path().filename().substr(0,8) == "TXMPfail" ||
142 dir_itr->path().filename().substr(0,9) == "TXMPlevel" ||
143 ( dir_itr->path().filename().substr(0,4) == "TXMP" && dir_itr->path().filename().find("intro")!=string::npos) ||
144 dir_itr->path().filename().substr(0,4) == "TXMB" ||
145 dir_itr->path().filename() == "M3GMpowerup_lsi.oni" ||
146 dir_itr->path().filename() == "TXMPlsi_icon.oni" ||
147 ( dir_itr->path().filename().substr(0,4) == "TXMB" && dir_itr->path().filename().find("splash_screen.oni")!=string::npos) )
148 {
149 cout <<dir_itr->path().filename() << "\n";
150 create_directory( dir_itr->path().parent_path() / "NoGlobal");
151 if(!exists( dir_itr->path().parent_path() / "NoGlobal" / dir_itr->filename())) rename(dir_itr->path(), dir_itr->path().parent_path() / "NoGlobal" /
152 dir_itr->filename());
153 else remove(dir_itr->path());
154 }
155 else if (dir_itr->path().filename().substr(0,4) == "TRAC"
156 ) {
157 cout <<dir_itr->path().filename() << "\n";
158 if(!exists( TRAC / dir_itr->filename())) rename(dir_itr->path(), TRAC / dir_itr->filename());
159 else remove(dir_itr->path());
160 }
161 else if (dir_itr->path().filename().substr(0,4) == "TRAM") {
162 cout <<dir_itr->path().filename() << "\n";
163 if(!exists( TRAM / dir_itr->filename())) rename(dir_itr->path(), TRAM / dir_itr->filename());
164 else remove(dir_itr->path());
165 }
166 else if (dir_itr->path().filename().substr(0,4) == "ONSK" ||
167 dir_itr->path().filename().substr(0,4) == "TXMP") {
168 cout <<dir_itr->path().filename() << "\n";\
169 create_directory( dir_itr->path().parent_path() / "TexFix");
170 if(!exists( Textures / dir_itr->filename())) rename(dir_itr->path(), Textures / dir_itr->filename());
171 }
172 else if (dir_itr->path().filename().substr(0,4) == "ONCC"
173 || dir_itr->path().filename().substr(0,4) == "TRBS"
174 || dir_itr->path().filename().substr(0,4) == "ONCV"
175 || dir_itr->path().filename().substr(0,4) == "ONVL"
176 || dir_itr->path().filename().substr(0,4) == "TRMA"
177 || dir_itr->path().filename().substr(0,4) == "TRSC"
178 || dir_itr->path().filename().substr(0,4) == "TRAS") {
179 cout <<dir_itr->path().filename() << "\n";
180 if(!exists( Characters / dir_itr->filename())) rename(dir_itr->path(), Characters / dir_itr->filename());
181 else remove(dir_itr->path());
182 }
183 else if (dir_itr->path().filename().substr(0,4) == "OSBD"
184 || dir_itr->path().filename().substr(0,4) == "SNDD") {
185 cout << dir_itr->path().filename() << "\n";
186 if(!exists( Sounds / dir_itr->filename())) rename(dir_itr->path(), Sounds / dir_itr->filename());
187 else remove(dir_itr->path());
188 }
189 else if (dir_itr->path().filename().substr(0,5) == "BINA3"
190 || dir_itr->path().filename().substr(0,10) == "M3GMdebris"
191 || dir_itr->path().filename() == "M3GMtoxic_bubble.oni"
192 || dir_itr->path().filename().substr(0,8) == "M3GMelec"
193 || dir_itr->path().filename().substr(0,7) == "M3GMrat"
194 || dir_itr->path().filename().substr(0,7) == "M3GMjet"
195 || dir_itr->path().filename().substr(0,9) == "M3GMbomb_"
196 || dir_itr->path().filename() == "M3GMbarab_swave.oni"
197 || dir_itr->path().filename() == "M3GMbloodyfoot.oni"
198 ){
199 cout <<dir_itr->path().filename() << "\n";
200 if(!exists( Particles / dir_itr->filename())) rename(dir_itr->path(), Particles / dir_itr->filename());
201 else remove(dir_itr->path());
202 }
203 else if (dir_itr->path().filename().substr(0,4) == "AGDB"
204 || dir_itr->path().filename().substr(0,4) == "TRCM") {
205 cout <<dir_itr->path().filename() << "\n";
206
207 if(!exists( Archive / dir_itr->filename())) rename(dir_itr->path(), Archive / dir_itr->filename());
208 else remove(dir_itr->path());
209 }
210 else if (dir_itr->path().filename().substr(0,4) == "ONWC") { //fix for buggy ONWC overriding
211 cout <<dir_itr->path().filename() << "\n";
212
213 if(!exists( "VanillaDats/level0_Final/level0_Final/" + dir_itr->filename()))
214 rename(dir_itr->path(), "VanillaDats/level0_Final/level0_Final/" + dir_itr->filename());
215 else remove(dir_itr->path());
216 }
[325]217
[487]218 if (exists(dir_itr->path())) {
219
220 }
221 else {
222 //logfile << "\tMoved file: " << dir_itr->path().filename() << "\n";
223 }
[325]224 }
[487]225
226
227
[325]228 }
229
[487]230 logfile << "\tCleaning up TXMPs...\n";
231 system( (strOniSplit + " -move:delete " + Textures.string() + " ../GameDataFolder/level" + levels[i] + "_Final/TXMP*.oni").c_str());
[325]232
[487]233
234 if ( strcmp(levels[i].c_str(), "0") ){
[506]235 system((strOniSplit + " -move:overwrite ../GameDataFolder/level" + levels[i] + "_Final ../GameDataFolder/level" + levels[i] +
236 "_Final/AKEV/AKEV*.oni").c_str());
[487]237 remove( "../GameDataFolder/level" + levels[i] + "_Final/AKEV" );
238 }
239
240 parts_done++;
241
242 setProgressBar( (int)(1000 * (float)(parts_done) / (float)(total_steps) ));
243
[325]244 }
245 }
[487]246 logfile << "Reimporting levels\n";
[325]247 for (int i = 0; i < 15; i++)
248 {
[487]249 logfile << "\tReimporting level" << levels[i] << "_Final.oni\n";
[506]250 setStatusArea("Step " + lexical_cast<std::string>(parts_done + 1) + "/" + lexical_cast<std::string>(total_steps) + " reimporting level" +
251 levels[i] + "_Final.oni");
[487]252 logfile << (strOniSplit + " " + strImportOption + " ../GameDataFolder/level" + levels[i] + "_Final VanillaDats/level" + levels[i] + "_Final/level"
253 + levels[i] + "_Final/level" + levels[i] + "_Final.oni >> Globalize.log").c_str() << '\n';
254 string sys_str = (strOniSplit + " " + strImportOption + " ../GameDataFolder/level" + levels[i] + "_Final VanillaDats/level" + levels[i] + "_Final/level"
255 + levels[i] + "_Final/level" + levels[i] + "_Final.oni");
256 system(sys_str.c_str() );
257 setProgressBar( (int)(1000 * (float)(parts_done) / (float)(total_steps) ));
258 parts_done++;
[325]259 }
260 create_directory( VanillaParticles.parent_path() );
261 create_directory( VanillaTextures.parent_path() );
262 create_directory( VanillaSounds.parent_path() );
263 create_directory( VanillaAnimations.remove_filename() );
264
[487]265 for(unsigned int j = 0; j < GDFPaths.size(); j++) {
266 logfile << "\tReimporting " << GDFPaths[j].filename() << ".oni\n";
267 setStatusArea("Step " + lexical_cast<std::string>(parts_done + 1) + "/" + lexical_cast<std::string>(total_steps) + ": reimporting " + GDFPaths[j].filename() );
268 system((strOniSplit + " " + strImportOption + " " + GDFPaths[j].string() + " " + VanillaPaths[j].string()).c_str());
269 parts_done++;
270 setProgressBar( (int)(1000 * (float)(parts_done) / (float)(total_steps) ));
271 }
272 logfile << "\nMoving level0_Characters\n";
273 setStatusArea("Step " + lexical_cast<std::string>(parts_done + 1) + "/" + lexical_cast<std::string>(total_steps) + ": moving level0_Characters" );
274 copy((path)"../GameDataFolder/level0_Characters", (path)("VanillaDats/level0_Final"));
275 GDFPaths.push_back( Characters );
[504]276 //concactates level0....
[487]277 for(int i = 0; i < GDFPaths.size(); i++)
278 {
279 directory_iterator end_iter;
280 for ( directory_iterator dir_itr( GDFPaths[i] ); dir_itr != end_iter; ++dir_itr )
[325]281 {
[487]282 try
[325]283 {
[487]284 rename(dir_itr->path(), "../GameDataFolder/level0_Final/" + dir_itr->path().filename() );
[325]285 }
[487]286 catch(exception &ex) {
287
288 }
[325]289 }
290 }
[504]291 //?: syntax is fun.
292 //condition ? value_if_true : value_if_false
293 (is_empty(Characters) ? remove( Characters ) : 1);
294 (is_empty(Particles) ? remove( Particles ) : 1);
295 (is_empty(Textures) ? remove( Textures ) : 1);
296 (is_empty(Sounds) ? remove( Sounds ) : 1);
297 (is_empty(TRAC) ? remove( TRAC ) : 1);
298 (is_empty(TRAM) ? remove( TRAM ) : 1);
299 (is_empty(Animations) ? remove( Animations ) : 1);
[325]300
[487]301 create_directory((path)"../GameDataFolder/IGMD");
302 copy((path)"packages/VanillaBSL/IGMD", (path)"../GameDataFolder");
303 setProgressBar( 1000 );
[504]304
305 if(exists("../../persist.dat") && !exists("../persist.dat")) copy("../../persist.dat","..");
306 if(exists("../../key_config.txt")&& !exists("../key_config.txt")) copy("../../key_config.txt","..");
307
[487]308#ifndef WIN32
309 /* On Mac only, set the current GDF to the AE GDF by writing to Oni's global preferences file (thankfully a standard OS X ".plist" XML file).
310 Tests for presence of prefs with [ -f ] before doing anything so it doesn't create a partial prefs file -- just in case user has never
311 run Oni before :-p */
[506]312 string fullAEpath = escapePath(system_complete(".").parent_path().parent_path().string()); // get full path for Edition/ (Oni wants folder that *contains* the GDF)
[504]313 //bad Iritscen, bad! fixed buffers can cause crashes.
314 /*char prefsCommand[300] = "[ -f ~/Library/Preferences/com.godgames.oni.plist ] && defaults write com.godgames.oni RetailInstallationPath -string '";
[500]315 strcat(prefsCommand, fullAEpath.c_str());
[487]316 strcat(prefsCommand, "'"); // path string is enclosed in single quotes to avoid the need to escape UNIX-unfriendly characters
[504]317 */
318 string prefsCommand = "[ -f ~/Library/Preferences/com.godgames.oni.plist ] && defaults write com.godgames.oni RetailInstallationPath -string '"
319 + fullAEpath + "'";
320 system(prefsCommand.c_str());
321
[487]322#endif
323
324 setStatusArea((string)"Done! Now select your mod packages and click install.");
[325]325 }
[487]326 catch (exception & ex) {
327 setStatusArea("Warning, handled exception: " + (string)ex.what());
328 }
[325]329
[487]330 ptime end_time(second_clock::local_time());
331 time_period total_time (start_time, end_time);
332 logfile << "\n\nGlobalization ended " << to_simple_string(end_time) << "\nThe process took " << total_time.length();
333 logfile.close();
334 busy = 0;
[325]335 return err;
336}
337
[487]338vector<ModPackage> getPackages(string packageDir)
[325]339{
340 vector<ModPackage> packages;
[500]341 ModPackage package;
[487]342 packages.reserve(256);
[325]343 fstream file;
344 string filename = "\0";
345 string MODINFO_CFG = "Mod_Info.cfg";
346
347 try
348 {
[487]349 for (directory_iterator dir_itr(packageDir), end_itr; dir_itr != end_itr; ++dir_itr)
[325]350 {
351 file.open((dir_itr->path().string() + "/" + MODINFO_CFG).c_str());
352
[500]353 if (!file.fail())
[325]354 {
[505]355 package = fileToModPackage(file, dir_itr->path().filename());
[500]356 if (package.installerVersion.compare(INSTALLER_VERSION) < 1) // if mod requires newer version of the Installer, we won't add it to the list
[502]357 {
358#ifdef WIN32
359 if (!package.platform.compare("Windows") || !package.platform.compare("Both")) // don't show package if it's not for the right OS
360#else
361 if (!package.platform.compare("Macintosh") || !package.platform.compare("Both"))
362#endif
363 packages.push_back(package);
364 }
[325]365 }
366 file.close();
367 file.clear();
368 }
[487]369 sort(packages.begin(), packages.end());
[325]370 }
371 catch (const std::exception & ex)
372 {
373 cout << "Warning, something odd happened!\n";
374 }
375
376 return packages;
377}
378
[505]379ModPackage fileToModPackage(fstream &file, string modName)
[506]380{
[325]381 ModPackage package;
382 string line;
[505]383 const string AEInstallVersion = "AEInstallVersion"; // used for comparing to the current token...
384 const string NameOfMod = "NameOfMod";
385 const string ARROW = "->";
386 const string ModString = "ModString";
387 const string ModVersion = "ModVersion";
388 const string Platform = "Platform";
389 const string HasOnis = "HasOnis";
390 const string HasDeltas = "HasDeltas";
391 const string HasBSL = "HasBSL";
392 const string HasDats = "HasDats";
393 const string IsEngine = "IsEngine";
394 const string Readme = "Readme";
395 const string GlobalNeeded = "GlobalNeeded";
396 const string Category = "Category";
397 const string Creator = "Creator";
398 package.modStringName = modName;
[500]399 while (!file.eof())
[325]400 {
[500]401 getline(file,line);
[325]402 vector<string> tokens;
403 vector<string>::iterator iter;
[500]404 tokenize(line, tokens);
405 if (tokens.capacity() >= 3)
406 {
407 iter = tokens.begin();
408
409 if (!AEInstallVersion.compare(*iter))
410 {
411 iter++; iter++;
412 package.installerVersion = *iter;
413 }
414 else if (!NameOfMod.compare(*iter))
415 {
416 for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) // iterates through the words, ends if it reaches the end of the line or a "//" comment
417 {
418 if (ARROW.compare(*iter) && NameOfMod.compare(*iter)) // ignores "->" and "NameOfMod"
[325]419 package.name += *iter + " ";
420 }
421 }
[500]422 else if (!ModString.compare(*iter))
423 {
[325]424 iter++; iter++;
[505]425 //package.modStringName = *iter;
[325]426 iter++;
[504]427 package.modStringVersion = atof((*iter).c_str());
[325]428 }
[505]429 else if (!ModString.compare(*iter))
430 {
431 iter++; iter++;
432 package.modStringVersion = atof((*iter).c_str());
433 }
[502]434 else if (!Platform.compare(*iter))
435 {
436 iter++; iter++;
437 package.platform = *iter;
438 }
[500]439 else if (!HasOnis.compare(*iter))
440 {
[325]441 iter++; iter++;
[500]442 if (boost::iequals(*iter, "Yes")) package.hasOnis = 1;
[487]443 }
[500]444 else if (!HasBSL.compare(*iter))
445 {
446 iter++; iter++;
[505]447 if (boost::iequals(*iter, "Yes")) package.hasBSL = true;
[500]448 else if (boost::iequals(*iter, "Addon")) package.hasAddon = true;
[325]449 }
[500]450 else if (!HasDeltas.compare(*iter))
451 {
[325]452 iter++; iter++;
[500]453 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDeltas = 1;
[325]454 }
[500]455 else if (!HasDats.compare(*iter))
456 {
[325]457 iter++; iter++;
[500]458 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDats = 1;
[325]459 }
[500]460 else if (!IsEngine.compare(*iter))
461 {
[325]462 iter++; iter++;
[500]463 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.isEngine = 1;
[325]464 }
[500]465 else if (!GlobalNeeded.compare(*iter))
466 {
[325]467 iter++; iter++;
[500]468 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.globalNeeded = 1;
469 else if (toupper((*iter)[0]) == 'N' && toupper((*iter)[1]) == 'O') package.globalNeeded = 1; // only place where checking for "No" is important atm
[325]470 }
[500]471 else if (!Category.compare(*iter))
472 {
473 for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++)
474 {
475 if (ARROW.compare(*iter) && Category.compare(*iter)) // ignores "->" and "Category"
[325]476 package.category += *iter + " ";
477 }
478 }
[500]479 else if (!Creator.compare(*iter))
480 {
481 for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++)
482 {
483 if (ARROW.compare(*iter) && Creator.compare(*iter)) // ignores "->" and "Creator"
[325]484 package.creator += *iter + " ";
485 }
486 }
[500]487 else if (!Readme.compare(*iter))
488 {
489 for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++)
490 {
491 if (ARROW.compare(*iter) && Readme.compare(*iter)) // ignores "->" and "Readme"
492 {
493 if (!(*iter).compare("\\n")) package.readme += '\n';
[325]494 else package.readme += *iter + " ";
495 }
496 }
497 }
498 }
499 }
[487]500
[325]501 return package;
502}
503
504void recompileAll(vector<string> installedMods)
[487]505{try {
506 busy = 1;
507 using namespace boost::gregorian;
508 using namespace boost::posix_time;
509 using boost::lexical_cast;
510 using boost::bad_lexical_cast;
511 path vanilla_dir = "./VanillaDats/";
[325]512 string importCommand = "";
513 int numberOfDats = 0;
514 int j = 1;
515 string datString;
[487]516
517 setStatusArea("Importing levels...");
518
[325]519 std::stringstream out;
520
[487]521 ptime start_time(second_clock::local_time());
522 clearOldDats();
[325]523
[487]524 if(exists("Install.log")) remove("Install.log");
525 ofstream logfile("Install.log");
526 logfile << "Mod Installation started " << to_simple_string(start_time) << endl;
527 logfile.close();
528
529 if(splitInstances == true)
530 {
[325]531 recursive_directory_iterator end_iter;
532
533 for ( recursive_directory_iterator dir_itr( vanilla_dir );
534 dir_itr != end_iter;
535 ++dir_itr )
536 {
537 try{
538 if ( is_directory( dir_itr->status() ) && dir_itr.level() == 1)
539 {
540 numberOfDats++;
541 }
542 }
[487]543 catch(exception & ex) {
544 remove("Install.log");
545 ofstream logfile("Install.log");
[325]546
[487]547 logfile << "Warning, exception " << ex.what() << "!";
548 setStatusArea("Warning, exception " + (string)ex.what() + "!");
549 logfile.close();
[325]550 }
551 }
552 try {
[487]553 out << numberOfDats;
554 datString = out.str();
[325]555 for ( recursive_directory_iterator dir_itr( vanilla_dir );
556 dir_itr != end_iter;
557 ++dir_itr )
558 {
559 try
560 {
561 if ( is_directory( dir_itr->status() ) && dir_itr.level() == 1)
562 {
563 importCommand = strOniSplit + " " + strImportOption + " " + dir_itr->path().parent_path().string() + '/' + dir_itr->path().filename();
[487]564 for (unsigned int i = 0; i < installedMods.size(); ++i) {
[325]565 if (exists("packages/" + installedMods[i] + "/oni/" + dir_itr->path().parent_path().filename() + '/' + dir_itr->path().filename() ))
566 importCommand += " packages/" + installedMods[i] + "/oni/" + dir_itr->path().parent_path().filename() + '/' + dir_itr->path().filename();
567 }
[487]568 importCommand += " ../GameDataFolder/" + dir_itr->path().filename() + ".dat >> Install.log";
[325]569
[487]570
[325]571 setProgressBar( (int)(1000 * (float)(j-1) / (float)numberOfDats) ); //100% * dat we're on / total dats
[506]572 setStatusArea("Step " + lexical_cast<std::string>(j) + '/' + lexical_cast<std::string>(numberOfDats)+ ": Importing " +
573 dir_itr->path().filename() + " ");
[325]574
575 system(importCommand.c_str());
[487]576 j++;
[325]577 }
578 }
579 catch ( const std::exception & ex )
580 {
[487]581 remove("Install.log");
582 ofstream logfile("Install.log");
583 logfile << "Warning, exception " << ex.what() << "!";
584 setStatusArea("Warning, exception " + (string)ex.what() + "!");
585 logfile.close();
[325]586 }
587 }
588 }
589 catch( const std::exception & ex ) {
[487]590 remove("Install.log");
591 ofstream logfile("Install.log");
592 logfile << "Warning, exception " << ex.what() << "!";
593 setStatusArea("Warning, exception " + (string)ex.what() + "!");
594 logfile.close();
[325]595 }
596 }
[487]597 else if(splitInstances == false){
[325]598 directory_iterator end_iter;
599
600 for ( directory_iterator dir_itr( vanilla_dir );
601 dir_itr != end_iter;
602 ++dir_itr )
603 {
604 if ( is_directory( dir_itr->status() ) )
605 {
606 numberOfDats++;
607 }
608 }
609
610 out << numberOfDats;
611 datString = out.str();
612
613 for ( directory_iterator dir_itr( vanilla_dir );
614 dir_itr != end_iter;
615 ++dir_itr )
616 {
617 try
618 {
619 if ( is_directory( dir_itr->status() ) )
620 {
[487]621 importCommand = strOniSplit + " " + strImportOption + " " + vanilla_dir.string() + dir_itr->path().filename() + " ";
622 for (unsigned int i = 0; i < installedMods.size(); ++i) {
[325]623 if (exists("packages/" + installedMods[i] + "/oni/" + dir_itr->path().filename() ))
624 importCommand += " packages/" + installedMods[i] + "/oni/" + dir_itr->path().filename();
625 }
[487]626 importCommand += " ../GameDataFolder/" + dir_itr->path().filename() + ".dat >> Install.log";
[325]627
628 setProgressBar( (int)(1000 * (float)(j-1) / (float)numberOfDats) ); //100% * dat we're on / total dats
[506]629 setStatusArea("Step " + lexical_cast<std::string>(j) + '/' + lexical_cast<std::string>(numberOfDats)+ ": Importing " +
630 dir_itr->path().filename() + " ");
[325]631 system(importCommand.c_str());
632 j++;
633 }
634 }
635 catch ( const std::exception & ex )
636 {
[487]637 remove("Install.log");
638 ofstream logfile("Install.log");
639 logfile << "Warning, exception " << ex.what() << "!";
640 setStatusArea("Warning, exception " + (string)ex.what() + "!");
641 logfile.close();
[325]642 }
643 }
644 }
[487]645
646 vector<string> BSLfolders;
647 vector<string> skippedfolders;
648
649 ofstream BSLlog("BSL.log");
650 for ( directory_iterator dir_itr( "../GameDataFolder/IGMD/" ), end_itr;
651 dir_itr != end_itr;
652 ++dir_itr ) {
653 if( exists(dir_itr->path().string() + "/ignore.txt") ){
654 BSLfolders.push_back(dir_itr->path().filename());
655 skippedfolders.push_back(dir_itr->path().filename());
656 }
657 }
658
659 for (int i = installedMods.size() - 1; i >= 0; i--) { //Iterates through the installed mods (backwards :P)
660 for (unsigned int j = 0; j < globalPackages.size(); ++j) { //looking in the global packages
661 if (globalPackages[j].modStringName == installedMods[i]) { //for a mod that has BSL in it
662 if(!(globalPackages[j].hasAddon || globalPackages[j].hasBSL)) break; //skip non-BSL
663 if( exists( "packages/" + globalPackages[j].modStringName + "/BSL/" ) ) {
664 copyBSL("packages/" + globalPackages[j].modStringName + "/BSL", BSLfolders, globalPackages[j] );
665 BSLlog << "Copied " << globalPackages[j].modStringName << "!\n";
666 }
667 }
668 }
669 }
670
671 ModPackage emptyPackage;
672 emptyPackage.modStringName = "VanillaBSL";
673 emptyPackage.hasBSL = 1;
674 copyBSL("packages/VanillaBSL/IGMD", BSLfolders, emptyPackage);
675 BSLlog.close();
676
677 logfile << "Writing config file";
[325]678 writeInstalledMods(installedMods);
679 setProgressBar(1000);
[487]680
681 string finallyDone = "Done! You can now play Oni.";
682 setStatusArea(finallyDone);
683
684 ptime end_time(second_clock::local_time());
685 time_period total_time (start_time, end_time);
686 ofstream logfile2("Install.log", ios::app | ios::ate);
687 string outstring = (string)"\n\nGlobalization ended " + to_simple_string(end_time) + "\nThe process took ";// + (string)total_time.length();
688
689 logfile2 << "\nInstallation ended " << to_simple_string(end_time) << "\nThe process took " << total_time.length();
690 logfile2.close();
691
692 Sleep(1000);
[325]693 setProgressBar(0);
[487]694}
695 catch(exception & ex) {
696 remove("Install.log"); //why did we do this? :|
697 ofstream logfile("Install.log");
698 logfile << "Warning, exception " << ex.what() << "!";
699 setStatusArea("Warning, exception " + (string)ex.what() + "!");
700 logfile.close();
701 }
702 busy = 0;
703}
704
705void copyBSL(string copypath, vector<string>& BSLfolders, ModPackage pkg)
706{
[506]707 ofstream BSLlog("BSL.log", ios::app );
[325]708
[487]709 try {
710 for ( directory_iterator dir_itr( copypath ), end_itr;
711 dir_itr != end_itr;
712 ++dir_itr ) {
713
714 if ( is_directory( dir_itr->path() ) && dir_itr->path().string() != ".svn" ) {
715 BSLlog << "Testing " << dir_itr->path().string() << " HasBSL: " << pkg.hasBSL << " HasAddon: " << pkg.hasAddon << "\n";
716 int skip_folder = 0;
717
718 for(unsigned int k = 0; k < BSLfolders.size(); k++) {//iterate through already found BSL folders
719 BSLlog << "testing " << dir_itr->path().filename() << " vs " << BSLfolders[k] << "\n";
720 if(dir_itr->path().filename() == BSLfolders[k]) {
721 skip_folder = 1;
722 BSLlog << "skipping " << BSLfolders[k] << " in " << pkg.modStringName << "\n";
723 break;
724 }
725 }
726 if (!skip_folder && !exists("../GameDataFolder/IGMD/" + dir_itr->path().filename() + "/ignore.txt")) {
727 remove_all( "../GameDataFolder/IGMD/" + dir_itr->path().filename() );
728 Sleep(100);
729 create_directory( "../GameDataFolder/IGMD/" + dir_itr->path().filename());
730 BSLlog << "Copied " << dir_itr->path().string() << " in " << pkg.modStringName << "!\n";
731 for ( directory_iterator bsl_itr( dir_itr->path() );
732 bsl_itr != end_itr;
733 bsl_itr++ ) {
734 if ( bsl_itr->path().extension() == ".bsl" ) {
735 copy_file(bsl_itr->path(), "../GameDataFolder/IGMD/" + dir_itr->path().filename() + "/" + bsl_itr->path().filename());
736 }
737 }
[526]738 if( !pkg.hasAddon ) {
739 BSLfolders.push_back( dir_itr->path().filename() ); //add back check for addon
740 BSLlog << "Pushing " << dir_itr->path().filename() << "\n" ;
741 }
[487]742 }
743 }
744 }
745 }
746 catch ( const std::exception & ex )
747 {
748 setStatusArea("Warning, exception " + (string)ex.what() + "!");
749 while(1) Sleep(1000);
750 }
751 BSLlog.close();
752
[325]753}
754
[487]755
[325]756void writeInstalledMods(vector<string> installedMods)
[487]757{
[325]758 if ( exists( strInstallCfg ) )
759 {
760 remove( strInstallCfg );
761 }
762
763 ofstream file(strInstallCfg.c_str());
764
765 vector<string>list = installedMods;
766 vector<string>::iterator begin_iter = list.begin();
767 vector<string>::iterator end_iter = list.end();
768
769 sort( list.begin(), list.end() );
770
771 for( ; begin_iter != end_iter; ++begin_iter) {
772 file << *begin_iter << " ";
773 }
774
775 file.close();
776 file.clear();
777}
778
779vector<string> getInstallString(string Cfg)
780{
[487]781 vector<string> returnval;
[325]782 string line;
783 fstream file;
784
785 if (exists( Cfg ))
786 {
787 file.open(Cfg.c_str());
788 getline(file, line);
789 tokenize(line, returnval);
790 file.close();
791 file.clear();
792 sort(returnval.begin(), returnval.end());
793 }
794 else cout << "fail";
795
796 return returnval;
797}
798
[487]799/* GetUpdateStatus determines whether there is an update available. It is called once, *\
800| on launch, by AEInstallerApp::OnInit(), and not only passes back a #defined result |
801| code, but also oversees the setting of data in the global structures currentAE and |
802| updateAE, which tell the Installer all the version information it needs to know. |
803| ---Return Values--- |
804| UPDATE_LOG_READ_ERR -- A log file could not be opened |
805| UPDATE_INST_REPL_ERR -- The Installer self-updating process failed |
806| UPDATE_MNTH_REQD_ERR -- The update is a patch, and the monthly release it |
807| patches is not installed |
808| UPDATE_NO_UPD_AVAIL -- Either there isn't an update in place, or it's not |
809| newer than what's installed |
810| UPDATE_SIMP_AVAIL -- An update is available |
811| UPDATE_GLOB_AVAIL -- An update is available that requires re-globalization |
812| afterwards (because of some notable change in the AE) |
813| UPDATE_INST_AVAIL -- An update is available that first requires the |
814| Installer to be replaced (when the new Installer |
815| launches, this function will be called again but will |
816| return UPDATE_SIMP_AVAIL or UPDATE_GLOB_AVAIL) |
[506]817| UPDATE_PKG_AVAIL -- A newer version of individual package(s) is available |
[487]818\* UPDATE_CONT_UPD -- Currently unused */
819int GetUpdateStatus(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
820{
821 fstream currentAECfg, updateAECfg, updateLog;
822 string strInstaller = "Installer";
823 string strBeing = "being";
824 string strWas = "was"; // lol
825#ifdef WIN32
826 string strInstallerName = "AEInstaller.exe";
827#else
828 string strInstallerName = "Installer.app";
829#endif
830
831 // Try to get current AE's version info; if it doesn't exist, then the default version data for 2009-07 remains in place
832 if (exists("packages/Globalize/Install_Info.cfg"))
833 {
834 currentAECfg.open("packages/Globalize/Install_Info.cfg");
835 if (!currentAECfg.fail())
836 {
837 if (!ReadInstallInfoCfg(&currentAECfg, currentAE))
838 return UPDATE_LOG_READ_ERR;
839
840 currentAECfg.close();
841 currentAECfg.clear();
842 }
843 else
844 return UPDATE_LOG_READ_ERR;
845 }
[504]846
[542]847 // Is there an update in the updates/ folder, and is it a monthly release or a patch?
[504]848 bool firstParty = 0;
[542]849 // First create the folder if it's missing, so users are never left wondering where updates are supposed to be put
850 if (!exists("../updates"))
851 create_directory("../updates");
[504]852 if (exists("../updates/Edition"))
[487]853 {
[504]854 firstParty = 1;
855 }
856 else {
[487]857 strEUFN = "Edition-patch";
[504]858 if (exists("../updates/Edition-patch")) {
859 firstParty = 1;
860 }
861
[487]862 }
[504]863
864 if(firstParty) {
865 // Unlike the current AE's version info, we *need* to find the update's version info or we won't continue
866 string updateCfgPath = ("../updates/" + strEUFN + "/install/packages/Globalize/Install_Info.cfg");
867 updateAECfg.open(updateCfgPath.c_str());
868 if (!updateAECfg.fail())
869 {
870 if (!ReadInstallInfoCfg(&updateAECfg, updateAE))
871 return UPDATE_LOG_READ_ERR;
872
873 updateAECfg.close();
874 updateAECfg.clear();
875 }
876 else
[487]877 return UPDATE_LOG_READ_ERR;
878
[504]879 // Now we check for an Installer update in progress
880 if (exists("Update.log"))
[487]881 {
[504]882 updateLog.open("Update.log");
883 if (!updateLog.fail())
[487]884 {
[504]885 vector<string> lines;
886 string line;
887 int num_lines = 0;
888 bool readingInstallerVersion = false, doneReadingFile = false;
889
890 while (!updateLog.eof() && !doneReadingFile)
[487]891 {
[504]892 getline(updateLog, line);
893 lines.push_back(line);
894 num_lines++;
895 vector<string> tokens;
896 vector<string>::iterator iter;
897 tokenize(line, tokens);
898 iter = tokens.begin();
899 if (!readingInstallerVersion && tokens.capacity() >= 4)
[487]900 {
[504]901 if (!strInstaller.compare(*iter))
902 {
903 if (!strBeing.compare(*++iter))
904 readingInstallerVersion = true;
905 else if (!strWas.compare(*iter))
906 *installerJustUpdated = true; // our third indirect return value after currentAE and updateAE
907 }
[487]908 }
[504]909 else if (readingInstallerVersion && tokens.capacity() >= 3)
[487]910 {
[504]911 readingInstallerVersion = false;
912 string installerVersion = INSTALLER_VERSION;
913 if (installerVersion.compare(*iter)) // then the shell script-powered replacement failed
914 return UPDATE_INST_REPL_ERR;
915 else
[487]916 {
[504]917 updateLog.close();
918 updateLog.clear();
919 Sleep(1000);
920 remove("Update.log");
921 ofstream newUpdateLog("Update.log");
922 if (!newUpdateLog.fail())
[487]923 {
[504]924 // Write over old log with updated information
925 ptime startTime(second_clock::local_time());
926 string strStartTime = to_simple_string(startTime);
927 string newUpdateLine = installerVersion + " on " + strStartTime;
928 for (int a = 0; a < lines.capacity() - 2; a++) // if there were even lines in the log before this at all
929 {
930 newUpdateLog << lines[a].c_str();
931 newUpdateLog << "\n";
932 }
933 newUpdateLog << "Installer was updated to:\n";
934 newUpdateLog << newUpdateLine.c_str();
[506]935 *installerJustUpdated = true; // this value is indirectly returned to AEInstallerApp::OnInit()
[504]936 doneReadingFile = true;
937 newUpdateLog.close();
938 newUpdateLog.clear();
939 //return UPDATE_CONT_UPD; // as noted above, we are not using this return value; in fact, we want...
940 // ...the code to continue running down through the Edition version check
[487]941 }
[504]942 else
943 return UPDATE_LOG_READ_ERR;
[487]944 }
945 }
946 }
[504]947 updateLog.close();
948 updateLog.clear();
[487]949 }
[504]950 else
951 return UPDATE_LOG_READ_ERR;
[487]952 }
[504]953
954 if (updateAE->AEVersion.compare(currentAE->AEVersion) >= 1) // is the release update newer than what's installed?
955 {
956 if (!strEUFN.compare("Edition-patch")) // if update is a patch...
957 {
958 if (currentAE->AEVersion.compare(updateAE->AEVersion.substr(0, updateAE->AEVersion.length() - 1))) // ...is it for a different month?
959 return UPDATE_MNTH_REQD_ERR;
960 }
961 string strNewInstallerPath = "../updates/" + strEUFN + "/install/" + strInstallerName;
962 string installerVersion = INSTALLER_VERSION;
963 if (updateAE->InstallerVersion.compare(installerVersion) >= 1)
964 {
965 if (exists(strNewInstallerPath))
966 return UPDATE_INST_AVAIL;
967 }
968 else if (updateAE->globalizationRequired)
969 return UPDATE_GLOB_AVAIL;
970 else
971 return UPDATE_SIMP_AVAIL;
972 }
[487]973 }
[506]974 try
[487]975 {
[506]976 directory_iterator end;
977 if (exists("../updates"))
978 {
979 for (directory_iterator install_iter("../updates"); install_iter != end; ++install_iter)
[504]980 {
[506]981 ModPackage installedPackage, updatePackage;
982 if (is_directory(install_iter->path()) && exists(install_iter->path().string() + "/Mod_Info.cfg"))
983 {
984 fstream file;
985 file.open((install_iter->path().string() + "/Mod_Info.cfg").c_str());
986 if (!file.fail())
[509]987 updatePackage = fileToModPackage(file, install_iter->path().filename());
[506]988 else
989 {
990 file.close();
991 continue;
992 }
[509]993 if (exists("packages/" + install_iter->path().filename() + "/Mod_Info.cfg"))
[506]994 {
995 file.close();
[509]996 file.clear();
997 file.open(("packages/" + install_iter->path().filename() + "/Mod_Info.cfg").c_str());
[506]998 if (!file.fail())
[509]999 installedPackage = fileToModPackage(file, install_iter->path().filename());
[506]1000 file.close();
1001 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
1002 {
1003 if (updatePackage.installerVersion <= INSTALLER_VERSION)
1004 return UPDATE_PKG_AVAIL;
1005 }
1006 }
1007 else
1008 {
1009 file.close();
1010 return UPDATE_PKG_AVAIL;
1011 }
1012 }
[504]1013 }
[487]1014 }
1015 }
[504]1016 catch (exception & ex) {
1017 // setStatusArea("Warning, handled exception: " + (string)ex.what());
1018 }
1019
[487]1020 return UPDATE_NO_UPD_AVAIL;
1021}
1022
1023bool ReadInstallInfoCfg(fstream *fileHandler, Install_info_cfg *info_cfg)
1024{
1025 vector<string> tokens;
1026 vector<string>::iterator iter;
1027 string line;
1028 string strAEVersion = "AE_Version";
1029 string strInstallerVersion = "Installer_Version";
1030 string strDaodanVersion = "Daodan_Version";
1031 string strOniSplitVersion = "OniSplit_Version";
1032 string strGUIWinVersion = "GUI_Win_Version";
[508]1033 string strGUIMacVersion = "GUI_Mac_Version";
[487]1034 string strReglobalize = "Reglobalize";
1035 string strDeleteList = "Delete_List";
1036 string strArrow = "->";
1037 string strDoubleSlash = "//";
1038 string strYes = "Yes"; // this is getting silly
1039
1040 while (getline(*fileHandler, line))
1041 {
[499]1042 StripNewlines(&line);
[487]1043 tokenize(line, tokens);
1044 iter = tokens.begin();
1045
1046 if (tokens.size() >= 3)
1047 {
1048 if (!strAEVersion.compare(*iter))
1049 {
1050 if (!strArrow.compare(*++iter))
1051 info_cfg->AEVersion = *++iter;
1052 else
1053 return false;
1054 }
1055 else if (!strInstallerVersion.compare(*iter))
1056 {
1057 if (!strArrow.compare(*++iter))
1058 info_cfg->InstallerVersion = *++iter;
1059 else
1060 return false;
1061 }
1062 else if (!strDaodanVersion.compare(*iter))
1063 {
1064 if (!strArrow.compare(*++iter))
1065 info_cfg->DaodanVersion = *++iter;
1066 else
1067 return false;
1068 }
1069 else if (!strOniSplitVersion.compare(*iter))
1070 {
1071 if (!strArrow.compare(*++iter))
1072 info_cfg->OniSplitVersion = *++iter;
1073 else
1074 return false;
1075 }
1076 else if (!strGUIWinVersion.compare(*iter))
1077 {
1078 if (!strArrow.compare(*++iter))
1079 info_cfg->WinGUIVersion = *++iter;
1080 else
1081 return false;
1082 }
1083 else if (!strGUIMacVersion.compare(*iter))
1084 {
1085 if (!strArrow.compare(*++iter))
1086 info_cfg->MacGUIVersion = *++iter;
1087 else
1088 return false;
1089 }
1090 else if (!strReglobalize.compare(*iter))
1091 {
1092 if (!strArrow.compare(*++iter))
1093 {
1094 if (!strYes.compare(*++iter))
1095 info_cfg->globalizationRequired = true;
1096 }
1097 else
1098 return false;
1099 }
1100 else if (!strDeleteList.compare(*iter))
1101 {
1102 // We need to perform a totally customized parsing process on this data
1103 if (!strArrow.compare(*++iter))
1104 {
1105 vector<string> tokens2;
1106 tokenize(line, tokens2, ","); // the paths on this line are comma-delimited, so we parse it again
1107 vector<string>::iterator iter2 = tokens2.begin();
1108 string finalPath = "";
1109 for (; iter2 != tokens2.end(); iter2++)
1110 {
1111 finalPath = finalPath + *iter2;
1112
1113 string::size_type loc = finalPath.find("->", 0); // the first word will have "Delete_List ->" at the front, so let's cut that off
1114 if (loc != string::npos)
1115 finalPath = finalPath.substr(loc + 3, finalPath.size());
1116
1117 // If a path has '//' in it, it must contain some optional comments that were at the end of the Delete_List line
1118 loc = finalPath.find("//", 0);
1119 if (loc != string::npos)
1120 finalPath = finalPath.substr(0, loc);
1121
1122 // Trim a single space if it exists at the start or finish; putting more than one space after a comma will break this
1123 if (finalPath.at(0) == ' ')
1124 finalPath = finalPath.substr(1, finalPath.size());
1125 if (finalPath.at(finalPath.size() - 1) == ' ')
1126 finalPath = finalPath.substr(0, finalPath.size() - 1);
1127
1128 // If the tokenized path ends with a '\', then we assume it was followed by a comma
1129 if (finalPath.at(finalPath.size() - 1) == '\\')
1130 {
1131 finalPath = finalPath.substr(0, finalPath.size() - 1); // clip the '\' off the end of the string now that it served its purpose...
1132 finalPath = finalPath + ","; // ...and add the actual comma back at the end
1133 }
1134 else // we can add the path to deleteList, and clear the path; otherwise it will be added to on the next iteration
1135 {
1136 if (StringIsLegalPathForDeletion(finalPath)) // ...and it's not violating any of our security rules as to what can be deleted...
1137 info_cfg->deleteList.push_back(finalPath); // ...then add it to our deleteList in memory
1138 finalPath.clear(); // clear the token we were building up before the next pass
1139 }
1140 }
1141 }
1142 else
1143 return false;
1144 }
1145 }
1146 tokens.clear();
1147 }
1148
1149 return true;
1150}
1151
1152// TODO: Fix security holes here
1153/* There is currently a security hole in this function; the first occurrence of a '.' not followed by a second '.' will prevent the function from
1154 noticing an actual occurrence of '..' later in the string; iow, it only looks after the first period it finds for a second period.
1155 A second hole is that the last slash will be trimmed from the path, but one could still use ".//" and it would get past this function, and
1156 possibly be interpreted by the Boost file functions as "the current directory". Iow, both of these checks need to be iterative, not one-time.
1157 Not too concerned about this at the moment, as only we of the AE Team are supplying the install_info file that this function is connected to. -I */
1158
1159/* This function serves as a barrier against the Installer deleting files it shouldn't. *\
1160| It tests for each of the following conditions in the path it is passed: |
1161| A. '..' as the whole path or '/..', '\..' anywhere in the path |
1162| Reason: Moving up from the parent directory, the Edition folder, would allow one |
1163| to delete anything on the hard drive, so all "parent path" references are illegal. |
1164| B. '/' at the beginning of the path |
1165| Reason: In Unix, this means the path starts from root level, as opposed to the |
1166| directory we will evaluate these paths as being relative to, which is Edition/. |
1167| C. '.' as the whole path |
1168| Reason: This would mean "the Edition folder", which is not allowed to be deleted. |
1169| D. 'GameDataFolder' at the end of the path |
1170| Reason: We don't allow the entire GDF to be deleted, only specific files in it. |
1171| E. '*' anywhere in the path |
1172| Reason: We don't want this interpreted as a wildcard; it's best to only delete |
1173*\ files by name. */
1174bool StringIsLegalPathForDeletion(string word)
1175{
1176 string::size_type loc1, loc2;
1177
1178 // Trim ending slashes in order to simplify the test
1179 // Note that we're only altering the local copy of the string here
1180 loc1 = word.find_last_of("\\", word.size());
1181 if (loc1 == word.size() - 1)
1182 word.resize(word.size() - 1);
1183 loc1 = word.find_last_of("/", word.size());
1184 if (loc1 == word.size() - 1)
1185 word.resize(word.size() - 1);
1186
1187 // Test B
1188 loc1 = word.find_first_of("\\", 0);
1189 if (loc1 == 0)
1190 return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1191 loc1 = word.find_first_of("/", 0);
1192 if (loc1 == 0)
1193 return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1194
1195 // Test E
1196 loc1 = word.find("*", 0);
1197 if (loc1 != string::npos) // if we found our character before reaching the end of the string
1198 return false; // path cannot contain the '*' character
1199
1200 // Tests A (part 1) and C
1201 loc1 = word.find(".", 0);
1202 if (loc1 != string::npos)
1203 {
1204 if (word.size() == 1)
1205 return false; // path cannot be simply '.', referring to Edition folder itself
1206 loc2 = word.find(".", loc1 + 1);
1207 if (loc2 == loc1 + 1) // make sure this second period comes after the first one
1208 if (word.size() == 2)
1209 return false; // not allowed to reference a parent directory
1210 }
1211
1212 // Test A (part 2)
1213 loc1 = word.find("/..", 0);
1214 if (loc1 != string::npos)
1215 return false; // not allowed to reference a parent directory
1216 loc1 = word.find("\\..", 0);
1217 if (loc1 != string::npos)
1218 return false; // not allowed to reference a parent directory
1219
1220 // Test D
1221 loc1 = word.find("GameDataFolder", 0);
1222 if (loc1 == word.size() - 14) // if "GameDataFolder" is the last 14 characters of the string...
1223 return false; // not allowed to delete the GDF
1224
1225 return true;
1226}
1227
1228bool ProcessInstallerUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE)
1229{
1230 ofstream file;
1231 string shellScript;
1232
1233 ptime startTime(second_clock::local_time());
1234 string strStartTime = to_simple_string(startTime);
1235 string progressMsg = "Installer being updated to:\n" +
1236 updateAE->InstallerVersion + " on " + strStartTime;
1237 file.open("Update.log");
1238 if (!file.fail())
1239 file << progressMsg.c_str();
1240 file.close();
1241 file.clear();
1242
1243 string popenCommand = "../updates/" + strEUFN + "/install/";
1244#ifdef WIN32
[496]1245 popenCommand = "replace_installer.bat";
[487]1246#else
1247 // We can't just use '~' to mean "the home directory" because we need to check the path in C...
1248 // ...so we actually get the current user's shortname and manually construct the path to home
1249 FILE *fUserName = NULL;
1250 char chrUserName[32];
1251 fUserName = popen("whoami", "r");
1252 fgets(chrUserName, sizeof(chrUserName), fUserName);
1253 pclose(fUserName);
1254 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1255 int endOfName = strUserName.find("\n", 0);
1256 string pathToTrash = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1257 tm tmStartTime = to_tm(startTime);
1258 pathToTrash = pathToTrash + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1259 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec); // lol
1260 create_directory(pathToTrash);
1261 // The script takes as a parameter the path the old Installer should go to, in quotes
1262 popenCommand = "bash " + popenCommand + "replace_installer.sh " + pathToTrash + "/Installer.app";
1263
1264#endif
1265 file.close();
1266 file.clear();
[496]1267#ifdef WIN32
1268 system(popenCommand.c_str());
1269#else
[487]1270 popen(popenCommand.c_str(), "r");
[496]1271#endif
[487]1272 return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running
1273}
1274
1275bool ProcessAEUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
1276{
[543]1277 try {
[487]1278 fstream file;
1279 string line;
1280 vector<string> tokens, updateStarted;
1281 string strInstaller = "Installer";
1282 string strWas = "was";
1283 string strPathToEUFN = ("../updates/" + strEUFN + "/"); // strEUFN is set by GetUpdateStatus()
1284 string strPathToEUFNInstall = ("../updates/" + strEUFN + "/install/");
1285 string strPathToEUFNPackages = ("../updates/" + strEUFN + "/install/packages/");
1286 string strPathToPackages = "packages/";
1287 string strGlobalize = "Globalize/";
1288 string strOniSplit = "OniSplit.exe";
1289 string strDaodan = "binkw32.dll";
1290 string strWinGUI = "onisplit_gui.exe";
1291 string strWinGUILang = "ospgui_lang.ini";
1292 string strMacGUI = "AETools.app";
1293#ifdef WIN32
1294 string strOniApp = "Oni.exe";
1295#else
1296 string strOniApp = "Oni.app";
[543]1297 bool needNewTrashDir = false;
[487]1298#endif
[543]1299
[487]1300 bool readingVerAndDate = false;
1301
1302#ifdef WIN32
[543]1303 //string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
[487]1304#else
1305 FILE *fUserName = NULL;
1306 char chrUserName[32];
1307 fUserName = popen("whoami", "r");
1308 fgets(chrUserName, sizeof(chrUserName), fUserName);
1309 pclose(fUserName);
1310 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1311 int endOfName = strUserName.find("\n", 0);
1312 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1313#endif
1314
1315 // Write to log that we are beginning the update process
1316 ptime startTime(second_clock::local_time());
1317 string strStartTime = to_simple_string(startTime);
1318 string progressMsg = "\nEdition being updated to:\n" +
1319 updateAE->AEVersion + " on " + strStartTime;
1320 file.open("Update.log");
1321 if (!file.fail())
1322 file << progressMsg.c_str();
1323
1324 if (*installerJustUpdated) // then we want to know what folder in the Trash the Installer was placed in...
1325 {
1326 while (!file.eof()) // ...so we read the log to get the timestamp so we know the name of the folder that should be in the Trash
1327 {
1328 getline(file, line);
1329 tokenize(line, tokens);
1330
1331 if (tokens.capacity() >= 4)
1332 if (!strInstaller.compare(tokens[0]))
1333 if (!strWas.compare(tokens[1]))
1334 readingVerAndDate = true;
1335 if (readingVerAndDate && tokens.capacity() >= 3)
1336 tokenize(tokens[2], updateStarted, "-");
1337 }
[543]1338#ifndef WIN32
[487]1339 if (updateStarted.capacity() < 3)
1340 needNewTrashDir = true;
1341 else
1342 {
1343 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "-" +
1344 updateStarted[0] + "-" + updateStarted[1] + "-" + updateStarted[2] + "/";
1345 if (!exists(strTrashDir))
1346 needNewTrashDir = true;
1347 }
[543]1348#endif
[487]1349 }
[496]1350#ifndef WIN32
[487]1351 if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to
1352 {
1353 tm tmStartTime = to_tm(startTime);
1354 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1355 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1356 create_directory(strTrashDir);
1357 }
[496]1358#endif
[487]1359 file.close();
1360 file.clear();
1361
1362 // Special code to replace our special files -- the Oni app, OniSplit, the Daodan DLL, and the GUI for OniSplit
1363 if (exists(strPathToEUFN + strOniApp))
1364 {
1365 if (exists(strOniApp))
[543]1366#ifdef WIN32
1367 remove((path)strOniApp);
1368#else
[487]1369 rename((path)strOniApp, (path)(strTrashDir + strOniApp));
[543]1370#endif
[487]1371 rename((path)(strPathToEUFN + strOniApp), (path)strOniApp);
1372 }
1373 if (updateAE->OniSplitVersion.compare(currentAE->OniSplitVersion) >= 1)
1374 {
1375 if (exists(strPathToEUFNInstall + strOniSplit))
1376 {
1377 if (exists(strOniSplit))
[543]1378#ifdef WIN32
1379 remove((path)strOniSplit);
1380#else
[487]1381 rename((path)strOniSplit, (path)(strTrashDir + strOniSplit));
[543]1382#endif
[487]1383 rename((path)(strPathToEUFNInstall + strOniSplit), (path)strOniSplit);
1384 }
1385 }
1386#ifdef WIN32
1387 if (updateAE->DaodanVersion.compare(currentAE->DaodanVersion) >= 1)
1388 {
1389 if (exists(strPathToEUFN + strDaodan))
1390 {
1391 if (exists(("../" + strDaodan)))
[543]1392 remove((path)("../" + strDaodan));
[487]1393 rename((path)(strPathToEUFN + strDaodan), (path)("../" + strDaodan));
1394 }
1395 }
1396 if (updateAE->WinGUIVersion.compare(currentAE->WinGUIVersion) >= 1)
1397 {
1398 if (exists(strPathToEUFNInstall + strWinGUI))
1399 {
1400 if (exists((path)strWinGUI))
[543]1401 remove((path)strWinGUI);
[487]1402 if (exists(strWinGUILang))
[543]1403 remove((path)strWinGUILang);
[487]1404 rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI);
1405 rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang);
1406 }
1407 }
1408#else
1409 if (updateAE->MacGUIVersion.compare(currentAE->MacGUIVersion) >= 1)
1410 {
1411 if (exists(strPathToEUFN + strMacGUI))
1412 {
1413 if (exists(("../" + strMacGUI)))
1414 rename((path)("../" + strMacGUI), (path)(strTrashDir + strMacGUI));
1415 rename((path)(strPathToEUFN + strMacGUI), (path)("../" + strMacGUI));
1416 }
1417 }
1418#endif
1419
1420 // Now we trash whatever's in DeleteList; this allows us to clear out obsolete files in the previous AE install
1421 // Before moving a file to the Trash, we need to make sure each of the file's parent paths exists in the Trash...
1422 // ...so we iterate through the hierarchy of the file path, checking for each one and creating it if necessary
1423 for (vector<string>::iterator iter = updateAE->deleteList.begin(); iter != updateAE->deleteList.end(); iter++)
1424 {
1425 string thePath = *iter;
1426 if (exists((path)("../" + thePath)))
1427 {
1428 string aParentPath;
1429 string::size_type curPos = thePath.find("/", 0);
1430 if (curPos != string::npos)
1431 aParentPath = thePath.substr(0, curPos);
1432 string::size_type lastPos = curPos;
1433 while (curPos != string::npos && curPos < thePath.size())
1434 {
1435 aParentPath = aParentPath + thePath.substr(lastPos, curPos - lastPos);
[543]1436#ifndef WIN32
[487]1437 if (!exists(strTrashDir + aParentPath))
1438 create_directory(strTrashDir + aParentPath);
[543]1439#endif
[487]1440 lastPos = curPos + 1;
1441 curPos = thePath.find("/", lastPos);
1442 aParentPath = aParentPath + "/";
1443 }
[496]1444#ifndef WIN32
[487]1445 rename((path)("../" + thePath), (path)(strTrashDir + thePath));
[496]1446#else
1447 remove((path)("../" + thePath));
1448#endif
[487]1449 }
1450 }
1451
[506]1452 ProcessPackageUpdates(strPathToEUFNPackages, strPathToPackages);
[487]1453
1454 // Next, we get a list of which files and folders in the update's Globalize folder to move over; all files not starting with '.' will be moved...
1455 // ...and folders which do not exist in the current AE will be created there
1456 vector<string> foldersToMake, filesToMove;
1457 string thePath;
1458 for (recursive_directory_iterator dir_itr(strPathToEUFNPackages + strGlobalize), end_itr; dir_itr != end_itr; ++dir_itr)
1459 {
1460 thePath = dir_itr->path().string();
1461 MakePathLocalToGlobalize(&thePath);
1462 if (is_regular_file(dir_itr->status()))
1463 {
1464 if (dir_itr->filename().at(0) != '.') // skip over dot-files, which are invisible in Unix
1465 filesToMove.push_back(thePath);
1466 }
1467 else if (is_directory(dir_itr->status()))
1468 {
1469 if (!exists(strPathToPackages + strGlobalize + thePath))
1470 foldersToMake.push_back(thePath);
1471 }
1472 }
1473 // Sort the foldersToMake strings by length, which is a fast solution to the problem of "How do we make sure we create folder 'parent/'...
1474 // ...before folder 'parent/child/'"?
1475 sort(foldersToMake.begin(), foldersToMake.end(), SortBySize); // SortBySize is a custom comparison function found later in this source file
1476 // First make the folders that don't exist in the current AE, so all the files have a place to go
1477 for (vector<string>::iterator iter = foldersToMake.begin(); iter != foldersToMake.end(); iter++)
1478 {
1479 create_directory(strPathToPackages + strGlobalize + *iter);
1480 }
1481 for (vector<string>::iterator iter = filesToMove.begin(); iter != filesToMove.end(); iter++)
1482 {
1483 if (exists(strPathToPackages + strGlobalize + *iter))
[543]1484#ifdef WIN32
1485 remove((path)(strPathToPackages + strGlobalize + *iter));
1486#else
[487]1487 rename((path)(strPathToPackages + strGlobalize + *iter), (path)(strTrashDir + *iter));
[543]1488#endif
[487]1489 rename((path)(strPathToEUFNPackages + strGlobalize + *iter), (path)(strPathToPackages + strGlobalize + *iter));
1490 }
1491
1492 // Clean up after ourselves, trashing any packages or programs in the update package that are not newer than the current AE
[543]1493#ifdef WIN32
1494 remove((path)strPathToEUFN);
1495#else
[487]1496 create_directory(strTrashDir + "Unneeded update files");
1497 rename((path)strPathToEUFN, (path)(strTrashDir + "Unneeded update files/" + strEUFN));
[543]1498#endif
[487]1499 // Write to log that we are finished with update
1500 ptime end_time(second_clock::local_time());
1501 string progressMsg2 = "Edition was updated to:\n" +
1502 updateAE->AEVersion + " on " + to_simple_string(end_time);
1503
1504 file.open("Update.log");
1505
1506 if(!file.fail())
1507 file.write(progressMsg2.c_str(), sizeof(progressMsg2.c_str()));
1508
1509 file.close();
1510 file.clear();
1511
1512 if (updateAE->globalizationRequired)
1513 CheckForGlobalization(true); // the 'true' value forces re-globalization
1514
1515 globalPackages = getPackages(); // refresh the list in memory
[510]1516 wxCommandEvent e;
1517 TheWindow->OnRefreshButtonClick( e );
[487]1518 return true;
[543]1519 }
1520 catch (exception & ex)
1521 {
1522 setStatusArea("Warning, handled exception: " + (string)ex.what());
1523 return false;
1524 }
1525
[487]1526}
[511]1527
[506]1528void ProcessPackageUpdates(string pathToUpdate, string strPathToPackages)
1529{
1530 ptime startTime(second_clock::local_time());
1531#ifdef WIN32
1532 string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
1533#else
1534 FILE *fUserName = NULL;
1535 char chrUserName[32];
1536 fUserName = popen("whoami", "r");
1537 fgets(chrUserName, sizeof(chrUserName), fUserName);
1538 pclose(fUserName);
1539 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1540 int endOfName = strUserName.find("\n", 0);
1541 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1542 bool needNewTrashDir = true;
1543 tm tmStartTime = to_tm(startTime);
1544#endif
1545
1546 try
1547 {
1548 directory_iterator end;
1549 for (directory_iterator update_iter(pathToUpdate); update_iter != end; ++update_iter)
1550 {
1551 ModPackage installedPackage, updatePackage;
1552 string updtPath = update_iter->path().string();
1553 string updtFolder = update_iter->path().filename();
1554 string updtModInfo = updtPath + "/Mod_Info.cfg";
1555 string instModInfo = strPathToPackages + "/" + updtFolder + "/Mod_Info.cfg";
1556 if (!boost::iequals(updtFolder, "Edition")
1557 && !boost::iequals(updtFolder, "Edition-patch")
1558 && is_directory(update_iter->path())
1559 && exists(updtModInfo))
1560 {
1561 fstream file;
1562 file.open((updtModInfo).c_str());
1563 if (!file.fail())
1564 updatePackage = fileToModPackage(file, updtFolder);
1565 else
1566 {
1567 file.close();
1568 continue;
1569 }
1570 if (exists(instModInfo))
1571 {
1572 file.close();
1573 file.clear();
1574 file.open(instModInfo.c_str());
1575 if (!file.fail())
1576 {
1577 installedPackage = fileToModPackage(file, updtFolder);
[509]1578 }
1579 file.close();
1580 }
1581 file.close();
1582 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
1583 {
1584 if (updatePackage.installerVersion <= INSTALLER_VERSION)
1585 {
1586 if(exists(strPathToPackages + "/" + updatePackage.modStringName)) {
[506]1587#ifdef WIN32
[509]1588 remove_all((path)(strPathToPackages + "/" + updatePackage.modStringName));
[506]1589#else
[509]1590 if (needNewTrashDir)
1591 {
1592 strTrashDir = strTrashDir + "Old_packages_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1593 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1594 create_directory(strTrashDir);
1595 needNewTrashDir = false;
1596 }
1597 rename((path)(strPathToPackages + "/" + updatePackage.modStringName), (path)(strTrashDir + updatePackage.modStringName));
[506]1598#endif
1599 }
[509]1600 rename((path)(pathToUpdate + "/" + updatePackage.modStringName), (path)(strPathToPackages + "/" + updatePackage.modStringName));
[506]1601 }
1602 }
1603 }
1604 }
1605 }
1606 catch (exception & ex)
1607 {
1608 setStatusArea("Warning, handled exception: " + (string)ex.what());
1609 }
[509]1610 wxCommandEvent e;
1611 TheWindow->OnRefreshButtonClick( e );
[506]1612}
1613
[487]1614/* MakePathLocalToGlobalize is a function used once by ProcessAEUpdate() that takes a file in an \
1615| update's Globalize folder and changes its path, originally relative to the Installer, to be |
1616| relative to the structure of the Globalize folder; this makes it easier to have the Installer |
1617\ move said file from an update's Globalize folder to the current Globalize folder. */
1618void MakePathLocalToGlobalize(string *installerBasedPath)
1619{
1620 int deleteToHere = 0;
1621 deleteToHere = installerBasedPath->find("Globalize/");
1622 if (deleteToHere != 0)
1623 {
1624 deleteToHere += strlen("Globalize/");
1625 installerBasedPath->erase(0, deleteToHere);
1626 }
1627}
1628
1629/* SortBySize is a custom comparison function that we call when using the C++ sort() function in \
1630\ ProcessAEUpdate() on some strings that we want to sort by length. */
1631bool SortBySize(string a, string b)
1632{
1633 return (a.size() < b.size());
1634}
1635
[325]1636//stolen token function...
1637void tokenize(const string& str, vector<string>& tokens, const string& delimiters)
1638{
1639 // Skip delimiters at beginning.
1640 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1641 // Find first "non-delimiter".
1642 string::size_type pos = str.find_first_of(delimiters, lastPos);
1643
1644 while (string::npos != pos || string::npos != lastPos)
1645 {
1646 // Found a token, add it to the vector.
1647 tokens.push_back(str.substr(lastPos, pos - lastPos));
1648 // Skip delimiters. Note the "not_of"
1649 lastPos = str.find_first_not_of(delimiters, pos);
1650 // Find next "non-delimiter"
1651 pos = str.find_first_of(delimiters, lastPos);
1652 }
1653}
1654
[499]1655/* StripNewlines() gets rids of any linebreaks that come from text returned by getline(); \
1656| getline() should be stripping those out, but Windows CR/LF files seem to be sneaking |
1657\ some extra return characters into strings in the ReadInstallInfoCfg() function. */
1658void StripNewlines(string *theLine)
1659{
1660 int deleteFromHere = 0;
1661 deleteFromHere = theLine->find("\r");
1662 if (deleteFromHere > 0)
1663 theLine->erase(deleteFromHere, theLine->size());
1664}
1665
[325]1666void clearOldDats(void) {
1667 directory_iterator end_iter_gdf;
1668 for ( directory_iterator dir_itr_gdf( "../GameDataFolder" );
1669 dir_itr_gdf != end_iter_gdf;
1670 ++dir_itr_gdf )
1671 {
1672 if ( dir_itr_gdf->path().extension() == ".dat" || dir_itr_gdf->path().extension() == ".raw" || dir_itr_gdf->path().extension() == ".sep" ) {
1673 remove( dir_itr_gdf->path() );
1674 }
1675
1676 }
1677
[487]1678}
1679
1680// this function copies files and directories. If copying a
1681// directory to a directory, it copies recursively.
1682
1683//pardon the mess, I did this at midnight, and had to fix a bug
1684void copy( const path & from_ph,
1685 const path & to_ph )
1686{
1687 cout << to_ph.string() << "\n";
1688 // Make sure that the destination, if it exists, is a directory
1689 if((exists(to_ph) && !is_directory(to_ph)) || (!exists(from_ph))) cout << "error";
1690 if(!is_directory(from_ph))
1691 {
1692
1693 if(exists(to_ph))
1694 {
1695 copy_file(from_ph,to_ph/from_ph.filename());
1696 }
1697 else
1698 {
1699 try{
1700
1701 copy_file(from_ph,to_ph);
1702 }
1703 catch (exception ex){
1704 cout << from_ph.string() << " to " << to_ph.string() << "\n";
1705 }
1706 }
1707
1708 }
1709 else if(from_ph.filename() != ".svn")
1710 {
1711 path destination;
1712 if(!exists(to_ph))
1713 {
1714 destination=to_ph;
1715 }
1716 else
1717 {
1718 destination=to_ph/from_ph.filename();
1719 }
1720
1721 for(directory_iterator i(from_ph); i!=directory_iterator(); ++i)
1722 {
1723 //the idiot who coded this in the first place (not me)
1724 //forgot to make a new directory. Exception city. x_x
1725 create_directory(destination);
1726 copy(*i,destination/i->filename());
1727 }
1728 }
1729}
1730
1731void copy_directory( const path &from_dir_ph,
1732 const path &to_dir_ph)
1733{
1734 if(!exists(from_dir_ph) || !is_directory(from_dir_ph)
1735 || exists(to_dir_ph))
1736 cout << !exists(from_dir_ph) << " " << !is_directory(from_dir_ph)
1737 << " " << exists(to_dir_ph);
1738
1739# ifdef BOOST_POSIX
1740 struct stat from_stat;
1741 if ( (::stat( from_dir_ph.string().c_str(), &from_stat ) != 0)
1742 || ::mkdir(to_dir_ph.native_directory_string().c_str(),
1743 from_stat.st_mode)!=0)
1744# endif
1745 }
1746
1747string escapePath(string input)
1748{
1749 string output;
1750 string escape_me = "& ;()|<>\"'\\#*?$";
1751 for (unsigned int i = 0; i < input.size(); i++)
1752 {
1753 for (unsigned int j = 0; j < escape_me.size(); j++)
1754 if (input[i] == escape_me[j])
1755 output += '\\';
1756 output += input[i];
1757 }
1758 return output;
1759}
1760
1761Install_info_cfg::Install_info_cfg()
1762{
1763 AEVersion = "2009-07b";
1764 InstallerVersion = "1.0.1";
1765 DaodanVersion = "1.0";
1766 OniSplitVersion = "0.9.38.0";
1767 WinGUIVersion = "0";
1768 MacGUIVersion = "0";
1769 patch = false;
1770 globalizationRequired = false;
1771 deleteList.reserve(255);
1772}
1773
1774ModPackage::ModPackage()
1775{
1776 isInstalled = true; // replace with function
1777 name = "";
1778 modStringName = "";
1779 modStringVersion = 0;
[502]1780 platform = "Both";
[487]1781 hasOnis = false;
1782 hasDeltas = false;
1783 hasBSL = false;
1784 hasAddon = false;
1785 hasDats = false;
1786 category = "";
1787 creator = "";
1788 isEngine = false;
1789 readme = "";
1790 globalNeeded = true;
1791}
[489]1792#ifndef WIN32
[487]1793void Sleep(int ms)
1794{
1795 sleep(ms / 1000);
1796}
[489]1797#endif
[487]1798#ifdef WIN32
1799
1800void RedirectIOToConsole()
1801{
1802 int hConHandle;
1803 long lStdHandle;
1804 CONSOLE_SCREEN_BUFFER_INFO coninfo;
1805 FILE *fp;
1806
1807 // allocate a console for this app
1808 AllocConsole();
1809
1810 // set the screen buffer to be big enough to let us scroll text
1811 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
1812 &coninfo);
1813 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
1814 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
1815 coninfo.dwSize);
1816
1817 // redirect unbuffered STDOUT to the console
1818 lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
1819 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1820 fp = _fdopen( hConHandle, "w" );
1821 *stdout = *fp;
1822 setvbuf( stdout, NULL, _IONBF, 0 );
1823
1824 // redirect unbuffered STDIN to the console
1825 lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
1826 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1827 fp = _fdopen( hConHandle, "r" );
1828 *stdin = *fp;
1829 setvbuf( stdin, NULL, _IONBF, 0 );
1830
1831 // redirect unbuffered STDERR to the console
1832 lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
1833 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1834 fp = _fdopen( hConHandle, "w" );
1835 *stderr = *fp;
1836 setvbuf( stderr, NULL, _IONBF, 0 );
1837
1838 // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
1839 // point to console as well
1840 ios::sync_with_stdio();
1841}
1842#endif
Note: See TracBrowser for help on using the repository browser.