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

Last change on this file since 543 was 543, checked in by gumby, 11 years ago

Fixed PC issues, added exception handling.

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