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

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

Fixes for incorrect AE version comparison and trashing of files during update.

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