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

Last change on this file since 550 was 550, checked in by gumby, 9 years ago

fixing broken code

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