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

Last change on this file since 536 was 526, checked in by gumby, 15 years ago

Fixed bug with BSL
PCs now use complete dats instead of seperated

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