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

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

Fixes for WIN32

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