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

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

Windows fixes.

File size: 61.0 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        popenCommand = "replace_installer.bat";
1146#else
1147        // We can't just use '~' to mean "the home directory" because we need to check the path in C...
1148        // ...so we actually get the current user's shortname and manually construct the path to home
1149        FILE *fUserName = NULL;
1150        char chrUserName[32];
1151        fUserName = popen("whoami", "r");
1152        fgets(chrUserName, sizeof(chrUserName), fUserName);
1153        pclose(fUserName);
1154        string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1155        int endOfName = strUserName.find("\n", 0);
1156        string pathToTrash = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1157        tm tmStartTime = to_tm(startTime);
1158        pathToTrash = pathToTrash + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1159        boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec); // lol
1160        create_directory(pathToTrash);
1161        // The script takes as a parameter the path the old Installer should go to, in quotes
1162        popenCommand = "bash " + popenCommand + "replace_installer.sh " + pathToTrash + "/Installer.app";
1163                                   
1164#endif
1165        file.close();
1166        file.clear();
1167#ifdef WIN32
1168        system(popenCommand.c_str());
1169#else
1170        popen(popenCommand.c_str(), "r");
1171#endif
1172        return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running
1173}
1174
1175bool ProcessAEUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
1176{
1177        fstream file;
1178        string line;
1179        vector<string> tokens, updateStarted;
1180        string strInstaller = "Installer";
1181        string strWas = "was";
1182        string strPathToEUFN = ("../updates/" + strEUFN + "/"); // strEUFN is set by GetUpdateStatus()
1183        string strPathToEUFNInstall = ("../updates/" + strEUFN + "/install/");
1184        string strPathToEUFNPackages = ("../updates/" + strEUFN + "/install/packages/");
1185        string strPathToPackages = "packages/";
1186        string strGlobalize = "Globalize/";
1187        string strOniSplit = "OniSplit.exe";
1188        string strDaodan = "binkw32.dll";
1189        string strWinGUI = "onisplit_gui.exe";
1190        string strWinGUILang = "ospgui_lang.ini";
1191        string strMacGUI = "AETools.app";
1192#ifdef WIN32
1193        string strOniApp = "Oni.exe";
1194#else
1195        string strOniApp = "Oni.app";
1196#endif
1197        bool needNewTrashDir = false;
1198        bool readingVerAndDate = false;
1199       
1200        // TODO: Fill in Windows equivalent of code below
1201#ifdef WIN32
1202        string strTrashDir = "Trash\\";
1203#else
1204        FILE *fUserName = NULL;
1205        char chrUserName[32];
1206        fUserName = popen("whoami", "r");
1207        fgets(chrUserName, sizeof(chrUserName), fUserName);
1208        pclose(fUserName);
1209        string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1210        int endOfName = strUserName.find("\n", 0);
1211        string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1212#endif
1213       
1214        // Write to log that we are beginning the update process
1215        ptime startTime(second_clock::local_time());
1216        string strStartTime = to_simple_string(startTime);
1217        string progressMsg = "\nEdition being updated to:\n" +
1218                                                 updateAE->AEVersion + " on " + strStartTime;
1219        file.open("Update.log");
1220        if (!file.fail())
1221                file << progressMsg.c_str();
1222       
1223        if (*installerJustUpdated) // then we want to know what folder in the Trash the Installer was placed in...
1224        {
1225                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
1226                {
1227                        getline(file, line);
1228                        tokenize(line, tokens);
1229                       
1230                        if (tokens.capacity() >= 4)
1231                                if (!strInstaller.compare(tokens[0]))
1232                                        if (!strWas.compare(tokens[1]))
1233                                                readingVerAndDate = true;
1234                        if (readingVerAndDate && tokens.capacity() >= 3)
1235                                tokenize(tokens[2], updateStarted, "-");
1236                }
1237                if (updateStarted.capacity() < 3)
1238                        needNewTrashDir = true;
1239                else
1240                {
1241                        strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "-" +
1242                                                  updateStarted[0] + "-" + updateStarted[1] + "-" + updateStarted[2] + "/";
1243                        if (!exists(strTrashDir))
1244                                needNewTrashDir = true;
1245                }
1246        }
1247#ifndef WIN32
1248        if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to
1249        {
1250                tm tmStartTime = to_tm(startTime);
1251                strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1252                                          boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1253                create_directory(strTrashDir);
1254        }
1255#endif
1256        file.close();
1257        file.clear();
1258
1259        // Special code to replace our special files -- the Oni app, OniSplit, the Daodan DLL, and the GUI for OniSplit
1260        if (exists(strPathToEUFN + strOniApp))
1261        {
1262                if (exists(strOniApp))
1263                        rename((path)strOniApp, (path)(strTrashDir + strOniApp));
1264                rename((path)(strPathToEUFN + strOniApp), (path)strOniApp);
1265        }
1266        if (updateAE->OniSplitVersion.compare(currentAE->OniSplitVersion) >= 1)
1267        {
1268                if (exists(strPathToEUFNInstall + strOniSplit))
1269                {
1270                        if (exists(strOniSplit))
1271                                rename((path)strOniSplit, (path)(strTrashDir + strOniSplit));
1272                        rename((path)(strPathToEUFNInstall + strOniSplit), (path)strOniSplit);
1273                }
1274        }
1275#ifdef WIN32
1276        if (updateAE->DaodanVersion.compare(currentAE->DaodanVersion) >= 1)
1277        {
1278                if (exists(strPathToEUFN + strDaodan))
1279                {
1280                        if (exists(("../" + strDaodan)))
1281                                rename((path)("../" + strDaodan), (path)(strTrashDir + strDaodan));
1282                        rename((path)(strPathToEUFN + strDaodan), (path)("../" + strDaodan));
1283                }
1284        }
1285        if (updateAE->WinGUIVersion.compare(currentAE->WinGUIVersion) >= 1)
1286        {
1287                if (exists(strPathToEUFNInstall + strWinGUI))
1288                {
1289                        if (exists((path)strWinGUI))
1290                                rename((path)strWinGUI, (path)(strTrashDir + strWinGUI));
1291                        if (exists(strWinGUILang))
1292                                rename((path)strWinGUILang, (path)(strTrashDir + strWinGUILang));
1293                        rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI);
1294                        rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang);
1295                }
1296        }
1297#else
1298        if (updateAE->MacGUIVersion.compare(currentAE->MacGUIVersion) >= 1)
1299        {
1300                if (exists(strPathToEUFN + strMacGUI))
1301                {
1302                        if (exists(("../" + strMacGUI)))
1303                                rename((path)("../" + strMacGUI), (path)(strTrashDir + strMacGUI));
1304                        rename((path)(strPathToEUFN + strMacGUI), (path)("../" + strMacGUI));
1305                }
1306        }
1307#endif
1308       
1309        // Now we trash whatever's in DeleteList; this allows us to clear out obsolete files in the previous AE install
1310        // Before moving a file to the Trash, we need to make sure each of the file's parent paths exists in the Trash...
1311        // ...so we iterate through the hierarchy of the file path, checking for each one and creating it if necessary
1312        for (vector<string>::iterator iter = updateAE->deleteList.begin(); iter != updateAE->deleteList.end(); iter++)
1313        {
1314                string thePath = *iter;
1315                if (exists((path)("../" + thePath)))
1316                {
1317                        string aParentPath;
1318                        string::size_type curPos = thePath.find("/", 0);
1319                        if (curPos != string::npos)
1320                                aParentPath = thePath.substr(0, curPos);
1321                        string::size_type lastPos = curPos;
1322                        while (curPos != string::npos && curPos < thePath.size())
1323                        {
1324                                aParentPath = aParentPath + thePath.substr(lastPos, curPos - lastPos);
1325                                if (!exists(strTrashDir + aParentPath))
1326                                        create_directory(strTrashDir + aParentPath);
1327                                lastPos = curPos + 1;
1328                                curPos = thePath.find("/", lastPos);
1329                                aParentPath = aParentPath + "/";
1330                        }
1331#ifndef WIN32
1332                        rename((path)("../" + thePath), (path)(strTrashDir + thePath));
1333#else
1334                        remove((path)("../" + thePath));
1335#endif
1336                }
1337        }
1338       
1339        // Now we crawl the update's package folders for newer versions and move them over, trashing ones that are already present
1340        vector<ModPackage> updatePackages, currentPackages;
1341        bool matchFound;
1342        updatePackages.reserve(256);
1343        currentPackages.reserve(256);
1344       
1345        currentPackages = getPackages();
1346        updatePackages = getPackages(strPathToEUFNPackages);
1347
1348        for (vector<ModPackage>::iterator iter1 = updatePackages.begin(); iter1 != updatePackages.end(); iter1++)
1349        {
1350                matchFound = false;
1351                for (vector<ModPackage>::iterator iter2 = currentPackages.begin(); iter2 != currentPackages.end(); iter2++)
1352                {
1353                        if (!iter1->modStringName.compare(iter2->modStringName))
1354                        {
1355                                matchFound = true;
1356                                if (iter1->modStringVersion > iter2->modStringVersion)
1357                                {
1358#ifndef WIN32
1359                                        rename((path)(strPathToPackages + iter2->modStringName), (path)(strTrashDir + iter2->modStringName));
1360#else
1361                                        remove((path)(strPathToPackages + iter2->modStringName));
1362#endif
1363                                        rename((path)(strPathToEUFNPackages + iter1->modStringName), (path)(strPathToPackages + iter1->modStringName));
1364                                }
1365                        }
1366                }
1367                if (!matchFound) // then there's no old package in the way, so just move in the one from the update
1368                        rename((path)(strPathToEUFNPackages + iter1->modStringName), (path)(strPathToPackages + iter1->modStringName));
1369        }
1370       
1371        // 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...
1372        // ...and folders which do not exist in the current AE will be created there
1373        vector<string> foldersToMake, filesToMove;
1374        string thePath;
1375        for (recursive_directory_iterator dir_itr(strPathToEUFNPackages + strGlobalize), end_itr; dir_itr != end_itr; ++dir_itr)
1376        {
1377                thePath = dir_itr->path().string();
1378                MakePathLocalToGlobalize(&thePath);
1379                if (is_regular_file(dir_itr->status()))
1380                {
1381                        if (dir_itr->filename().at(0) != '.') // skip over dot-files, which are invisible in Unix
1382                                filesToMove.push_back(thePath);
1383                }
1384                else if (is_directory(dir_itr->status()))
1385                {
1386                        if (!exists(strPathToPackages + strGlobalize + thePath))
1387                                foldersToMake.push_back(thePath);
1388                }
1389        }
1390        // Sort the foldersToMake strings by length, which is a fast solution to the problem of "How do we make sure we create folder 'parent/'...
1391        // ...before folder 'parent/child/'"?
1392        sort(foldersToMake.begin(), foldersToMake.end(), SortBySize); // SortBySize is a custom comparison function found later in this source file
1393        // First make the folders that don't exist in the current AE, so all the files have a place to go
1394        for (vector<string>::iterator iter = foldersToMake.begin(); iter != foldersToMake.end(); iter++)
1395        {
1396                create_directory(strPathToPackages + strGlobalize + *iter);
1397        }
1398        for (vector<string>::iterator iter = filesToMove.begin(); iter != filesToMove.end(); iter++)
1399        {
1400                if (exists(strPathToPackages + strGlobalize + *iter))
1401                        rename((path)(strPathToPackages + strGlobalize + *iter), (path)(strTrashDir + *iter));
1402                rename((path)(strPathToEUFNPackages + strGlobalize + *iter), (path)(strPathToPackages + strGlobalize + *iter));
1403        }
1404       
1405        // Clean up after ourselves, trashing any packages or programs in the update package that are not newer than the current AE
1406        create_directory(strTrashDir + "Unneeded update files");
1407        rename((path)strPathToEUFN, (path)(strTrashDir + "Unneeded update files/" + strEUFN));
1408       
1409        // Write to log that we are finished with update
1410        ptime end_time(second_clock::local_time());
1411        string progressMsg2 = "Edition was updated to:\n" +
1412                                                  updateAE->AEVersion + " on " + to_simple_string(end_time);
1413       
1414        file.open("Update.log");
1415       
1416        if(!file.fail())
1417                file.write(progressMsg2.c_str(), sizeof(progressMsg2.c_str()));
1418       
1419        file.close();
1420        file.clear();
1421       
1422        if (updateAE->globalizationRequired)
1423                CheckForGlobalization(true); // the 'true' value forces re-globalization
1424       
1425        globalPackages = getPackages(); // refresh the list in memory
1426       
1427        // TODO: Refresh the packages list in the window
1428
1429        return true;
1430}
1431
1432/* MakePathLocalToGlobalize is a function used once by ProcessAEUpdate() that takes a file in an        \
1433|  update's Globalize folder and changes its path, originally relative to the Installer, to be          |
1434|  relative to the structure of the Globalize folder; this makes it easier to have the Installer        |
1435\  move said file from an update's Globalize folder to the current Globalize folder.               */
1436void MakePathLocalToGlobalize(string *installerBasedPath)
1437{
1438        int deleteToHere = 0;
1439        deleteToHere = installerBasedPath->find("Globalize/");
1440        if (deleteToHere != 0)
1441        {
1442                deleteToHere += strlen("Globalize/");
1443                installerBasedPath->erase(0, deleteToHere);
1444        }
1445}
1446
1447/* SortBySize is a custom comparison function that we call when using the C++ sort() function in \
1448\  ProcessAEUpdate() on some strings that we want to sort by length.                                                    */
1449bool SortBySize(string a, string b)
1450{
1451        return (a.size() < b.size());
1452}
1453
1454//stolen token function...
1455void tokenize(const string& str, vector<string>& tokens, const string& delimiters)
1456{
1457        // Skip delimiters at beginning.
1458        string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1459        // Find first "non-delimiter".
1460        string::size_type pos     = str.find_first_of(delimiters, lastPos);
1461       
1462        while (string::npos != pos || string::npos != lastPos)
1463        {
1464                // Found a token, add it to the vector.
1465                tokens.push_back(str.substr(lastPos, pos - lastPos));
1466                // Skip delimiters.  Note the "not_of"
1467                lastPos = str.find_first_not_of(delimiters, pos);
1468                // Find next "non-delimiter"
1469                pos = str.find_first_of(delimiters, lastPos);
1470        }
1471}
1472
1473void clearOldDats(void) {
1474        directory_iterator end_iter_gdf;
1475        for ( directory_iterator dir_itr_gdf( "../GameDataFolder" );
1476                 dir_itr_gdf != end_iter_gdf;
1477                 ++dir_itr_gdf )
1478        {
1479                if ( dir_itr_gdf->path().extension() == ".dat" || dir_itr_gdf->path().extension() == ".raw" || dir_itr_gdf->path().extension() == ".sep" ) {
1480                        remove( dir_itr_gdf->path() );
1481                }
1482               
1483        }
1484       
1485}
1486
1487// this function copies files and directories. If copying a
1488// directory to a directory, it copies recursively.
1489
1490//pardon the mess, I did this at midnight, and had to fix a bug
1491void copy( const path & from_ph, 
1492                  const path & to_ph ) 
1493{ 
1494        cout << to_ph.string() << "\n";
1495        // Make sure that the destination, if it exists, is a directory
1496        if((exists(to_ph) && !is_directory(to_ph)) || (!exists(from_ph))) cout << "error";
1497        if(!is_directory(from_ph)) 
1498        { 
1499               
1500                if(exists(to_ph)) 
1501                { 
1502                        copy_file(from_ph,to_ph/from_ph.filename()); 
1503                } 
1504                else 
1505                { 
1506                        try{
1507                               
1508                                copy_file(from_ph,to_ph);
1509                        }
1510                        catch (exception ex){
1511                                cout << from_ph.string() << " to " << to_ph.string() << "\n";
1512                        }
1513                }
1514               
1515        } 
1516        else if(from_ph.filename() != ".svn")
1517        { 
1518                path destination; 
1519                if(!exists(to_ph)) 
1520                { 
1521                        destination=to_ph; 
1522                } 
1523                else 
1524                { 
1525                        destination=to_ph/from_ph.filename(); 
1526                } 
1527               
1528                for(directory_iterator i(from_ph); i!=directory_iterator(); ++i) 
1529                { 
1530                        //the idiot who coded this in the first place (not me)
1531                        //forgot to make a new directory. Exception city. x_x
1532                        create_directory(destination); 
1533                        copy(*i,destination/i->filename()); 
1534                } 
1535        } 
1536} 
1537
1538void copy_directory( const path &from_dir_ph, 
1539                                        const path &to_dir_ph) 
1540{ 
1541        if(!exists(from_dir_ph) || !is_directory(from_dir_ph) 
1542           || exists(to_dir_ph)) 
1543                cout << !exists(from_dir_ph) << " " << !is_directory(from_dir_ph) 
1544                << " " << exists(to_dir_ph);
1545       
1546# ifdef BOOST_POSIX
1547        struct stat from_stat; 
1548        if ( (::stat( from_dir_ph.string().c_str(), &from_stat ) != 0) 
1549                || ::mkdir(to_dir_ph.native_directory_string().c_str(), 
1550                                   from_stat.st_mode)!=0) 
1551# endif
1552                } 
1553
1554string escapePath(string input)
1555{
1556        string output;
1557        string escape_me = "& ;()|<>\"'\\#*?$";
1558        for (unsigned int i = 0; i < input.size(); i++)
1559        {
1560                for (unsigned int j = 0; j < escape_me.size(); j++)
1561                        if (input[i] == escape_me[j])
1562                                output += '\\';
1563                output += input[i];
1564        }
1565        return output;
1566}
1567
1568Install_info_cfg::Install_info_cfg()
1569{
1570        AEVersion = "2009-07b";
1571        InstallerVersion = "1.0.1";
1572        DaodanVersion = "1.0";
1573        OniSplitVersion = "0.9.38.0";
1574        WinGUIVersion = "0";
1575        MacGUIVersion = "0";
1576        patch = false;
1577        globalizationRequired = false;
1578        deleteList.reserve(255);
1579}
1580
1581ModPackage::ModPackage()
1582{
1583        isInstalled = true; // replace with function
1584        name = "";
1585        modStringName = "";
1586        modStringVersion = 0;
1587        hasOnis = false;
1588        hasDeltas = false;
1589        hasBSL = false;
1590        hasAddon = false;
1591        hasDats = false;
1592        category = "";
1593        creator = "";
1594        isEngine = false;
1595        readme = "";
1596        globalNeeded = true;
1597}
1598#ifndef WIN32
1599void Sleep(int ms)
1600{
1601        sleep(ms / 1000);
1602}
1603#endif
1604#ifdef WIN32
1605
1606void RedirectIOToConsole()
1607{
1608        int hConHandle; 
1609        long lStdHandle;
1610        CONSOLE_SCREEN_BUFFER_INFO coninfo;
1611        FILE *fp;
1612       
1613        // allocate a console for this app
1614        AllocConsole();
1615       
1616        // set the screen buffer to be big enough to let us scroll text
1617        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), 
1618                                                           &coninfo);
1619        coninfo.dwSize.Y = MAX_CONSOLE_LINES;
1620        SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), 
1621                                                           coninfo.dwSize);
1622       
1623        // redirect unbuffered STDOUT to the console
1624        lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
1625        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1626        fp = _fdopen( hConHandle, "w" );
1627        *stdout = *fp;
1628        setvbuf( stdout, NULL, _IONBF, 0 );
1629       
1630        // redirect unbuffered STDIN to the console
1631        lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
1632        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1633        fp = _fdopen( hConHandle, "r" );
1634        *stdin = *fp;
1635        setvbuf( stdin, NULL, _IONBF, 0 );
1636       
1637        // redirect unbuffered STDERR to the console
1638        lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
1639        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1640        fp = _fdopen( hConHandle, "w" );
1641        *stderr = *fp;
1642        setvbuf( stderr, NULL, _IONBF, 0 );
1643       
1644        // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
1645        // point to console as well
1646        ios::sync_with_stdio();
1647}
1648#endif
Note: See TracBrowser for help on using the repository browser.