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

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

BSL addon fixes.

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