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

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

Updated credits within AEI.
AEI now makes sure there is always an updates/ folder in Edition/.

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