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

Last change on this file since 565 was 565, checked in by iritscen, 13 years ago

Fixing (hopefully) update bug stemming from extra return characters (\r) messing up string comparisons.
Converting #defined installer version global to static string just to be safe (what was I thinking?).

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