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

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

Another place we need to refresh.

File size: 65.3 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 string blah =install_iter->path().string() + "/Mod_Info.cfg";
979 string blah2 = "packages/" + install_iter->path().filename() + "/Mod_Info.cfg";
980 fstream file;
981 file.open((install_iter->path().string() + "/Mod_Info.cfg").c_str());
982 if (!file.fail())
983 updatePackage = fileToModPackage(file, install_iter->path().filename());
984 else
985 {
986 file.close();
987 continue;
988 }
989 if (exists("packages/" + install_iter->path().filename() + "/Mod_Info.cfg"))
990 {
991 file.close();
992 file.clear();
993 file.open(("packages/" + install_iter->path().filename() + "/Mod_Info.cfg").c_str());
994 if (!file.fail())
995 installedPackage = fileToModPackage(file, install_iter->path().filename());
996 file.close();
997 if (updatePackage.modStringVersion > installedPackage.modStringVersion)
998 {
999 if (updatePackage.installerVersion <= INSTALLER_VERSION)
1000 return UPDATE_PKG_AVAIL;
1001 }
1002 }
1003 else
1004 {
1005 file.close();
1006 return UPDATE_PKG_AVAIL;
1007 }
1008 }
1009 }
1010 }
1011 }
1012 catch (exception & ex) {
1013 // setStatusArea("Warning, handled exception: " + (string)ex.what());
1014 }
1015
1016 return UPDATE_NO_UPD_AVAIL;
1017}
1018
1019bool ReadInstallInfoCfg(fstream *fileHandler, Install_info_cfg *info_cfg)
1020{
1021 vector<string> tokens;
1022 vector<string>::iterator iter;
1023 string line;
1024 string strAEVersion = "AE_Version";
1025 string strInstallerVersion = "Installer_Version";
1026 string strDaodanVersion = "Daodan_Version";
1027 string strOniSplitVersion = "OniSplit_Version";
1028 string strGUIWinVersion = "GUI_Win_Version";
1029 string strGUIMacVersion = "GUI_Mac_Version";
1030 string strReglobalize = "Reglobalize";
1031 string strDeleteList = "Delete_List";
1032 string strArrow = "->";
1033 string strDoubleSlash = "//";
1034 string strYes = "Yes"; // this is getting silly
1035
1036 while (getline(*fileHandler, line))
1037 {
1038 StripNewlines(&line);
1039 tokenize(line, tokens);
1040 iter = tokens.begin();
1041
1042 if (tokens.size() >= 3)
1043 {
1044 if (!strAEVersion.compare(*iter))
1045 {
1046 if (!strArrow.compare(*++iter))
1047 info_cfg->AEVersion = *++iter;
1048 else
1049 return false;
1050 }
1051 else if (!strInstallerVersion.compare(*iter))
1052 {
1053 if (!strArrow.compare(*++iter))
1054 info_cfg->InstallerVersion = *++iter;
1055 else
1056 return false;
1057 }
1058 else if (!strDaodanVersion.compare(*iter))
1059 {
1060 if (!strArrow.compare(*++iter))
1061 info_cfg->DaodanVersion = *++iter;
1062 else
1063 return false;
1064 }
1065 else if (!strOniSplitVersion.compare(*iter))
1066 {
1067 if (!strArrow.compare(*++iter))
1068 info_cfg->OniSplitVersion = *++iter;
1069 else
1070 return false;
1071 }
1072 else if (!strGUIWinVersion.compare(*iter))
1073 {
1074 if (!strArrow.compare(*++iter))
1075 info_cfg->WinGUIVersion = *++iter;
1076 else
1077 return false;
1078 }
1079 else if (!strGUIMacVersion.compare(*iter))
1080 {
1081 if (!strArrow.compare(*++iter))
1082 info_cfg->MacGUIVersion = *++iter;
1083 else
1084 return false;
1085 }
1086 else if (!strReglobalize.compare(*iter))
1087 {
1088 if (!strArrow.compare(*++iter))
1089 {
1090 if (!strYes.compare(*++iter))
1091 info_cfg->globalizationRequired = true;
1092 }
1093 else
1094 return false;
1095 }
1096 else if (!strDeleteList.compare(*iter))
1097 {
1098 // We need to perform a totally customized parsing process on this data
1099 if (!strArrow.compare(*++iter))
1100 {
1101 vector<string> tokens2;
1102 tokenize(line, tokens2, ","); // the paths on this line are comma-delimited, so we parse it again
1103 vector<string>::iterator iter2 = tokens2.begin();
1104 string finalPath = "";
1105 for (; iter2 != tokens2.end(); iter2++)
1106 {
1107 finalPath = finalPath + *iter2;
1108
1109 string::size_type loc = finalPath.find("->", 0); // the first word will have "Delete_List ->" at the front, so let's cut that off
1110 if (loc != string::npos)
1111 finalPath = finalPath.substr(loc + 3, finalPath.size());
1112
1113 // If a path has '//' in it, it must contain some optional comments that were at the end of the Delete_List line
1114 loc = finalPath.find("//", 0);
1115 if (loc != string::npos)
1116 finalPath = finalPath.substr(0, loc);
1117
1118 // Trim a single space if it exists at the start or finish; putting more than one space after a comma will break this
1119 if (finalPath.at(0) == ' ')
1120 finalPath = finalPath.substr(1, finalPath.size());
1121 if (finalPath.at(finalPath.size() - 1) == ' ')
1122 finalPath = finalPath.substr(0, finalPath.size() - 1);
1123
1124 // If the tokenized path ends with a '\', then we assume it was followed by a comma
1125 if (finalPath.at(finalPath.size() - 1) == '\\')
1126 {
1127 finalPath = finalPath.substr(0, finalPath.size() - 1); // clip the '\' off the end of the string now that it served its purpose...
1128 finalPath = finalPath + ","; // ...and add the actual comma back at the end
1129 }
1130 else // we can add the path to deleteList, and clear the path; otherwise it will be added to on the next iteration
1131 {
1132 if (StringIsLegalPathForDeletion(finalPath)) // ...and it's not violating any of our security rules as to what can be deleted...
1133 info_cfg->deleteList.push_back(finalPath); // ...then add it to our deleteList in memory
1134 finalPath.clear(); // clear the token we were building up before the next pass
1135 }
1136 }
1137 }
1138 else
1139 return false;
1140 }
1141 }
1142 tokens.clear();
1143 }
1144
1145 return true;
1146}
1147
1148// TODO: Fix security holes here
1149/* There is currently a security hole in this function; the first occurrence of a '.' not followed by a second '.' will prevent the function from
1150 noticing an actual occurrence of '..' later in the string; iow, it only looks after the first period it finds for a second period.
1151 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
1152 possibly be interpreted by the Boost file functions as "the current directory". Iow, both of these checks need to be iterative, not one-time.
1153 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 */
1154
1155/* This function serves as a barrier against the Installer deleting files it shouldn't. *\
1156| It tests for each of the following conditions in the path it is passed: |
1157| A. '..' as the whole path or '/..', '\..' anywhere in the path |
1158| Reason: Moving up from the parent directory, the Edition folder, would allow one |
1159| to delete anything on the hard drive, so all "parent path" references are illegal. |
1160| B. '/' at the beginning of the path |
1161| Reason: In Unix, this means the path starts from root level, as opposed to the |
1162| directory we will evaluate these paths as being relative to, which is Edition/. |
1163| C. '.' as the whole path |
1164| Reason: This would mean "the Edition folder", which is not allowed to be deleted. |
1165| D. 'GameDataFolder' at the end of the path |
1166| Reason: We don't allow the entire GDF to be deleted, only specific files in it. |
1167| E. '*' anywhere in the path |
1168| Reason: We don't want this interpreted as a wildcard; it's best to only delete |
1169*\ files by name. */
1170bool StringIsLegalPathForDeletion(string word)
1171{
1172 string::size_type loc1, loc2;
1173
1174 // Trim ending slashes in order to simplify the test
1175 // Note that we're only altering the local copy of the string here
1176 loc1 = word.find_last_of("\\", word.size());
1177 if (loc1 == word.size() - 1)
1178 word.resize(word.size() - 1);
1179 loc1 = word.find_last_of("/", word.size());
1180 if (loc1 == word.size() - 1)
1181 word.resize(word.size() - 1);
1182
1183 // Test B
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 loc1 = word.find_first_of("/", 0);
1188 if (loc1 == 0)
1189 return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1190
1191 // Test E
1192 loc1 = word.find("*", 0);
1193 if (loc1 != string::npos) // if we found our character before reaching the end of the string
1194 return false; // path cannot contain the '*' character
1195
1196 // Tests A (part 1) and C
1197 loc1 = word.find(".", 0);
1198 if (loc1 != string::npos)
1199 {
1200 if (word.size() == 1)
1201 return false; // path cannot be simply '.', referring to Edition folder itself
1202 loc2 = word.find(".", loc1 + 1);
1203 if (loc2 == loc1 + 1) // make sure this second period comes after the first one
1204 if (word.size() == 2)
1205 return false; // not allowed to reference a parent directory
1206 }
1207
1208 // Test A (part 2)
1209 loc1 = word.find("/..", 0);
1210 if (loc1 != string::npos)
1211 return false; // not allowed to reference a parent directory
1212 loc1 = word.find("\\..", 0);
1213 if (loc1 != string::npos)
1214 return false; // not allowed to reference a parent directory
1215
1216 // Test D
1217 loc1 = word.find("GameDataFolder", 0);
1218 if (loc1 == word.size() - 14) // if "GameDataFolder" is the last 14 characters of the string...
1219 return false; // not allowed to delete the GDF
1220
1221 return true;
1222}
1223
1224bool ProcessInstallerUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE)
1225{
1226 ofstream file;
1227 string shellScript;
1228
1229 ptime startTime(second_clock::local_time());
1230 string strStartTime = to_simple_string(startTime);
1231 string progressMsg = "Installer being updated to:\n" +
1232 updateAE->InstallerVersion + " on " + strStartTime;
1233 file.open("Update.log");
1234 if (!file.fail())
1235 file << progressMsg.c_str();
1236 file.close();
1237 file.clear();
1238
1239 string popenCommand = "../updates/" + strEUFN + "/install/";
1240#ifdef WIN32
1241 popenCommand = "replace_installer.bat";
1242#else
1243 // We can't just use '~' to mean "the home directory" because we need to check the path in C...
1244 // ...so we actually get the current user's shortname and manually construct the path to home
1245 FILE *fUserName = NULL;
1246 char chrUserName[32];
1247 fUserName = popen("whoami", "r");
1248 fgets(chrUserName, sizeof(chrUserName), fUserName);
1249 pclose(fUserName);
1250 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1251 int endOfName = strUserName.find("\n", 0);
1252 string pathToTrash = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1253 tm tmStartTime = to_tm(startTime);
1254 pathToTrash = pathToTrash + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1255 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec); // lol
1256 create_directory(pathToTrash);
1257 // The script takes as a parameter the path the old Installer should go to, in quotes
1258 popenCommand = "bash " + popenCommand + "replace_installer.sh " + pathToTrash + "/Installer.app";
1259
1260#endif
1261 file.close();
1262 file.clear();
1263#ifdef WIN32
1264 system(popenCommand.c_str());
1265#else
1266 popen(popenCommand.c_str(), "r");
1267#endif
1268 return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running
1269}
1270
1271bool ProcessAEUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
1272{
1273 fstream file;
1274 string line;
1275 vector<string> tokens, updateStarted;
1276 string strInstaller = "Installer";
1277 string strWas = "was";
1278 string strPathToEUFN = ("../updates/" + strEUFN + "/"); // strEUFN is set by GetUpdateStatus()
1279 string strPathToEUFNInstall = ("../updates/" + strEUFN + "/install/");
1280 string strPathToEUFNPackages = ("../updates/" + strEUFN + "/install/packages/");
1281 string strPathToPackages = "packages/";
1282 string strGlobalize = "Globalize/";
1283 string strOniSplit = "OniSplit.exe";
1284 string strDaodan = "binkw32.dll";
1285 string strWinGUI = "onisplit_gui.exe";
1286 string strWinGUILang = "ospgui_lang.ini";
1287 string strMacGUI = "AETools.app";
1288#ifdef WIN32
1289 string strOniApp = "Oni.exe";
1290#else
1291 string strOniApp = "Oni.app";
1292#endif
1293 bool needNewTrashDir = false;
1294 bool readingVerAndDate = false;
1295
1296#ifdef WIN32
1297 string strTrashDir = "Trash\\"; // string unused in Windows because files are simply deleted
1298#else
1299 FILE *fUserName = NULL;
1300 char chrUserName[32];
1301 fUserName = popen("whoami", "r");
1302 fgets(chrUserName, sizeof(chrUserName), fUserName);
1303 pclose(fUserName);
1304 string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1305 int endOfName = strUserName.find("\n", 0);
1306 string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1307#endif
1308
1309 // Write to log that we are beginning the update process
1310 ptime startTime(second_clock::local_time());
1311 string strStartTime = to_simple_string(startTime);
1312 string progressMsg = "\nEdition being updated to:\n" +
1313 updateAE->AEVersion + " on " + strStartTime;
1314 file.open("Update.log");
1315 if (!file.fail())
1316 file << progressMsg.c_str();
1317
1318 if (*installerJustUpdated) // then we want to know what folder in the Trash the Installer was placed in...
1319 {
1320 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
1321 {
1322 getline(file, line);
1323 tokenize(line, tokens);
1324
1325 if (tokens.capacity() >= 4)
1326 if (!strInstaller.compare(tokens[0]))
1327 if (!strWas.compare(tokens[1]))
1328 readingVerAndDate = true;
1329 if (readingVerAndDate && tokens.capacity() >= 3)
1330 tokenize(tokens[2], updateStarted, "-");
1331 }
1332 if (updateStarted.capacity() < 3)
1333 needNewTrashDir = true;
1334 else
1335 {
1336 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "-" +
1337 updateStarted[0] + "-" + updateStarted[1] + "-" + updateStarted[2] + "/";
1338 if (!exists(strTrashDir))
1339 needNewTrashDir = true;
1340 }
1341 }
1342#ifndef WIN32
1343 if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to
1344 {
1345 tm tmStartTime = to_tm(startTime);
1346 strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1347 boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1348 create_directory(strTrashDir);
1349 }
1350#endif
1351 file.close();
1352 file.clear();
1353
1354 // Special code to replace our special files -- the Oni app, OniSplit, the Daodan DLL, and the GUI for OniSplit
1355 if (exists(strPathToEUFN + strOniApp))
1356 {
1357 if (exists(strOniApp))
1358 rename((path)strOniApp, (path)(strTrashDir + strOniApp));
1359 rename((path)(strPathToEUFN + strOniApp), (path)strOniApp);
1360 }
1361 if (updateAE->OniSplitVersion.compare(currentAE->OniSplitVersion) >= 1)
1362 {
1363 if (exists(strPathToEUFNInstall + strOniSplit))
1364 {
1365 if (exists(strOniSplit))
1366 rename((path)strOniSplit, (path)(strTrashDir + strOniSplit));
1367 rename((path)(strPathToEUFNInstall + strOniSplit), (path)strOniSplit);
1368 }
1369 }
1370#ifdef WIN32
1371 if (updateAE->DaodanVersion.compare(currentAE->DaodanVersion) >= 1)
1372 {
1373 if (exists(strPathToEUFN + strDaodan))
1374 {
1375 if (exists(("../" + strDaodan)))
1376 rename((path)("../" + strDaodan), (path)(strTrashDir + strDaodan));
1377 rename((path)(strPathToEUFN + strDaodan), (path)("../" + strDaodan));
1378 }
1379 }
1380 if (updateAE->WinGUIVersion.compare(currentAE->WinGUIVersion) >= 1)
1381 {
1382 if (exists(strPathToEUFNInstall + strWinGUI))
1383 {
1384 if (exists((path)strWinGUI))
1385 rename((path)strWinGUI, (path)(strTrashDir + strWinGUI));
1386 if (exists(strWinGUILang))
1387 rename((path)strWinGUILang, (path)(strTrashDir + strWinGUILang));
1388 rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI);
1389 rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang);
1390 }
1391 }
1392#else
1393 if (updateAE->MacGUIVersion.compare(currentAE->MacGUIVersion) >= 1)
1394 {
1395 if (exists(strPathToEUFN + strMacGUI))
1396 {
1397 if (exists(("../" + strMacGUI)))
1398 rename((path)("../" + strMacGUI), (path)(strTrashDir + strMacGUI));
1399 rename((path)(strPathToEUFN + strMacGUI), (path)("../" + strMacGUI));
1400 }
1401 }
1402#endif
1403
1404 // Now we trash whatever's in DeleteList; this allows us to clear out obsolete files in the previous AE install
1405 // Before moving a file to the Trash, we need to make sure each of the file's parent paths exists in the Trash...
1406 // ...so we iterate through the hierarchy of the file path, checking for each one and creating it if necessary
1407 for (vector<string>::iterator iter = updateAE->deleteList.begin(); iter != updateAE->deleteList.end(); iter++)
1408 {
1409 string thePath = *iter;
1410 if (exists((path)("../" + thePath)))
1411 {
1412 string aParentPath;
1413 string::size_type curPos = thePath.find("/", 0);
1414 if (curPos != string::npos)
1415 aParentPath = thePath.substr(0, curPos);
1416 string::size_type lastPos = curPos;
1417 while (curPos != string::npos && curPos < thePath.size())
1418 {
1419 aParentPath = aParentPath + thePath.substr(lastPos, curPos - lastPos);
1420 if (!exists(strTrashDir + aParentPath))
1421 create_directory(strTrashDir + aParentPath);
1422 lastPos = curPos + 1;
1423 curPos = thePath.find("/", lastPos);
1424 aParentPath = aParentPath + "/";
1425 }
1426#ifndef WIN32
1427 rename((path)("../" + thePath), (path)(strTrashDir + thePath));
1428#else
1429 remove((path)("../" + thePath));
1430#endif
1431 }
1432 }
1433
1434 ProcessPackageUpdates(strPathToEUFNPackages, strPathToPackages);
1435
1436 // 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...
1437 // ...and folders which do not exist in the current AE will be created there
1438 vector<string> foldersToMake, filesToMove;
1439 string thePath;
1440 for (recursive_directory_iterator dir_itr(strPathToEUFNPackages + strGlobalize), end_itr; dir_itr != end_itr; ++dir_itr)
1441 {
1442 thePath = dir_itr->path().string();
1443 MakePathLocalToGlobalize(&thePath);
1444 if (is_regular_file(dir_itr->status()))
1445 {
1446 if (dir_itr->filename().at(0) != '.') // skip over dot-files, which are invisible in Unix
1447 filesToMove.push_back(thePath);
1448 }
1449 else if (is_directory(dir_itr->status()))
1450 {
1451 if (!exists(strPathToPackages + strGlobalize + thePath))
1452 foldersToMake.push_back(thePath);
1453 }
1454 }
1455 // Sort the foldersToMake strings by length, which is a fast solution to the problem of "How do we make sure we create folder 'parent/'...
1456 // ...before folder 'parent/child/'"?
1457 sort(foldersToMake.begin(), foldersToMake.end(), SortBySize); // SortBySize is a custom comparison function found later in this source file
1458 // First make the folders that don't exist in the current AE, so all the files have a place to go
1459 for (vector<string>::iterator iter = foldersToMake.begin(); iter != foldersToMake.end(); iter++)
1460 {
1461 create_directory(strPathToPackages + strGlobalize + *iter);
1462 }
1463 for (vector<string>::iterator iter = filesToMove.begin(); iter != filesToMove.end(); iter++)
1464 {
1465 if (exists(strPathToPackages + strGlobalize + *iter))
1466 rename((path)(strPathToPackages + strGlobalize + *iter), (path)(strTrashDir + *iter));
1467 rename((path)(strPathToEUFNPackages + strGlobalize + *iter), (path)(strPathToPackages + strGlobalize + *iter));
1468 }
1469
1470 // Clean up after ourselves, trashing any packages or programs in the update package that are not newer than the current AE
1471 create_directory(strTrashDir + "Unneeded update files");
1472 rename((path)strPathToEUFN, (path)(strTrashDir + "Unneeded update files/" + strEUFN));
1473
1474 // Write to log that we are finished with update
1475 ptime end_time(second_clock::local_time());
1476 string progressMsg2 = "Edition was updated to:\n" +
1477 updateAE->AEVersion + " on " + to_simple_string(end_time);
1478
1479 file.open("Update.log");
1480
1481 if(!file.fail())
1482 file.write(progressMsg2.c_str(), sizeof(progressMsg2.c_str()));
1483
1484 file.close();
1485 file.clear();
1486
1487 if (updateAE->globalizationRequired)
1488 CheckForGlobalization(true); // the 'true' value forces re-globalization
1489
1490 globalPackages = getPackages(); // refresh the list in memory
1491 // TODO: Refresh the packages list in the window
1492 wxCommandEvent e;
1493 TheWindow->OnRefreshButtonClick( e );
1494 return true;
1495}
1496extern MainWindow* TheWindow;
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.