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

Last change on this file since 558 was 558, checked in by gumby, 10 years ago

fix for addon files not replacing bsl files if they exist

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