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

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

really really depreciated ModString
Fixed bug with detelist on windows systems

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