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

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

BSLBackup only backupededed on first update.

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