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

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

Installer removes all read only attributes in install folder and a few other files.

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