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

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

BSL addon fixes.

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