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

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

Compile fixes
BSL Backup

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