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

Last change on this file since 525 was 516, checked in by iritscen, 15 years ago

Removed TODO lines for things we've done; small fix to package update msg text.

File size: 65.1 KB
Line 
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\***************************************************************************/
8
9// TODO: Load credits from text resource file
10// TODO: Clear mod info fields when mod is de-selected
11
12//#define DEBUG
13#ifdef WIN32
14//#include <windows.h>
15#define popen _popen
16#endif
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"
20#include <boost/algorithm/string.hpp>
21#include "installer.h"
22#include "aeinstallerapp.h"
23
24using namespace boost::gregorian;
25using namespace boost::posix_time;
26
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/
30extern MainWindow* TheWindow;
31
32int globalizeData(void)
33{
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!");
42 int err = 0;
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]);
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";
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";
92 create_directory( "../GameDataFolder/" );
93
94 create_directory( "packages" );
95
96 if (exists("VanillaDats")) remove_all("VanillaDats");
97 create_directory( "VanillaDats" );
98 create_directory( "VanillaDats/level0_Final/" );
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 );
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;
117
118 for(int i = 0; i < 15; i++)
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 )
138 {
139 if ( is_regular_file( dir_itr->status() ) )
140 {
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 }
217
218 if (exists(dir_itr->path())) {
219
220 }
221 else {
222 //logfile << "\tMoved file: " << dir_itr->path().filename() << "\n";
223 }
224 }
225
226
227
228 }
229
230 logfile << "\tCleaning up TXMPs...\n";
231 system( (strOniSplit + " -move:delete " + Textures.string() + " ../GameDataFolder/level" + levels[i] + "_Final/TXMP*.oni").c_str());
232
233
234 if ( strcmp(levels[i].c_str(), "0") ){
235 system((strOniSplit + " -move:overwrite ../GameDataFolder/level" + levels[i] + "_Final ../GameDataFolder/level" + levels[i] +
236 "_Final/AKEV/AKEV*.oni").c_str());
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
244 }
245 }
246 logfile << "Reimporting levels\n";
247 for (int i = 0; i < 15; i++)
248 {
249 logfile << "\tReimporting level" << levels[i] << "_Final.oni\n";
250 setStatusArea("Step " + lexical_cast<std::string>(parts_done + 1) + "/" + lexical_cast<std::string>(total_steps) + " reimporting level" +
251 levels[i] + "_Final.oni");
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++;
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
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 );
276 //concactates level0....
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 )
281 {
282 try
283 {
284 rename(dir_itr->path(), "../GameDataFolder/level0_Final/" + dir_itr->path().filename() );
285 }
286 catch(exception &ex) {
287
288 }
289 }
290 }
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);
300
301 create_directory((path)"../GameDataFolder/IGMD");
302 copy((path)"packages/VanillaBSL/IGMD", (path)"../GameDataFolder");
303 setProgressBar( 1000 );
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
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 */
312 string fullAEpath = escapePath(system_complete(".").parent_path().parent_path().string()); // get full path for Edition/ (Oni wants folder that *contains* the GDF)
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 '";
315 strcat(prefsCommand, fullAEpath.c_str());
316 strcat(prefsCommand, "'"); // path string is enclosed in single quotes to avoid the need to escape UNIX-unfriendly characters
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
322#endif
323
324 setStatusArea((string)"Done! Now select your mod packages and click install.");
325 }
326 catch (exception & ex) {
327 setStatusArea("Warning, handled exception: " + (string)ex.what());
328 }
329
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;
335 return err;
336}
337
338vector<ModPackage> getPackages(string packageDir)
339{
340 vector<ModPackage> packages;
341 ModPackage package;
342 packages.reserve(256);
343 fstream file;
344 string filename = "\0";
345 string MODINFO_CFG = "Mod_Info.cfg";
346
347 try
348 {
349 for (directory_iterator dir_itr(packageDir), end_itr; dir_itr != end_itr; ++dir_itr)
350 {
351 file.open((dir_itr->path().string() + "/" + MODINFO_CFG).c_str());
352
353 if (!file.fail())
354 {
355 package = fileToModPackage(file, dir_itr->path().filename());
356 if (package.installerVersion.compare(INSTALLER_VERSION) < 1) // if mod requires newer version of the Installer, we won't add it to the list
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 }
365 }
366 file.close();
367 file.clear();
368 }
369 sort(packages.begin(), packages.end());
370 }
371 catch (const std::exception & ex)
372 {
373 cout << "Warning, something odd happened!\n";
374 }
375
376 return packages;
377}
378
379ModPackage fileToModPackage(fstream &file, string modName)
380{
381 ModPackage package;
382 string line;
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;
399 while (!file.eof())
400 {
401 getline(file,line);
402 vector<string> tokens;
403 vector<string>::iterator iter;
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"
419 package.name += *iter + " ";
420 }
421 }
422 else if (!ModString.compare(*iter))
423 {
424 iter++; iter++;
425 //package.modStringName = *iter;
426 iter++;
427 package.modStringVersion = atof((*iter).c_str());
428 }
429 else if (!ModString.compare(*iter))
430 {
431 iter++; iter++;
432 package.modStringVersion = atof((*iter).c_str());
433 }
434 else if (!Platform.compare(*iter))
435 {
436 iter++; iter++;
437 package.platform = *iter;
438 }
439 else if (!HasOnis.compare(*iter))
440 {
441 iter++; iter++;
442 if (boost::iequals(*iter, "Yes")) package.hasOnis = 1;
443 }
444 else if (!HasBSL.compare(*iter))
445 {
446 iter++; iter++;
447 if (boost::iequals(*iter, "Yes")) package.hasBSL = true;
448 else if (boost::iequals(*iter, "Addon")) package.hasAddon = true;
449 }
450 else if (!HasDeltas.compare(*iter))
451 {
452 iter++; iter++;
453 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDeltas = 1;
454 }
455 else if (!HasDats.compare(*iter))
456 {
457 iter++; iter++;
458 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDats = 1;
459 }
460 else if (!IsEngine.compare(*iter))
461 {
462 iter++; iter++;
463 if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.isEngine = 1;
464 }
465 else if (!GlobalNeeded.compare(*iter))
466 {
467 iter++; iter++;
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
470 }
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"
476 package.category += *iter + " ";
477 }
478 }
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"
484 package.creator += *iter + " ";
485 }
486 }
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';
494 else package.readme += *iter + " ";
495 }
496 }
497 }
498 }
499 }
500
501 return package;
502}
503
504void recompileAll(vector<string> installedMods)
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/";
512 string importCommand = "";
513 int numberOfDats = 0;
514 int j = 1;
515 string datString;
516
517 setStatusArea("Importing levels...");
518
519 std::stringstream out;
520
521 ptime start_time(second_clock::local_time());
522 clearOldDats();
523
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 {
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 }
543 catch(exception & ex) {
544 remove("Install.log");
545 ofstream logfile("Install.log");
546
547 logfile << "Warning, exception " << ex.what() << "!";
548 setStatusArea("Warning, exception " + (string)ex.what() + "!");
549 logfile.close();
550 }
551 }
552 try {
553 out << numberOfDats;
554 datString = out.str();
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();
564 for (unsigned int i = 0; i < installedMods.size(); ++i) {
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 }
568 importCommand += " ../GameDataFolder/" + dir_itr->path().filename() + ".dat >> Install.log";
569
570
571 setProgressBar( (int)(1000 * (float)(j-1) / (float)numberOfDats) ); //100% * dat we're on / total dats
572 setStatusArea("Step " + lexical_cast<std::string>(j) + '/' + lexical_cast<std::string>(numberOfDats)+ ": Importing " +
573 dir_itr->path().filename() + " ");
574
575 system(importCommand.c_str());
576 j++;
577 }
578 }
579 catch ( const std::exception & ex )
580 {
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();
586 }
587 }
588 }
589 catch( const std::exception & ex ) {
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();
595 }
596 }
597 else if(splitInstances == false){
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 {
621 importCommand = strOniSplit + " " + strImportOption + " " + vanilla_dir.string() + dir_itr->path().filename() + " ";
622 for (unsigned int i = 0; i < installedMods.size(); ++i) {
623 if (exists("packages/" + installedMods[i] + "/oni/" + dir_itr->path().filename() ))
624 importCommand += " packages/" + installedMods[i] + "/oni/" + dir_itr->path().filename();
625 }
626 importCommand += " ../GameDataFolder/" + dir_itr->path().filename() + ".dat >> Install.log";
627
628 setProgressBar( (int)(1000 * (float)(j-1) / (float)numberOfDats) ); //100% * dat we're on / total dats
629 setStatusArea("Step " + lexical_cast<std::string>(j) + '/' + lexical_cast<std::string>(numberOfDats)+ ": Importing " +
630 dir_itr->path().filename() + " ");
631 system(importCommand.c_str());
632 j++;
633 }
634 }
635 catch ( const std::exception & ex )
636 {
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();
642 }
643 }
644 }
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";
678 writeInstalledMods(installedMods);
679 setProgressBar(1000);
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);
693 setProgressBar(0);
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{
707 ofstream BSLlog("BSL.log", ios::app );
708
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 }
738 BSLfolders.push_back( dir_itr->path().filename() ); //add back check for addon
739 BSLlog << "Pushing " << dir_itr->path().filename() << "\n" ;
740 }
741 }
742 }
743 }
744 catch ( const std::exception & ex )
745 {
746 setStatusArea("Warning, exception " + (string)ex.what() + "!");
747 while(1) Sleep(1000);
748 }
749 BSLlog.close();
750
751}
752
753
754void writeInstalledMods(vector<string> installedMods)
755{
756 if ( exists( strInstallCfg ) )
757 {
758 remove( strInstallCfg );
759 }
760
761 ofstream file(strInstallCfg.c_str());
762
763 vector<string>list = installedMods;
764 vector<string>::iterator begin_iter = list.begin();
765 vector<string>::iterator end_iter = list.end();
766
767 sort( list.begin(), list.end() );
768
769 for( ; begin_iter != end_iter; ++begin_iter) {
770 file << *begin_iter << " ";
771 }
772
773 file.close();
774 file.clear();
775}
776
777vector<string> getInstallString(string Cfg)
778{
779 vector<string> returnval;
780 string line;
781 fstream file;
782
783 if (exists( Cfg ))
784 {
785 file.open(Cfg.c_str());
786 getline(file, line);
787 tokenize(line, returnval);
788 file.close();
789 file.clear();
790 sort(returnval.begin(), returnval.end());
791 }
792 else cout << "fail";
793
794 return returnval;
795}
796
797/* GetUpdateStatus determines whether there is an update available. It is called once, *\
798| on launch, by AEInstallerApp::OnInit(), and not only passes back a #defined result |
799| code, but also oversees the setting of data in the global structures currentAE and |
800| updateAE, which tell the Installer all the version information it needs to know. |
801| ---Return Values--- |
802| UPDATE_LOG_READ_ERR -- A log file could not be opened |
803| UPDATE_INST_REPL_ERR -- The Installer self-updating process failed |
804| UPDATE_MNTH_REQD_ERR -- The update is a patch, and the monthly release it |
805| patches is not installed |
806| UPDATE_NO_UPD_AVAIL -- Either there isn't an update in place, or it's not |
807| newer than what's installed |
808| UPDATE_SIMP_AVAIL -- An update is available |
809| UPDATE_GLOB_AVAIL -- An update is available that requires re-globalization |
810| afterwards (because of some notable change in the AE) |
811| UPDATE_INST_AVAIL -- An update is available that first requires the |
812| Installer to be replaced (when the new Installer |
813| launches, this function will be called again but will |
814| return UPDATE_SIMP_AVAIL or UPDATE_GLOB_AVAIL) |
815| UPDATE_PKG_AVAIL -- A newer version of individual package(s) is available |
816\* UPDATE_CONT_UPD -- Currently unused */
817int GetUpdateStatus(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
818{
819 fstream currentAECfg, updateAECfg, updateLog;
820 string strInstaller = "Installer";
821 string strBeing = "being";
822 string strWas = "was"; // lol
823#ifdef WIN32
824 string strInstallerName = "AEInstaller.exe";
825#else
826 string strInstallerName = "Installer.app";
827#endif
828
829 // Try to get current AE's version info; if it doesn't exist, then the default version data for 2009-07 remains in place
830 if (exists("packages/Globalize/Install_Info.cfg"))
831 {
832 currentAECfg.open("packages/Globalize/Install_Info.cfg");
833 if (!currentAECfg.fail())
834 {
835 if (!ReadInstallInfoCfg(&currentAECfg, currentAE))
836 return UPDATE_LOG_READ_ERR;
837
838 currentAECfg.close();
839 currentAECfg.clear();
840 }
841 else
842 return UPDATE_LOG_READ_ERR;
843 }
844
845 // Is there an update folder, and is it a monthly release or a patch?
846 bool firstParty = 0;
847 if (exists("../updates/Edition"))
848 {
849 firstParty = 1;
850 }
851 else {
852 strEUFN = "Edition-patch";
853 if (exists("../updates/Edition-patch")) {
854 firstParty = 1;
855 }
856
857 }
858
859 if(firstParty) {
860 // Unlike the current AE's version info, we *need* to find the update's version info or we won't continue
861 string updateCfgPath = ("../updates/" + strEUFN + "/install/packages/Globalize/Install_Info.cfg");
862 updateAECfg.open(updateCfgPath.c_str());
863 if (!updateAECfg.fail())
864 {
865 if (!ReadInstallInfoCfg(&updateAECfg, updateAE))
866 return UPDATE_LOG_READ_ERR;
867
868 updateAECfg.close();
869 updateAECfg.clear();
870 }
871 else
872 return UPDATE_LOG_READ_ERR;
873
874 // Now we check for an Installer update in progress
875 if (exists("Update.log"))
876 {
877 updateLog.open("Update.log");
878 if (!updateLog.fail())
879 {
880 vector<string> lines;
881 string line;
882 int num_lines = 0;
883 bool readingInstallerVersion = false, doneReadingFile = false;
884
885 while (!updateLog.eof() && !doneReadingFile)
886 {
887 getline(updateLog, line);
888 lines.push_back(line);
889 num_lines++;
890 vector<string> tokens;
891 vector<string>::iterator iter;
892 tokenize(line, tokens);
893 iter = tokens.begin();
894 if (!readingInstallerVersion && tokens.capacity() >= 4)
895 {
896 if (!strInstaller.compare(*iter))
897 {
898 if (!strBeing.compare(*++iter))
899 readingInstallerVersion = true;
900 else if (!strWas.compare(*iter))
901 *installerJustUpdated = true; // our third indirect return value after currentAE and updateAE
902 }
903 }
904 else if (readingInstallerVersion && tokens.capacity() >= 3)
905 {
906 readingInstallerVersion = false;
907 string installerVersion = INSTALLER_VERSION;
908 if (installerVersion.compare(*iter)) // then the shell script-powered replacement failed
909 return UPDATE_INST_REPL_ERR;
910 else
911 {
912 updateLog.close();
913 updateLog.clear();
914 Sleep(1000);
915 remove("Update.log");
916 ofstream newUpdateLog("Update.log");
917 if (!newUpdateLog.fail())
918 {
919 // Write over old log with updated information
920 ptime startTime(second_clock::local_time());
921 string strStartTime = to_simple_string(startTime);
922 string newUpdateLine = installerVersion + " on " + strStartTime;
923 for (int a = 0; a < lines.capacity() - 2; a++) // if there were even lines in the log before this at all
924 {
925 newUpdateLog << lines[a].c_str();
926 newUpdateLog << "\n";
927 }
928 newUpdateLog << "Installer was updated to:\n";
929 newUpdateLog << newUpdateLine.c_str();
930 *installerJustUpdated = true; // this value is indirectly returned to AEInstallerApp::OnInit()
931 doneReadingFile = true;
932 newUpdateLog.close();
933 newUpdateLog.clear();
934 //return UPDATE_CONT_UPD; // as noted above, we are not using this return value; in fact, we want...
935 // ...the code to continue running down through the Edition version check
936 }
937 else
938 return UPDATE_LOG_READ_ERR;
939 }
940 }
941 }
942 updateLog.close();
943 updateLog.clear();
944 }
945 else
946 return UPDATE_LOG_READ_ERR;
947 }
948
949 if (updateAE->AEVersion.compare(currentAE->AEVersion) >= 1) // is the release update newer than what's installed?
950 {
951 if (!strEUFN.compare("Edition-patch")) // if update is a patch...
952 {
953 if (currentAE->AEVersion.compare(updateAE->AEVersion.substr(0, updateAE->AEVersion.length() - 1))) // ...is it for a different month?
954 return UPDATE_MNTH_REQD_ERR;
955 }
956 string strNewInstallerPath = "../updates/" + strEUFN + "/install/" + strInstallerName;
957 string installerVersion = INSTALLER_VERSION;
958 if (updateAE->InstallerVersion.compare(installerVersion) >= 1)
959 {
960 if (exists(strNewInstallerPath))
961 return UPDATE_INST_AVAIL;
962 }
963 else if (updateAE->globalizationRequired)
964 return UPDATE_GLOB_AVAIL;
965 else
966 return UPDATE_SIMP_AVAIL;
967 }
968 }
969 try
970 {
971 directory_iterator end;
972 if (exists("../updates"))
973 {
974 for (directory_iterator install_iter("../updates"); install_iter != end; ++install_iter)
975 {
976 ModPackage installedPackage, updatePackage;
977 if (is_directory(install_iter->path()) && exists(install_iter->path().string() + "/Mod_Info.cfg"))
978 {
979 fstream file;
980 file.open((install_iter->path().string() + "/Mod_Info.cfg").c_str());
981 if (!file.fail())
982 updatePackage = fileToModPackage(file, install_iter->path().filename());
983 else
984 {
985 file.close();
986 continue;
987 }
988 if (exists("packages/" + install_iter->path().filename() + "/Mod_Info.cfg"))
989 {
990 file.close();
991 file.clear();
992 file.open(("packages/" + install_iter->path().filename() + "/Mod_Info.cfg").c_str());
993 if (!file.fail())
994 installedPackage = fileToModPackage(file, install_iter->path().filename());
995 file.close();
996 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
997 {
998 if (updatePackage.installerVersion <= INSTALLER_VERSION)
999 return UPDATE_PKG_AVAIL;
1000 }
1001 }
1002 else
1003 {
1004 file.close();
1005 return UPDATE_PKG_AVAIL;
1006 }
1007 }
1008 }
1009 }
1010 }
1011 catch (exception & ex) {
1012 // setStatusArea("Warning, handled exception: " + (string)ex.what());
1013 }
1014
1015 return UPDATE_NO_UPD_AVAIL;
1016}
1017
1018bool ReadInstallInfoCfg(fstream *fileHandler, Install_info_cfg *info_cfg)
1019{
1020 vector<string> tokens;
1021 vector<string>::iterator iter;
1022 string line;
1023 string strAEVersion = "AE_Version";
1024 string strInstallerVersion = "Installer_Version";
1025 string strDaodanVersion = "Daodan_Version";
1026 string strOniSplitVersion = "OniSplit_Version";
1027 string strGUIWinVersion = "GUI_Win_Version";
1028 string strGUIMacVersion = "GUI_Mac_Version";
1029 string strReglobalize = "Reglobalize";
1030 string strDeleteList = "Delete_List";
1031 string strArrow = "->";
1032 string strDoubleSlash = "//";
1033 string strYes = "Yes"; // this is getting silly
1034
1035 while (getline(*fileHandler, line))
1036 {
1037 StripNewlines(&line);
1038 tokenize(line, tokens);
1039 iter = tokens.begin();
1040
1041 if (tokens.size() >= 3)
1042 {
1043 if (!strAEVersion.compare(*iter))
1044 {
1045 if (!strArrow.compare(*++iter))
1046 info_cfg->AEVersion = *++iter;
1047 else
1048 return false;
1049 }
1050 else if (!strInstallerVersion.compare(*iter))
1051 {
1052 if (!strArrow.compare(*++iter))
1053 info_cfg->InstallerVersion = *++iter;
1054 else
1055 return false;
1056 }
1057 else if (!strDaodanVersion.compare(*iter))
1058 {
1059 if (!strArrow.compare(*++iter))
1060 info_cfg->DaodanVersion = *++iter;
1061 else
1062 return false;
1063 }
1064 else if (!strOniSplitVersion.compare(*iter))
1065 {
1066 if (!strArrow.compare(*++iter))
1067 info_cfg->OniSplitVersion = *++iter;
1068 else
1069 return false;
1070 }
1071 else if (!strGUIWinVersion.compare(*iter))
1072 {
1073 if (!strArrow.compare(*++iter))
1074 info_cfg->WinGUIVersion = *++iter;
1075 else
1076 return false;
1077 }
1078 else if (!strGUIMacVersion.compare(*iter))
1079 {
1080 if (!strArrow.compare(*++iter))
1081 info_cfg->MacGUIVersion = *++iter;
1082 else
1083 return false;
1084 }
1085 else if (!strReglobalize.compare(*iter))
1086 {
1087 if (!strArrow.compare(*++iter))
1088 {
1089 if (!strYes.compare(*++iter))
1090 info_cfg->globalizationRequired = true;
1091 }
1092 else
1093 return false;
1094 }
1095 else if (!strDeleteList.compare(*iter))
1096 {
1097 // We need to perform a totally customized parsing process on this data
1098 if (!strArrow.compare(*++iter))
1099 {
1100 vector<string> tokens2;
1101 tokenize(line, tokens2, ","); // the paths on this line are comma-delimited, so we parse it again
1102 vector<string>::iterator iter2 = tokens2.begin();
1103 string finalPath = "";
1104 for (; iter2 != tokens2.end(); iter2++)
1105 {
1106 finalPath = finalPath + *iter2;
1107
1108 string::size_type loc = finalPath.find("->", 0); // the first word will have "Delete_List ->" at the front, so let's cut that off
1109 if (loc != string::npos)
1110 finalPath = finalPath.substr(loc + 3, finalPath.size());
1111
1112 // If a path has '//' in it, it must contain some optional comments that were at the end of the Delete_List line
1113 loc = finalPath.find("//", 0);
1114 if (loc != string::npos)
1115 finalPath = finalPath.substr(0, loc);
1116
1117 // Trim a single space if it exists at the start or finish; putting more than one space after a comma will break this
1118 if (finalPath.at(0) == ' ')
1119 finalPath = finalPath.substr(1, finalPath.size());
1120 if (finalPath.at(finalPath.size() - 1) == ' ')
1121 finalPath = finalPath.substr(0, finalPath.size() - 1);
1122
1123 // If the tokenized path ends with a '\', then we assume it was followed by a comma
1124 if (finalPath.at(finalPath.size() - 1) == '\\')
1125 {
1126 finalPath = finalPath.substr(0, finalPath.size() - 1); // clip the '\' off the end of the string now that it served its purpose...
1127 finalPath = finalPath + ","; // ...and add the actual comma back at the end
1128 }
1129 else // we can add the path to deleteList, and clear the path; otherwise it will be added to on the next iteration
1130 {
1131 if (StringIsLegalPathForDeletion(finalPath)) // ...and it's not violating any of our security rules as to what can be deleted...
1132 info_cfg->deleteList.push_back(finalPath); // ...then add it to our deleteList in memory
1133 finalPath.clear(); // clear the token we were building up before the next pass
1134 }
1135 }
1136 }
1137 else
1138 return false;
1139 }
1140 }
1141 tokens.clear();
1142 }
1143
1144 return true;
1145}
1146
1147// TODO: Fix security holes here
1148/* There is currently a security hole in this function; the first occurrence of a '.' not followed by a second '.' will prevent the function from
1149 noticing an actual occurrence of '..' later in the string; iow, it only looks after the first period it finds for a second period.
1150 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
1151 possibly be interpreted by the Boost file functions as "the current directory". Iow, both of these checks need to be iterative, not one-time.
1152 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 */
1153
1154/* This function serves as a barrier against the Installer deleting files it shouldn't. *\
1155| It tests for each of the following conditions in the path it is passed: |
1156| A. '..' as the whole path or '/..', '\..' anywhere in the path |
1157| Reason: Moving up from the parent directory, the Edition folder, would allow one |
1158| to delete anything on the hard drive, so all "parent path" references are illegal. |
1159| B. '/' at the beginning of the path |
1160| Reason: In Unix, this means the path starts from root level, as opposed to the |
1161| directory we will evaluate these paths as being relative to, which is Edition/. |
1162| C. '.' as the whole path |
1163| Reason: This would mean "the Edition folder", which is not allowed to be deleted. |
1164| D. 'GameDataFolder' at the end of the path |
1165| Reason: We don't allow the entire GDF to be deleted, only specific files in it. |
1166| E. '*' anywhere in the path |
1167| Reason: We don't want this interpreted as a wildcard; it's best to only delete |
1168*\ files by name. */
1169bool StringIsLegalPathForDeletion(string word)
1170{
1171 string::size_type loc1, loc2;
1172
1173 // Trim ending slashes in order to simplify the test
1174 // Note that we're only altering the local copy of the string here
1175 loc1 = word.find_last_of("\\", word.size());
1176 if (loc1 == word.size() - 1)
1177 word.resize(word.size() - 1);
1178 loc1 = word.find_last_of("/", word.size());
1179 if (loc1 == word.size() - 1)
1180 word.resize(word.size() - 1);
1181
1182 // Test B
1183 loc1 = word.find_first_of("\\", 0);
1184 if (loc1 == 0)
1185 return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1186 loc1 = word.find_first_of("/", 0);
1187 if (loc1 == 0)
1188 return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1189
1190 // Test E
1191 loc1 = word.find("*", 0);
1192 if (loc1 != string::npos) // if we found our character before reaching the end of the string
1193 return false; // path cannot contain the '*' character
1194
1195 // Tests A (part 1) and C
1196 loc1 = word.find(".", 0);
1197 if (loc1 != string::npos)
1198 {
1199 if (word.size() == 1)
1200 return false; // path cannot be simply '.', referring to Edition folder itself
1201 loc2 = word.find(".", loc1 + 1);
1202 if (loc2 == loc1 + 1) // make sure this second period comes after the first one
1203 if (word.size() == 2)
1204 return false; // not allowed to reference a parent directory
1205 }
1206
1207 // Test A (part 2)
1208 loc1 = word.find("/..", 0);
1209 if (loc1 != string::npos)
1210 return false; // not allowed to reference a parent directory
1211 loc1 = word.find("\\..", 0);
1212 if (loc1 != string::npos)
1213 return false; // not allowed to reference a parent directory
1214
1215 // Test D
1216 loc1 = word.find("GameDataFolder", 0);
1217 if (loc1 == word.size() - 14) // if "GameDataFolder" is the last 14 characters of the string...
1218 return false; // not allowed to delete the GDF
1219
1220 return true;
1221}
1222
1223bool ProcessInstallerUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE)
1224{
1225 ofstream file;
1226 string shellScript;
1227
1228 ptime startTime(second_clock::local_time());
1229 string strStartTime = to_simple_string(startTime);
1230 string progressMsg = "Installer being updated to:\n" +
1231 updateAE->InstallerVersion + " on " + strStartTime;
1232 file.open("Update.log");
1233 if (!file.fail())
1234 file << progressMsg.c_str();
1235 file.close();
1236 file.clear();
1237
1238 string popenCommand = "../updates/" + strEUFN + "/install/";
1239#ifdef WIN32
1240 popenCommand = "replace_installer.bat";
1241#else
1242 // We can't just use '~' to mean "the home directory" because we need to check the path in C...
1243 // ...so we actually get the current user's shortname and manually construct the path to home
1244 FILE *fUserName = NULL;
1245 char chrUserName[32];
1246 fUserName = popen("whoami", "r");
1247 fgets(chrUserName, sizeof(chrUserName), fUserName);
1248 pclose(fUserName);
1249 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1250 int endOfName = strUserName.find("\n", 0);
1251 string pathToTrash = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1252 tm tmStartTime = to_tm(startTime);
1253 pathToTrash = pathToTrash + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1254 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec); // lol
1255 create_directory(pathToTrash);
1256 // The script takes as a parameter the path the old Installer should go to, in quotes
1257 popenCommand = "bash " + popenCommand + "replace_installer.sh " + pathToTrash + "/Installer.app";
1258
1259#endif
1260 file.close();
1261 file.clear();
1262#ifdef WIN32
1263 system(popenCommand.c_str());
1264#else
1265 popen(popenCommand.c_str(), "r");
1266#endif
1267 return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running
1268}
1269
1270bool ProcessAEUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
1271{
1272 fstream file;
1273 string line;
1274 vector<string> tokens, updateStarted;
1275 string strInstaller = "Installer";
1276 string strWas = "was";
1277 string strPathToEUFN = ("../updates/" + strEUFN + "/"); // strEUFN is set by GetUpdateStatus()
1278 string strPathToEUFNInstall = ("../updates/" + strEUFN + "/install/");
1279 string strPathToEUFNPackages = ("../updates/" + strEUFN + "/install/packages/");
1280 string strPathToPackages = "packages/";
1281 string strGlobalize = "Globalize/";
1282 string strOniSplit = "OniSplit.exe";
1283 string strDaodan = "binkw32.dll";
1284 string strWinGUI = "onisplit_gui.exe";
1285 string strWinGUILang = "ospgui_lang.ini";
1286 string strMacGUI = "AETools.app";
1287#ifdef WIN32
1288 string strOniApp = "Oni.exe";
1289#else
1290 string strOniApp = "Oni.app";
1291#endif
1292 bool needNewTrashDir = false;
1293 bool readingVerAndDate = false;
1294
1295#ifdef WIN32
1296 string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
1297#else
1298 FILE *fUserName = NULL;
1299 char chrUserName[32];
1300 fUserName = popen("whoami", "r");
1301 fgets(chrUserName, sizeof(chrUserName), fUserName);
1302 pclose(fUserName);
1303 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1304 int endOfName = strUserName.find("\n", 0);
1305 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1306#endif
1307
1308 // Write to log that we are beginning the update process
1309 ptime startTime(second_clock::local_time());
1310 string strStartTime = to_simple_string(startTime);
1311 string progressMsg = "\nEdition being updated to:\n" +
1312 updateAE->AEVersion + " on " + strStartTime;
1313 file.open("Update.log");
1314 if (!file.fail())
1315 file << progressMsg.c_str();
1316
1317 if (*installerJustUpdated) // then we want to know what folder in the Trash the Installer was placed in...
1318 {
1319 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
1320 {
1321 getline(file, line);
1322 tokenize(line, tokens);
1323
1324 if (tokens.capacity() >= 4)
1325 if (!strInstaller.compare(tokens[0]))
1326 if (!strWas.compare(tokens[1]))
1327 readingVerAndDate = true;
1328 if (readingVerAndDate && tokens.capacity() >= 3)
1329 tokenize(tokens[2], updateStarted, "-");
1330 }
1331 if (updateStarted.capacity() < 3)
1332 needNewTrashDir = true;
1333 else
1334 {
1335 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "-" +
1336 updateStarted[0] + "-" + updateStarted[1] + "-" + updateStarted[2] + "/";
1337 if (!exists(strTrashDir))
1338 needNewTrashDir = true;
1339 }
1340 }
1341#ifndef WIN32
1342 if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to
1343 {
1344 tm tmStartTime = to_tm(startTime);
1345 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1346 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1347 create_directory(strTrashDir);
1348 }
1349#endif
1350 file.close();
1351 file.clear();
1352
1353 // Special code to replace our special files -- the Oni app, OniSplit, the Daodan DLL, and the GUI for OniSplit
1354 if (exists(strPathToEUFN + strOniApp))
1355 {
1356 if (exists(strOniApp))
1357 rename((path)strOniApp, (path)(strTrashDir + strOniApp));
1358 rename((path)(strPathToEUFN + strOniApp), (path)strOniApp);
1359 }
1360 if (updateAE->OniSplitVersion.compare(currentAE->OniSplitVersion) >= 1)
1361 {
1362 if (exists(strPathToEUFNInstall + strOniSplit))
1363 {
1364 if (exists(strOniSplit))
1365 rename((path)strOniSplit, (path)(strTrashDir + strOniSplit));
1366 rename((path)(strPathToEUFNInstall + strOniSplit), (path)strOniSplit);
1367 }
1368 }
1369#ifdef WIN32
1370 if (updateAE->DaodanVersion.compare(currentAE->DaodanVersion) >= 1)
1371 {
1372 if (exists(strPathToEUFN + strDaodan))
1373 {
1374 if (exists(("../" + strDaodan)))
1375 rename((path)("../" + strDaodan), (path)(strTrashDir + strDaodan));
1376 rename((path)(strPathToEUFN + strDaodan), (path)("../" + strDaodan));
1377 }
1378 }
1379 if (updateAE->WinGUIVersion.compare(currentAE->WinGUIVersion) >= 1)
1380 {
1381 if (exists(strPathToEUFNInstall + strWinGUI))
1382 {
1383 if (exists((path)strWinGUI))
1384 rename((path)strWinGUI, (path)(strTrashDir + strWinGUI));
1385 if (exists(strWinGUILang))
1386 rename((path)strWinGUILang, (path)(strTrashDir + strWinGUILang));
1387 rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI);
1388 rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang);
1389 }
1390 }
1391#else
1392 if (updateAE->MacGUIVersion.compare(currentAE->MacGUIVersion) >= 1)
1393 {
1394 if (exists(strPathToEUFN + strMacGUI))
1395 {
1396 if (exists(("../" + strMacGUI)))
1397 rename((path)("../" + strMacGUI), (path)(strTrashDir + strMacGUI));
1398 rename((path)(strPathToEUFN + strMacGUI), (path)("../" + strMacGUI));
1399 }
1400 }
1401#endif
1402
1403 // Now we trash whatever's in DeleteList; this allows us to clear out obsolete files in the previous AE install
1404 // Before moving a file to the Trash, we need to make sure each of the file's parent paths exists in the Trash...
1405 // ...so we iterate through the hierarchy of the file path, checking for each one and creating it if necessary
1406 for (vector<string>::iterator iter = updateAE->deleteList.begin(); iter != updateAE->deleteList.end(); iter++)
1407 {
1408 string thePath = *iter;
1409 if (exists((path)("../" + thePath)))
1410 {
1411 string aParentPath;
1412 string::size_type curPos = thePath.find("/", 0);
1413 if (curPos != string::npos)
1414 aParentPath = thePath.substr(0, curPos);
1415 string::size_type lastPos = curPos;
1416 while (curPos != string::npos && curPos < thePath.size())
1417 {
1418 aParentPath = aParentPath + thePath.substr(lastPos, curPos - lastPos);
1419 if (!exists(strTrashDir + aParentPath))
1420 create_directory(strTrashDir + aParentPath);
1421 lastPos = curPos + 1;
1422 curPos = thePath.find("/", lastPos);
1423 aParentPath = aParentPath + "/";
1424 }
1425#ifndef WIN32
1426 rename((path)("../" + thePath), (path)(strTrashDir + thePath));
1427#else
1428 remove((path)("../" + thePath));
1429#endif
1430 }
1431 }
1432
1433 ProcessPackageUpdates(strPathToEUFNPackages, strPathToPackages);
1434
1435 // 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...
1436 // ...and folders which do not exist in the current AE will be created there
1437 vector<string> foldersToMake, filesToMove;
1438 string thePath;
1439 for (recursive_directory_iterator dir_itr(strPathToEUFNPackages + strGlobalize), end_itr; dir_itr != end_itr; ++dir_itr)
1440 {
1441 thePath = dir_itr->path().string();
1442 MakePathLocalToGlobalize(&thePath);
1443 if (is_regular_file(dir_itr->status()))
1444 {
1445 if (dir_itr->filename().at(0) != '.') // skip over dot-files, which are invisible in Unix
1446 filesToMove.push_back(thePath);
1447 }
1448 else if (is_directory(dir_itr->status()))
1449 {
1450 if (!exists(strPathToPackages + strGlobalize + thePath))
1451 foldersToMake.push_back(thePath);
1452 }
1453 }
1454 // Sort the foldersToMake strings by length, which is a fast solution to the problem of "How do we make sure we create folder 'parent/'...
1455 // ...before folder 'parent/child/'"?
1456 sort(foldersToMake.begin(), foldersToMake.end(), SortBySize); // SortBySize is a custom comparison function found later in this source file
1457 // First make the folders that don't exist in the current AE, so all the files have a place to go
1458 for (vector<string>::iterator iter = foldersToMake.begin(); iter != foldersToMake.end(); iter++)
1459 {
1460 create_directory(strPathToPackages + strGlobalize + *iter);
1461 }
1462 for (vector<string>::iterator iter = filesToMove.begin(); iter != filesToMove.end(); iter++)
1463 {
1464 if (exists(strPathToPackages + strGlobalize + *iter))
1465 rename((path)(strPathToPackages + strGlobalize + *iter), (path)(strTrashDir + *iter));
1466 rename((path)(strPathToEUFNPackages + strGlobalize + *iter), (path)(strPathToPackages + strGlobalize + *iter));
1467 }
1468
1469 // Clean up after ourselves, trashing any packages or programs in the update package that are not newer than the current AE
1470 create_directory(strTrashDir + "Unneeded update files");
1471 rename((path)strPathToEUFN, (path)(strTrashDir + "Unneeded update files/" + strEUFN));
1472
1473 // Write to log that we are finished with update
1474 ptime end_time(second_clock::local_time());
1475 string progressMsg2 = "Edition was updated to:\n" +
1476 updateAE->AEVersion + " on " + to_simple_string(end_time);
1477
1478 file.open("Update.log");
1479
1480 if(!file.fail())
1481 file.write(progressMsg2.c_str(), sizeof(progressMsg2.c_str()));
1482
1483 file.close();
1484 file.clear();
1485
1486 if (updateAE->globalizationRequired)
1487 CheckForGlobalization(true); // the 'true' value forces re-globalization
1488
1489 globalPackages = getPackages(); // refresh the list in memory
1490 wxCommandEvent e;
1491 TheWindow->OnRefreshButtonClick( e );
1492 return true;
1493}
1494
1495void ProcessPackageUpdates(string pathToUpdate, string strPathToPackages)
1496{
1497 ptime startTime(second_clock::local_time());
1498#ifdef WIN32
1499 string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
1500#else
1501 FILE *fUserName = NULL;
1502 char chrUserName[32];
1503 fUserName = popen("whoami", "r");
1504 fgets(chrUserName, sizeof(chrUserName), fUserName);
1505 pclose(fUserName);
1506 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1507 int endOfName = strUserName.find("\n", 0);
1508 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1509 bool needNewTrashDir = true;
1510 tm tmStartTime = to_tm(startTime);
1511#endif
1512
1513 try
1514 {
1515 directory_iterator end;
1516 for (directory_iterator update_iter(pathToUpdate); update_iter != end; ++update_iter)
1517 {
1518 ModPackage installedPackage, updatePackage;
1519 string updtPath = update_iter->path().string();
1520 string updtFolder = update_iter->path().filename();
1521 string updtModInfo = updtPath + "/Mod_Info.cfg";
1522 string instModInfo = strPathToPackages + "/" + updtFolder + "/Mod_Info.cfg";
1523 if (!boost::iequals(updtFolder, "Edition")
1524 && !boost::iequals(updtFolder, "Edition-patch")
1525 && is_directory(update_iter->path())
1526 && exists(updtModInfo))
1527 {
1528 fstream file;
1529 file.open((updtModInfo).c_str());
1530 if (!file.fail())
1531 updatePackage = fileToModPackage(file, updtFolder);
1532 else
1533 {
1534 file.close();
1535 continue;
1536 }
1537 if (exists(instModInfo))
1538 {
1539 file.close();
1540 file.clear();
1541 file.open(instModInfo.c_str());
1542 if (!file.fail())
1543 {
1544 installedPackage = fileToModPackage(file, updtFolder);
1545 }
1546 file.close();
1547 }
1548 file.close();
1549 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
1550 {
1551 if (updatePackage.installerVersion <= INSTALLER_VERSION)
1552 {
1553 if(exists(strPathToPackages + "/" + updatePackage.modStringName)) {
1554#ifdef WIN32
1555 remove_all((path)(strPathToPackages + "/" + updatePackage.modStringName));
1556#else
1557 if (needNewTrashDir)
1558 {
1559 strTrashDir = strTrashDir + "Old_packages_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1560 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1561 create_directory(strTrashDir);
1562 needNewTrashDir = false;
1563 }
1564 rename((path)(strPathToPackages + "/" + updatePackage.modStringName), (path)(strTrashDir + updatePackage.modStringName));
1565#endif
1566 }
1567 rename((path)(pathToUpdate + "/" + updatePackage.modStringName), (path)(strPathToPackages + "/" + updatePackage.modStringName));
1568 }
1569 }
1570 }
1571 }
1572 }
1573 catch (exception & ex)
1574 {
1575 setStatusArea("Warning, handled exception: " + (string)ex.what());
1576 }
1577 wxCommandEvent e;
1578 TheWindow->OnRefreshButtonClick( e );
1579}
1580
1581/* MakePathLocalToGlobalize is a function used once by ProcessAEUpdate() that takes a file in an \
1582| update's Globalize folder and changes its path, originally relative to the Installer, to be |
1583| relative to the structure of the Globalize folder; this makes it easier to have the Installer |
1584\ move said file from an update's Globalize folder to the current Globalize folder. */
1585void MakePathLocalToGlobalize(string *installerBasedPath)
1586{
1587 int deleteToHere = 0;
1588 deleteToHere = installerBasedPath->find("Globalize/");
1589 if (deleteToHere != 0)
1590 {
1591 deleteToHere += strlen("Globalize/");
1592 installerBasedPath->erase(0, deleteToHere);
1593 }
1594}
1595
1596/* SortBySize is a custom comparison function that we call when using the C++ sort() function in \
1597\ ProcessAEUpdate() on some strings that we want to sort by length. */
1598bool SortBySize(string a, string b)
1599{
1600 return (a.size() < b.size());
1601}
1602
1603//stolen token function...
1604void tokenize(const string& str, vector<string>& tokens, const string& delimiters)
1605{
1606 // Skip delimiters at beginning.
1607 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1608 // Find first "non-delimiter".
1609 string::size_type pos = str.find_first_of(delimiters, lastPos);
1610
1611 while (string::npos != pos || string::npos != lastPos)
1612 {
1613 // Found a token, add it to the vector.
1614 tokens.push_back(str.substr(lastPos, pos - lastPos));
1615 // Skip delimiters. Note the "not_of"
1616 lastPos = str.find_first_not_of(delimiters, pos);
1617 // Find next "non-delimiter"
1618 pos = str.find_first_of(delimiters, lastPos);
1619 }
1620}
1621
1622/* StripNewlines() gets rids of any linebreaks that come from text returned by getline(); \
1623| getline() should be stripping those out, but Windows CR/LF files seem to be sneaking |
1624\ some extra return characters into strings in the ReadInstallInfoCfg() function. */
1625void StripNewlines(string *theLine)
1626{
1627 int deleteFromHere = 0;
1628 deleteFromHere = theLine->find("\r");
1629 if (deleteFromHere > 0)
1630 theLine->erase(deleteFromHere, theLine->size());
1631}
1632
1633void clearOldDats(void) {
1634 directory_iterator end_iter_gdf;
1635 for ( directory_iterator dir_itr_gdf( "../GameDataFolder" );
1636 dir_itr_gdf != end_iter_gdf;
1637 ++dir_itr_gdf )
1638 {
1639 if ( dir_itr_gdf->path().extension() == ".dat" || dir_itr_gdf->path().extension() == ".raw" || dir_itr_gdf->path().extension() == ".sep" ) {
1640 remove( dir_itr_gdf->path() );
1641 }
1642
1643 }
1644
1645}
1646
1647// this function copies files and directories. If copying a
1648// directory to a directory, it copies recursively.
1649
1650//pardon the mess, I did this at midnight, and had to fix a bug
1651void copy( const path & from_ph,
1652 const path & to_ph )
1653{
1654 cout << to_ph.string() << "\n";
1655 // Make sure that the destination, if it exists, is a directory
1656 if((exists(to_ph) && !is_directory(to_ph)) || (!exists(from_ph))) cout << "error";
1657 if(!is_directory(from_ph))
1658 {
1659
1660 if(exists(to_ph))
1661 {
1662 copy_file(from_ph,to_ph/from_ph.filename());
1663 }
1664 else
1665 {
1666 try{
1667
1668 copy_file(from_ph,to_ph);
1669 }
1670 catch (exception ex){
1671 cout << from_ph.string() << " to " << to_ph.string() << "\n";
1672 }
1673 }
1674
1675 }
1676 else if(from_ph.filename() != ".svn")
1677 {
1678 path destination;
1679 if(!exists(to_ph))
1680 {
1681 destination=to_ph;
1682 }
1683 else
1684 {
1685 destination=to_ph/from_ph.filename();
1686 }
1687
1688 for(directory_iterator i(from_ph); i!=directory_iterator(); ++i)
1689 {
1690 //the idiot who coded this in the first place (not me)
1691 //forgot to make a new directory. Exception city. x_x
1692 create_directory(destination);
1693 copy(*i,destination/i->filename());
1694 }
1695 }
1696}
1697
1698void copy_directory( const path &from_dir_ph,
1699 const path &to_dir_ph)
1700{
1701 if(!exists(from_dir_ph) || !is_directory(from_dir_ph)
1702 || exists(to_dir_ph))
1703 cout << !exists(from_dir_ph) << " " << !is_directory(from_dir_ph)
1704 << " " << exists(to_dir_ph);
1705
1706# ifdef BOOST_POSIX
1707 struct stat from_stat;
1708 if ( (::stat( from_dir_ph.string().c_str(), &from_stat ) != 0)
1709 || ::mkdir(to_dir_ph.native_directory_string().c_str(),
1710 from_stat.st_mode)!=0)
1711# endif
1712 }
1713
1714string escapePath(string input)
1715{
1716 string output;
1717 string escape_me = "& ;()|<>\"'\\#*?$";
1718 for (unsigned int i = 0; i < input.size(); i++)
1719 {
1720 for (unsigned int j = 0; j < escape_me.size(); j++)
1721 if (input[i] == escape_me[j])
1722 output += '\\';
1723 output += input[i];
1724 }
1725 return output;
1726}
1727
1728Install_info_cfg::Install_info_cfg()
1729{
1730 AEVersion = "2009-07b";
1731 InstallerVersion = "1.0.1";
1732 DaodanVersion = "1.0";
1733 OniSplitVersion = "0.9.38.0";
1734 WinGUIVersion = "0";
1735 MacGUIVersion = "0";
1736 patch = false;
1737 globalizationRequired = false;
1738 deleteList.reserve(255);
1739}
1740
1741ModPackage::ModPackage()
1742{
1743 isInstalled = true; // replace with function
1744 name = "";
1745 modStringName = "";
1746 modStringVersion = 0;
1747 platform = "Both";
1748 hasOnis = false;
1749 hasDeltas = false;
1750 hasBSL = false;
1751 hasAddon = false;
1752 hasDats = false;
1753 category = "";
1754 creator = "";
1755 isEngine = false;
1756 readme = "";
1757 globalNeeded = true;
1758}
1759#ifndef WIN32
1760void Sleep(int ms)
1761{
1762 sleep(ms / 1000);
1763}
1764#endif
1765#ifdef WIN32
1766
1767void RedirectIOToConsole()
1768{
1769 int hConHandle;
1770 long lStdHandle;
1771 CONSOLE_SCREEN_BUFFER_INFO coninfo;
1772 FILE *fp;
1773
1774 // allocate a console for this app
1775 AllocConsole();
1776
1777 // set the screen buffer to be big enough to let us scroll text
1778 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
1779 &coninfo);
1780 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
1781 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
1782 coninfo.dwSize);
1783
1784 // redirect unbuffered STDOUT to the console
1785 lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
1786 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1787 fp = _fdopen( hConHandle, "w" );
1788 *stdout = *fp;
1789 setvbuf( stdout, NULL, _IONBF, 0 );
1790
1791 // redirect unbuffered STDIN to the console
1792 lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
1793 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1794 fp = _fdopen( hConHandle, "r" );
1795 *stdin = *fp;
1796 setvbuf( stdin, NULL, _IONBF, 0 );
1797
1798 // redirect unbuffered STDERR to the console
1799 lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
1800 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1801 fp = _fdopen( hConHandle, "w" );
1802 *stderr = *fp;
1803 setvbuf( stderr, NULL, _IONBF, 0 );
1804
1805 // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
1806 // point to console as well
1807 ios::sync_with_stdio();
1808}
1809#endif
Note: See TracBrowser for help on using the repository browser.