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

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

1/4: fixed superfluous BSL2.log
2/4: fixed slightly dubious package updating code
3/4: Mac AEI now trashes old pkgs instead of deleting them, after a pkg update
4/4: added AEI version check to update code so pkgs requiring newer AEI are skipped

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