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

Last change on this file since 515 was 511, checked in by iritscen, 15 years ago

Fixed extern placement that Xcode doesn't like, removed some unused variables.

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 // TODO: Refresh the packages list in the window
1491 wxCommandEvent e;
1492 TheWindow->OnRefreshButtonClick( e );
1493 return true;
1494}
1495
1496void ProcessPackageUpdates(string pathToUpdate, string strPathToPackages)
1497{
1498 ptime startTime(second_clock::local_time());
1499#ifdef WIN32
1500 string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
1501#else
1502 FILE *fUserName = NULL;
1503 char chrUserName[32];
1504 fUserName = popen("whoami", "r");
1505 fgets(chrUserName, sizeof(chrUserName), fUserName);
1506 pclose(fUserName);
1507 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1508 int endOfName = strUserName.find("\n", 0);
1509 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1510 bool needNewTrashDir = true;
1511 tm tmStartTime = to_tm(startTime);
1512#endif
1513
1514 try
1515 {
1516 directory_iterator end;
1517 for (directory_iterator update_iter(pathToUpdate); update_iter != end; ++update_iter)
1518 {
1519 ModPackage installedPackage, updatePackage;
1520 string updtPath = update_iter->path().string();
1521 string updtFolder = update_iter->path().filename();
1522 string updtModInfo = updtPath + "/Mod_Info.cfg";
1523 string instModInfo = strPathToPackages + "/" + updtFolder + "/Mod_Info.cfg";
1524 if (!boost::iequals(updtFolder, "Edition")
1525 && !boost::iequals(updtFolder, "Edition-patch")
1526 && is_directory(update_iter->path())
1527 && exists(updtModInfo))
1528 {
1529 fstream file;
1530 file.open((updtModInfo).c_str());
1531 if (!file.fail())
1532 updatePackage = fileToModPackage(file, updtFolder);
1533 else
1534 {
1535 file.close();
1536 continue;
1537 }
1538 if (exists(instModInfo))
1539 {
1540 file.close();
1541 file.clear();
1542 file.open(instModInfo.c_str());
1543 if (!file.fail())
1544 {
1545 installedPackage = fileToModPackage(file, updtFolder);
1546 }
1547 file.close();
1548 }
1549 file.close();
1550 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
1551 {
1552 if (updatePackage.installerVersion <= INSTALLER_VERSION)
1553 {
1554 if(exists(strPathToPackages + "/" + updatePackage.modStringName)) {
1555#ifdef WIN32
1556 remove_all((path)(strPathToPackages + "/" + updatePackage.modStringName));
1557#else
1558 if (needNewTrashDir)
1559 {
1560 strTrashDir = strTrashDir + "Old_packages_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1561 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1562 create_directory(strTrashDir);
1563 needNewTrashDir = false;
1564 }
1565 rename((path)(strPathToPackages + "/" + updatePackage.modStringName), (path)(strTrashDir + updatePackage.modStringName));
1566#endif
1567 }
1568 rename((path)(pathToUpdate + "/" + updatePackage.modStringName), (path)(strPathToPackages + "/" + updatePackage.modStringName));
1569 }
1570 }
1571 }
1572 }
1573 }
1574 catch (exception & ex)
1575 {
1576 setStatusArea("Warning, handled exception: " + (string)ex.what());
1577 }
1578 wxCommandEvent e;
1579 TheWindow->OnRefreshButtonClick( e );
1580}
1581
1582/* MakePathLocalToGlobalize is a function used once by ProcessAEUpdate() that takes a file in an \
1583| update's Globalize folder and changes its path, originally relative to the Installer, to be |
1584| relative to the structure of the Globalize folder; this makes it easier to have the Installer |
1585\ move said file from an update's Globalize folder to the current Globalize folder. */
1586void MakePathLocalToGlobalize(string *installerBasedPath)
1587{
1588 int deleteToHere = 0;
1589 deleteToHere = installerBasedPath->find("Globalize/");
1590 if (deleteToHere != 0)
1591 {
1592 deleteToHere += strlen("Globalize/");
1593 installerBasedPath->erase(0, deleteToHere);
1594 }
1595}
1596
1597/* SortBySize is a custom comparison function that we call when using the C++ sort() function in \
1598\ ProcessAEUpdate() on some strings that we want to sort by length. */
1599bool SortBySize(string a, string b)
1600{
1601 return (a.size() < b.size());
1602}
1603
1604//stolen token function...
1605void tokenize(const string& str, vector<string>& tokens, const string& delimiters)
1606{
1607 // Skip delimiters at beginning.
1608 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1609 // Find first "non-delimiter".
1610 string::size_type pos = str.find_first_of(delimiters, lastPos);
1611
1612 while (string::npos != pos || string::npos != lastPos)
1613 {
1614 // Found a token, add it to the vector.
1615 tokens.push_back(str.substr(lastPos, pos - lastPos));
1616 // Skip delimiters. Note the "not_of"
1617 lastPos = str.find_first_not_of(delimiters, pos);
1618 // Find next "non-delimiter"
1619 pos = str.find_first_of(delimiters, lastPos);
1620 }
1621}
1622
1623/* StripNewlines() gets rids of any linebreaks that come from text returned by getline(); \
1624| getline() should be stripping those out, but Windows CR/LF files seem to be sneaking |
1625\ some extra return characters into strings in the ReadInstallInfoCfg() function. */
1626void StripNewlines(string *theLine)
1627{
1628 int deleteFromHere = 0;
1629 deleteFromHere = theLine->find("\r");
1630 if (deleteFromHere > 0)
1631 theLine->erase(deleteFromHere, theLine->size());
1632}
1633
1634void clearOldDats(void) {
1635 directory_iterator end_iter_gdf;
1636 for ( directory_iterator dir_itr_gdf( "../GameDataFolder" );
1637 dir_itr_gdf != end_iter_gdf;
1638 ++dir_itr_gdf )
1639 {
1640 if ( dir_itr_gdf->path().extension() == ".dat" || dir_itr_gdf->path().extension() == ".raw" || dir_itr_gdf->path().extension() == ".sep" ) {
1641 remove( dir_itr_gdf->path() );
1642 }
1643
1644 }
1645
1646}
1647
1648// this function copies files and directories. If copying a
1649// directory to a directory, it copies recursively.
1650
1651//pardon the mess, I did this at midnight, and had to fix a bug
1652void copy( const path & from_ph,
1653 const path & to_ph )
1654{
1655 cout << to_ph.string() << "\n";
1656 // Make sure that the destination, if it exists, is a directory
1657 if((exists(to_ph) && !is_directory(to_ph)) || (!exists(from_ph))) cout << "error";
1658 if(!is_directory(from_ph))
1659 {
1660
1661 if(exists(to_ph))
1662 {
1663 copy_file(from_ph,to_ph/from_ph.filename());
1664 }
1665 else
1666 {
1667 try{
1668
1669 copy_file(from_ph,to_ph);
1670 }
1671 catch (exception ex){
1672 cout << from_ph.string() << " to " << to_ph.string() << "\n";
1673 }
1674 }
1675
1676 }
1677 else if(from_ph.filename() != ".svn")
1678 {
1679 path destination;
1680 if(!exists(to_ph))
1681 {
1682 destination=to_ph;
1683 }
1684 else
1685 {
1686 destination=to_ph/from_ph.filename();
1687 }
1688
1689 for(directory_iterator i(from_ph); i!=directory_iterator(); ++i)
1690 {
1691 //the idiot who coded this in the first place (not me)
1692 //forgot to make a new directory. Exception city. x_x
1693 create_directory(destination);
1694 copy(*i,destination/i->filename());
1695 }
1696 }
1697}
1698
1699void copy_directory( const path &from_dir_ph,
1700 const path &to_dir_ph)
1701{
1702 if(!exists(from_dir_ph) || !is_directory(from_dir_ph)
1703 || exists(to_dir_ph))
1704 cout << !exists(from_dir_ph) << " " << !is_directory(from_dir_ph)
1705 << " " << exists(to_dir_ph);
1706
1707# ifdef BOOST_POSIX
1708 struct stat from_stat;
1709 if ( (::stat( from_dir_ph.string().c_str(), &from_stat ) != 0)
1710 || ::mkdir(to_dir_ph.native_directory_string().c_str(),
1711 from_stat.st_mode)!=0)
1712# endif
1713 }
1714
1715string escapePath(string input)
1716{
1717 string output;
1718 string escape_me = "& ;()|<>\"'\\#*?$";
1719 for (unsigned int i = 0; i < input.size(); i++)
1720 {
1721 for (unsigned int j = 0; j < escape_me.size(); j++)
1722 if (input[i] == escape_me[j])
1723 output += '\\';
1724 output += input[i];
1725 }
1726 return output;
1727}
1728
1729Install_info_cfg::Install_info_cfg()
1730{
1731 AEVersion = "2009-07b";
1732 InstallerVersion = "1.0.1";
1733 DaodanVersion = "1.0";
1734 OniSplitVersion = "0.9.38.0";
1735 WinGUIVersion = "0";
1736 MacGUIVersion = "0";
1737 patch = false;
1738 globalizationRequired = false;
1739 deleteList.reserve(255);
1740}
1741
1742ModPackage::ModPackage()
1743{
1744 isInstalled = true; // replace with function
1745 name = "";
1746 modStringName = "";
1747 modStringVersion = 0;
1748 platform = "Both";
1749 hasOnis = false;
1750 hasDeltas = false;
1751 hasBSL = false;
1752 hasAddon = false;
1753 hasDats = false;
1754 category = "";
1755 creator = "";
1756 isEngine = false;
1757 readme = "";
1758 globalNeeded = true;
1759}
1760#ifndef WIN32
1761void Sleep(int ms)
1762{
1763 sleep(ms / 1000);
1764}
1765#endif
1766#ifdef WIN32
1767
1768void RedirectIOToConsole()
1769{
1770 int hConHandle;
1771 long lStdHandle;
1772 CONSOLE_SCREEN_BUFFER_INFO coninfo;
1773 FILE *fp;
1774
1775 // allocate a console for this app
1776 AllocConsole();
1777
1778 // set the screen buffer to be big enough to let us scroll text
1779 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
1780 &coninfo);
1781 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
1782 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
1783 coninfo.dwSize);
1784
1785 // redirect unbuffered STDOUT to the console
1786 lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
1787 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1788 fp = _fdopen( hConHandle, "w" );
1789 *stdout = *fp;
1790 setvbuf( stdout, NULL, _IONBF, 0 );
1791
1792 // redirect unbuffered STDIN to the console
1793 lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
1794 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1795 fp = _fdopen( hConHandle, "r" );
1796 *stdin = *fp;
1797 setvbuf( stdin, NULL, _IONBF, 0 );
1798
1799 // redirect unbuffered STDERR to the console
1800 lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
1801 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1802 fp = _fdopen( hConHandle, "w" );
1803 *stderr = *fp;
1804 setvbuf( stderr, NULL, _IONBF, 0 );
1805
1806 // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
1807 // point to console as well
1808 ios::sync_with_stdio();
1809}
1810#endif
Note: See TracBrowser for help on using the repository browser.