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

Last change on this file since 986 was 570, checked in by gumby, 14 years ago

I made a boner. BSL mods should be fixed.

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