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

Last change on this file since 499 was 499, checked in by iritscen, 11 years ago

I don't fully understand the CR/LF bug, but this fixes it *shrug*.

File size: 61.5 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                StripNewlines(&line);
943                tokenize(line, tokens);
944                iter = tokens.begin();
945               
946                if (tokens.size() >= 3)
947                {
948                        if (!strAEVersion.compare(*iter))
949                        {
950                                if (!strArrow.compare(*++iter))
951                                        info_cfg->AEVersion = *++iter;
952                                else
953                                        return false;
954                        }
955                        else if (!strInstallerVersion.compare(*iter))
956                        {
957                                if (!strArrow.compare(*++iter))
958                                        info_cfg->InstallerVersion = *++iter;
959                                else
960                                        return false;
961                        }
962                        else if (!strDaodanVersion.compare(*iter))
963                        {
964                                if (!strArrow.compare(*++iter))
965                                        info_cfg->DaodanVersion = *++iter;
966                                else
967                                        return false;
968                        }
969                        else if (!strOniSplitVersion.compare(*iter))
970                        {
971                                if (!strArrow.compare(*++iter))
972                                        info_cfg->OniSplitVersion = *++iter;
973                                else
974                                        return false;
975                        }
976                        else if (!strGUIWinVersion.compare(*iter))
977                        {
978                                if (!strArrow.compare(*++iter))
979                                        info_cfg->WinGUIVersion = *++iter;
980                                else
981                                        return false;
982                        }
983                        else if (!strGUIMacVersion.compare(*iter))
984                        {
985                                if (!strArrow.compare(*++iter))
986                                        info_cfg->MacGUIVersion = *++iter;
987                                else
988                                        return false;
989                        }
990                        else if (!strReglobalize.compare(*iter))
991                        {
992                                if (!strArrow.compare(*++iter))
993                                {
994                                        if (!strYes.compare(*++iter))
995                                                info_cfg->globalizationRequired = true;
996                                }
997                                else
998                                        return false;
999                        }
1000                        else if (!strDeleteList.compare(*iter))
1001                        {
1002                                // We need to perform a totally customized parsing process on this data
1003                                if (!strArrow.compare(*++iter))
1004                                {
1005                                        vector<string> tokens2;
1006                                        tokenize(line, tokens2, ","); // the paths on this line are comma-delimited, so we parse it again
1007                                        vector<string>::iterator iter2 = tokens2.begin();
1008                                        string finalPath = "";
1009                                        for (; iter2 != tokens2.end(); iter2++)
1010                                        {
1011                                                finalPath = finalPath + *iter2;
1012                                               
1013                                                string::size_type loc = finalPath.find("->", 0); // the first word will have "Delete_List ->" at the front, so let's cut that off
1014                                                if (loc != string::npos)
1015                                                        finalPath = finalPath.substr(loc + 3, finalPath.size());
1016                                               
1017                                                // If a path has '//' in it, it must contain some optional comments that were at the end of the Delete_List line
1018                                                loc = finalPath.find("//", 0);
1019                                                if (loc != string::npos)
1020                                                        finalPath = finalPath.substr(0, loc);
1021                                               
1022                                                // Trim a single space if it exists at the start or finish; putting more than one space after a comma will break this
1023                                                if (finalPath.at(0) == ' ')
1024                                                        finalPath = finalPath.substr(1, finalPath.size());
1025                                                if (finalPath.at(finalPath.size() - 1) == ' ')
1026                                                        finalPath = finalPath.substr(0, finalPath.size() - 1);
1027
1028                                                // If the tokenized path ends with a '\', then we assume it was followed by a comma
1029                                                if (finalPath.at(finalPath.size() - 1) == '\\')
1030                                                {
1031                                                        finalPath = finalPath.substr(0, finalPath.size() - 1); // clip the '\' off the end of the string now that it served its purpose...
1032                                                        finalPath = finalPath + ","; // ...and add the actual comma back at the end
1033                                                }
1034                                                else // we can add the path to deleteList, and clear the path; otherwise it will be added to on the next iteration
1035                                                {
1036                                                        if (StringIsLegalPathForDeletion(finalPath)) // ...and it's not violating any of our security rules as to what can be deleted...
1037                                                                info_cfg->deleteList.push_back(finalPath); // ...then add it to our deleteList in memory
1038                                                        finalPath.clear(); // clear the token we were building up before the next pass
1039                                                }
1040                                        }
1041                                }
1042                                else
1043                                        return false;
1044                        }
1045                }
1046                tokens.clear();
1047        }
1048       
1049        return true;
1050}
1051
1052// TODO: Fix security holes here
1053/* There is currently a security hole in this function; the first occurrence of a '.' not followed by a second '.' will prevent the function from
1054 noticing an actual occurrence of '..' later in the string; iow, it only looks after the first period it finds for a second period.
1055 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
1056 possibly be interpreted by the Boost file functions as "the current directory". Iow, both of these checks need to be iterative, not one-time.
1057 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 */
1058
1059/* This function serves as a barrier against the Installer deleting files it shouldn't. *\
1060|  It tests for each of the following conditions in the path it is passed:                               |
1061|   A. '..' as the whole path or '/..', '\..' anywhere in the path                                               |
1062|        Reason: Moving up from the parent directory, the Edition folder, would allow one        |
1063|        to delete anything on the hard drive, so all "parent path" references are illegal.      |
1064|   B. '/' at the beginning of the path                                                                                                  |
1065|        Reason: In Unix, this means the path starts from root level, as opposed to the          |
1066|        directory we will evaluate these paths as being relative to, which is Edition/.         |
1067|   C. '.' as the whole path                                                                                                                     |
1068|        Reason: This would mean "the Edition folder", which is not allowed to be deleted.       |
1069|   D. 'GameDataFolder' at the end of the path                                                                                   |
1070|        Reason: We don't allow the entire GDF to be deleted, only specific files in it.         |
1071|   E. '*' anywhere in the path                                                                                                                  |
1072|        Reason: We don't want this interpreted as a wildcard; it's best to only delete          |
1073*\       files by name.                                                                                                                                         */
1074bool StringIsLegalPathForDeletion(string word)
1075{
1076        string::size_type loc1, loc2;
1077       
1078        // Trim ending slashes in order to simplify the test
1079        // Note that we're only altering the local copy of the string here
1080        loc1 = word.find_last_of("\\", word.size());
1081        if (loc1 == word.size() - 1)
1082                word.resize(word.size() - 1);
1083        loc1 = word.find_last_of("/", word.size());
1084        if (loc1 == word.size() - 1)
1085                word.resize(word.size() - 1);
1086       
1087        // Test B
1088        loc1 = word.find_first_of("\\", 0);
1089        if (loc1 == 0)
1090                return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1091        loc1 = word.find_first_of("/", 0);
1092        if (loc1 == 0)
1093                return false; // path begins with a slash, meaning root level of HD in Unix, an illegal path
1094       
1095        // Test E
1096        loc1 = word.find("*", 0);
1097        if (loc1 != string::npos) // if we found our character before reaching the end of the string
1098                return false; // path cannot contain the '*' character
1099       
1100        // Tests A (part 1) and C
1101        loc1 = word.find(".", 0);
1102        if (loc1 != string::npos)
1103        {
1104                if (word.size() == 1)
1105                        return false; // path cannot be simply '.', referring to Edition folder itself
1106                loc2 = word.find(".", loc1 + 1);
1107                if (loc2 == loc1 + 1) // make sure this second period comes after the first one
1108                        if (word.size() == 2)
1109                                return false; // not allowed to reference a parent directory
1110        }
1111       
1112        // Test A (part 2)
1113        loc1 = word.find("/..", 0);
1114        if (loc1 != string::npos)
1115                return false; // not allowed to reference a parent directory
1116        loc1 = word.find("\\..", 0);
1117        if (loc1 != string::npos)
1118                return false; // not allowed to reference a parent directory
1119       
1120        // Test D
1121        loc1 = word.find("GameDataFolder", 0);
1122        if (loc1 == word.size() - 14) // if "GameDataFolder" is the last 14 characters of the string...
1123                return false; // not allowed to delete the GDF
1124
1125        return true;
1126}
1127
1128bool ProcessInstallerUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE)
1129{
1130        ofstream file;
1131        string shellScript;
1132       
1133        ptime startTime(second_clock::local_time());
1134        string strStartTime = to_simple_string(startTime);
1135        string progressMsg = "Installer being updated to:\n" +
1136                                                 updateAE->InstallerVersion + " on " + strStartTime;
1137        file.open("Update.log");
1138        if (!file.fail())
1139                file << progressMsg.c_str();
1140        file.close();
1141        file.clear();
1142       
1143        string popenCommand = "../updates/" + strEUFN + "/install/";
1144#ifdef WIN32
1145        // TODO: Fill in Windows equivalent of code below :-3
1146        popenCommand = "replace_installer.bat";
1147#else
1148        // We can't just use '~' to mean "the home directory" because we need to check the path in C...
1149        // ...so we actually get the current user's shortname and manually construct the path to home
1150        FILE *fUserName = NULL;
1151        char chrUserName[32];
1152        fUserName = popen("whoami", "r");
1153        fgets(chrUserName, sizeof(chrUserName), fUserName);
1154        pclose(fUserName);
1155        string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1156        int endOfName = strUserName.find("\n", 0);
1157        string pathToTrash = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1158        tm tmStartTime = to_tm(startTime);
1159        pathToTrash = pathToTrash + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1160        boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec); // lol
1161        create_directory(pathToTrash);
1162        // The script takes as a parameter the path the old Installer should go to, in quotes
1163        popenCommand = "bash " + popenCommand + "replace_installer.sh " + pathToTrash + "/Installer.app";
1164                                   
1165#endif
1166        file.close();
1167        file.clear();
1168#ifdef WIN32
1169        system(popenCommand.c_str());
1170#else
1171        popen(popenCommand.c_str(), "r");
1172#endif
1173        return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running
1174}
1175
1176bool ProcessAEUpdate(Install_info_cfg *currentAE, Install_info_cfg *updateAE, bool *installerJustUpdated)
1177{
1178        fstream file;
1179        string line;
1180        vector<string> tokens, updateStarted;
1181        string strInstaller = "Installer";
1182        string strWas = "was";
1183        string strPathToEUFN = ("../updates/" + strEUFN + "/"); // strEUFN is set by GetUpdateStatus()
1184        string strPathToEUFNInstall = ("../updates/" + strEUFN + "/install/");
1185        string strPathToEUFNPackages = ("../updates/" + strEUFN + "/install/packages/");
1186        string strPathToPackages = "packages/";
1187        string strGlobalize = "Globalize/";
1188        string strOniSplit = "OniSplit.exe";
1189        string strDaodan = "binkw32.dll";
1190        string strWinGUI = "onisplit_gui.exe";
1191        string strWinGUILang = "ospgui_lang.ini";
1192        string strMacGUI = "AETools.app";
1193#ifdef WIN32
1194        string strOniApp = "Oni.exe";
1195#else
1196        string strOniApp = "Oni.app";
1197#endif
1198        bool needNewTrashDir = false;
1199        bool readingVerAndDate = false;
1200       
1201        // TODO: Fill in Windows equivalent of code below
1202#ifdef WIN32
1203        string strTrashDir = "Trash\\";
1204#else
1205        FILE *fUserName = NULL;
1206        char chrUserName[32];
1207        fUserName = popen("whoami", "r");
1208        fgets(chrUserName, sizeof(chrUserName), fUserName);
1209        pclose(fUserName);
1210        string strUserName = (string)chrUserName; // stringsblaaarrrgggghhhh
1211        int endOfName = strUserName.find("\n", 0);
1212        string strTrashDir = "/Users/" + strUserName.substr(0, endOfName) + "/.Trash/";
1213#endif
1214       
1215        // Write to log that we are beginning the update process
1216        ptime startTime(second_clock::local_time());
1217        string strStartTime = to_simple_string(startTime);
1218        string progressMsg = "\nEdition being updated to:\n" +
1219                                                 updateAE->AEVersion + " on " + strStartTime;
1220        file.open("Update.log");
1221        if (!file.fail())
1222                file << progressMsg.c_str();
1223       
1224        if (*installerJustUpdated) // then we want to know what folder in the Trash the Installer was placed in...
1225        {
1226                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
1227                {
1228                        getline(file, line);
1229                        tokenize(line, tokens);
1230                       
1231                        if (tokens.capacity() >= 4)
1232                                if (!strInstaller.compare(tokens[0]))
1233                                        if (!strWas.compare(tokens[1]))
1234                                                readingVerAndDate = true;
1235                        if (readingVerAndDate && tokens.capacity() >= 3)
1236                                tokenize(tokens[2], updateStarted, "-");
1237                }
1238                if (updateStarted.capacity() < 3)
1239                        needNewTrashDir = true;
1240                else
1241                {
1242                        strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "-" +
1243                                                  updateStarted[0] + "-" + updateStarted[1] + "-" + updateStarted[2] + "/";
1244                        if (!exists(strTrashDir))
1245                                needNewTrashDir = true;
1246                }
1247        }
1248#ifndef WIN32
1249        if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to
1250        {
1251                tm tmStartTime = to_tm(startTime);
1252                strTrashDir = strTrashDir + "Old_Edition_files_" + currentAE->AEVersion + "_" + boost::lexical_cast<string>(tmStartTime.tm_hour) + "-" +
1253                                          boost::lexical_cast<string>(tmStartTime.tm_min) + "-" + boost::lexical_cast<string>(tmStartTime.tm_sec) + "/";
1254                create_directory(strTrashDir);
1255        }
1256#endif
1257        file.close();
1258        file.clear();
1259
1260        // Special code to replace our special files -- the Oni app, OniSplit, the Daodan DLL, and the GUI for OniSplit
1261        if (exists(strPathToEUFN + strOniApp))
1262        {
1263                if (exists(strOniApp))
1264                        rename((path)strOniApp, (path)(strTrashDir + strOniApp));
1265                rename((path)(strPathToEUFN + strOniApp), (path)strOniApp);
1266        }
1267        if (updateAE->OniSplitVersion.compare(currentAE->OniSplitVersion) >= 1)
1268        {
1269                if (exists(strPathToEUFNInstall + strOniSplit))
1270                {
1271                        if (exists(strOniSplit))
1272                                rename((path)strOniSplit, (path)(strTrashDir + strOniSplit));
1273                        rename((path)(strPathToEUFNInstall + strOniSplit), (path)strOniSplit);
1274                }
1275        }
1276#ifdef WIN32
1277        if (updateAE->DaodanVersion.compare(currentAE->DaodanVersion) >= 1)
1278        {
1279                if (exists(strPathToEUFN + strDaodan))
1280                {
1281                        if (exists(("../" + strDaodan)))
1282                                rename((path)("../" + strDaodan), (path)(strTrashDir + strDaodan));
1283                        rename((path)(strPathToEUFN + strDaodan), (path)("../" + strDaodan));
1284                }
1285        }
1286        if (updateAE->WinGUIVersion.compare(currentAE->WinGUIVersion) >= 1)
1287        {
1288                if (exists(strPathToEUFNInstall + strWinGUI))
1289                {
1290                        if (exists((path)strWinGUI))
1291                                rename((path)strWinGUI, (path)(strTrashDir + strWinGUI));
1292                        if (exists(strWinGUILang))
1293                                rename((path)strWinGUILang, (path)(strTrashDir + strWinGUILang));
1294                        rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI);
1295                        rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang);
1296                }
1297        }
1298#else
1299        if (updateAE->MacGUIVersion.compare(currentAE->MacGUIVersion) >= 1)
1300        {
1301                if (exists(strPathToEUFN + strMacGUI))
1302                {
1303                        if (exists(("../" + strMacGUI)))
1304                                rename((path)("../" + strMacGUI), (path)(strTrashDir + strMacGUI));
1305                        rename((path)(strPathToEUFN + strMacGUI), (path)("../" + strMacGUI));
1306                }
1307        }
1308#endif
1309       
1310        // Now we trash whatever's in DeleteList; this allows us to clear out obsolete files in the previous AE install
1311        // Before moving a file to the Trash, we need to make sure each of the file's parent paths exists in the Trash...
1312        // ...so we iterate through the hierarchy of the file path, checking for each one and creating it if necessary
1313        for (vector<string>::iterator iter = updateAE->deleteList.begin(); iter != updateAE->deleteList.end(); iter++)
1314        {
1315                string thePath = *iter;
1316                if (exists((path)("../" + thePath)))
1317                {
1318                        string aParentPath;
1319                        string::size_type curPos = thePath.find("/", 0);
1320                        if (curPos != string::npos)
1321                                aParentPath = thePath.substr(0, curPos);
1322                        string::size_type lastPos = curPos;
1323                        while (curPos != string::npos && curPos < thePath.size())
1324                        {
1325                                aParentPath = aParentPath + thePath.substr(lastPos, curPos - lastPos);
1326                                if (!exists(strTrashDir + aParentPath))
1327                                        create_directory(strTrashDir + aParentPath);
1328                                lastPos = curPos + 1;
1329                                curPos = thePath.find("/", lastPos);
1330                                aParentPath = aParentPath + "/";
1331                        }
1332#ifndef WIN32
1333                        rename((path)("../" + thePath), (path)(strTrashDir + thePath));
1334#else
1335                        remove((path)("../" + thePath));
1336#endif
1337                }
1338        }
1339       
1340        // Now we crawl the update's package folders for newer versions and move them over, trashing ones that are already present
1341        vector<ModPackage> updatePackages, currentPackages;
1342        bool matchFound;
1343        updatePackages.reserve(256);
1344        currentPackages.reserve(256);
1345       
1346        currentPackages = getPackages();
1347        updatePackages = getPackages(strPathToEUFNPackages);
1348
1349        for (vector<ModPackage>::iterator iter1 = updatePackages.begin(); iter1 != updatePackages.end(); iter1++)
1350        {
1351                matchFound = false;
1352                for (vector<ModPackage>::iterator iter2 = currentPackages.begin(); iter2 != currentPackages.end(); iter2++)
1353                {
1354                        if (!iter1->modStringName.compare(iter2->modStringName))
1355                        {
1356                                matchFound = true;
1357                                if (iter1->modStringVersion > iter2->modStringVersion)
1358                                {
1359#ifndef WIN32
1360                                        rename((path)(strPathToPackages + iter2->modStringName), (path)(strTrashDir + iter2->modStringName));
1361#else
1362                                        remove((path)(strPathToPackages + iter2->modStringName));
1363#endif
1364                                        rename((path)(strPathToEUFNPackages + iter1->modStringName), (path)(strPathToPackages + iter1->modStringName));
1365                                }
1366                        }
1367                }
1368                if (!matchFound) // then there's no old package in the way, so just move in the one from the update
1369                        rename((path)(strPathToEUFNPackages + iter1->modStringName), (path)(strPathToPackages + iter1->modStringName));
1370        }
1371       
1372        // 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...
1373        // ...and folders which do not exist in the current AE will be created there
1374        vector<string> foldersToMake, filesToMove;
1375        string thePath;
1376        for (recursive_directory_iterator dir_itr(strPathToEUFNPackages + strGlobalize), end_itr; dir_itr != end_itr; ++dir_itr)
1377        {
1378                thePath = dir_itr->path().string();
1379                MakePathLocalToGlobalize(&thePath);
1380                if (is_regular_file(dir_itr->status()))
1381                {
1382                        if (dir_itr->filename().at(0) != '.') // skip over dot-files, which are invisible in Unix
1383                                filesToMove.push_back(thePath);
1384                }
1385                else if (is_directory(dir_itr->status()))
1386                {
1387                        if (!exists(strPathToPackages + strGlobalize + thePath))
1388                                foldersToMake.push_back(thePath);
1389                }
1390        }
1391        // Sort the foldersToMake strings by length, which is a fast solution to the problem of "How do we make sure we create folder 'parent/'...
1392        // ...before folder 'parent/child/'"?
1393        sort(foldersToMake.begin(), foldersToMake.end(), SortBySize); // SortBySize is a custom comparison function found later in this source file
1394        // First make the folders that don't exist in the current AE, so all the files have a place to go
1395        for (vector<string>::iterator iter = foldersToMake.begin(); iter != foldersToMake.end(); iter++)
1396        {
1397                create_directory(strPathToPackages + strGlobalize + *iter);
1398        }
1399        for (vector<string>::iterator iter = filesToMove.begin(); iter != filesToMove.end(); iter++)
1400        {
1401                if (exists(strPathToPackages + strGlobalize + *iter))
1402                        rename((path)(strPathToPackages + strGlobalize + *iter), (path)(strTrashDir + *iter));
1403                rename((path)(strPathToEUFNPackages + strGlobalize + *iter), (path)(strPathToPackages + strGlobalize + *iter));
1404        }
1405       
1406        // Clean up after ourselves, trashing any packages or programs in the update package that are not newer than the current AE
1407        create_directory(strTrashDir + "Unneeded update files");
1408        rename((path)strPathToEUFN, (path)(strTrashDir + "Unneeded update files/" + strEUFN));
1409       
1410        // Write to log that we are finished with update
1411        ptime end_time(second_clock::local_time());
1412        string progressMsg2 = "Edition was updated to:\n" +
1413                                                  updateAE->AEVersion + " on " + to_simple_string(end_time);
1414       
1415        file.open("Update.log");
1416       
1417        if(!file.fail())
1418                file.write(progressMsg2.c_str(), sizeof(progressMsg2.c_str()));
1419       
1420        file.close();
1421        file.clear();
1422       
1423        if (updateAE->globalizationRequired)
1424                CheckForGlobalization(true); // the 'true' value forces re-globalization
1425       
1426        globalPackages = getPackages(); // refresh the list in memory
1427       
1428        // TODO: Refresh the packages list in the window
1429
1430        return true;
1431}
1432
1433/* MakePathLocalToGlobalize is a function used once by ProcessAEUpdate() that takes a file in an        \
1434|  update's Globalize folder and changes its path, originally relative to the Installer, to be          |
1435|  relative to the structure of the Globalize folder; this makes it easier to have the Installer        |
1436\  move said file from an update's Globalize folder to the current Globalize folder.               */
1437void MakePathLocalToGlobalize(string *installerBasedPath)
1438{
1439        int deleteToHere = 0;
1440        deleteToHere = installerBasedPath->find("Globalize/");
1441        if (deleteToHere != 0)
1442        {
1443                deleteToHere += strlen("Globalize/");
1444                installerBasedPath->erase(0, deleteToHere);
1445        }
1446}
1447
1448/* SortBySize is a custom comparison function that we call when using the C++ sort() function in \
1449\  ProcessAEUpdate() on some strings that we want to sort by length.                                                    */
1450bool SortBySize(string a, string b)
1451{
1452        return (a.size() < b.size());
1453}
1454
1455//stolen token function...
1456void tokenize(const string& str, vector<string>& tokens, const string& delimiters)
1457{
1458        // Skip delimiters at beginning.
1459        string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1460        // Find first "non-delimiter".
1461        string::size_type pos     = str.find_first_of(delimiters, lastPos);
1462       
1463        while (string::npos != pos || string::npos != lastPos)
1464        {
1465                // Found a token, add it to the vector.
1466                tokens.push_back(str.substr(lastPos, pos - lastPos));
1467                // Skip delimiters.  Note the "not_of"
1468                lastPos = str.find_first_not_of(delimiters, pos);
1469                // Find next "non-delimiter"
1470                pos = str.find_first_of(delimiters, lastPos);
1471        }
1472}
1473
1474/* StripNewlines() gets rids of any linebreaks that come from text returned by getline(); \
1475|  getline() should be stripping those out, but Windows CR/LF files seem to be sneaking   |
1476\  some extra return characters into strings in the ReadInstallInfoCfg() function.               */
1477void StripNewlines(string *theLine)
1478{
1479        int deleteFromHere = 0;
1480        deleteFromHere = theLine->find("\r");
1481        if (deleteFromHere > 0)
1482                theLine->erase(deleteFromHere, theLine->size());
1483}
1484
1485void clearOldDats(void) {
1486        directory_iterator end_iter_gdf;
1487        for ( directory_iterator dir_itr_gdf( "../GameDataFolder" );
1488                 dir_itr_gdf != end_iter_gdf;
1489                 ++dir_itr_gdf )
1490        {
1491                if ( dir_itr_gdf->path().extension() == ".dat" || dir_itr_gdf->path().extension() == ".raw" || dir_itr_gdf->path().extension() == ".sep" ) {
1492                        remove( dir_itr_gdf->path() );
1493                }
1494               
1495        }
1496       
1497}
1498
1499// this function copies files and directories. If copying a
1500// directory to a directory, it copies recursively.
1501
1502//pardon the mess, I did this at midnight, and had to fix a bug
1503void copy( const path & from_ph, 
1504                  const path & to_ph ) 
1505{ 
1506        cout << to_ph.string() << "\n";
1507        // Make sure that the destination, if it exists, is a directory
1508        if((exists(to_ph) && !is_directory(to_ph)) || (!exists(from_ph))) cout << "error";
1509        if(!is_directory(from_ph)) 
1510        { 
1511               
1512                if(exists(to_ph)) 
1513                { 
1514                        copy_file(from_ph,to_ph/from_ph.filename()); 
1515                } 
1516                else 
1517                { 
1518                        try{
1519                               
1520                                copy_file(from_ph,to_ph);
1521                        }
1522                        catch (exception ex){
1523                                cout << from_ph.string() << " to " << to_ph.string() << "\n";
1524                        }
1525                }
1526               
1527        } 
1528        else if(from_ph.filename() != ".svn")
1529        { 
1530                path destination; 
1531                if(!exists(to_ph)) 
1532                { 
1533                        destination=to_ph; 
1534                } 
1535                else 
1536                { 
1537                        destination=to_ph/from_ph.filename(); 
1538                } 
1539               
1540                for(directory_iterator i(from_ph); i!=directory_iterator(); ++i) 
1541                { 
1542                        //the idiot who coded this in the first place (not me)
1543                        //forgot to make a new directory. Exception city. x_x
1544                        create_directory(destination); 
1545                        copy(*i,destination/i->filename()); 
1546                } 
1547        } 
1548} 
1549
1550void copy_directory( const path &from_dir_ph, 
1551                                        const path &to_dir_ph) 
1552{ 
1553        if(!exists(from_dir_ph) || !is_directory(from_dir_ph) 
1554           || exists(to_dir_ph)) 
1555                cout << !exists(from_dir_ph) << " " << !is_directory(from_dir_ph) 
1556                << " " << exists(to_dir_ph);
1557       
1558# ifdef BOOST_POSIX
1559        struct stat from_stat; 
1560        if ( (::stat( from_dir_ph.string().c_str(), &from_stat ) != 0) 
1561                || ::mkdir(to_dir_ph.native_directory_string().c_str(), 
1562                                   from_stat.st_mode)!=0) 
1563# endif
1564                } 
1565
1566string escapePath(string input)
1567{
1568        string output;
1569        string escape_me = "& ;()|<>\"'\\#*?$";
1570        for (unsigned int i = 0; i < input.size(); i++)
1571        {
1572                for (unsigned int j = 0; j < escape_me.size(); j++)
1573                        if (input[i] == escape_me[j])
1574                                output += '\\';
1575                output += input[i];
1576        }
1577        return output;
1578}
1579
1580Install_info_cfg::Install_info_cfg()
1581{
1582        AEVersion = "2009-07b";
1583        InstallerVersion = "1.0.1";
1584        DaodanVersion = "1.0";
1585        OniSplitVersion = "0.9.38.0";
1586        WinGUIVersion = "0";
1587        MacGUIVersion = "0";
1588        patch = false;
1589        globalizationRequired = false;
1590        deleteList.reserve(255);
1591}
1592
1593ModPackage::ModPackage()
1594{
1595        isInstalled = true; // replace with function
1596        name = "";
1597        modStringName = "";
1598        modStringVersion = 0;
1599        hasOnis = false;
1600        hasDeltas = false;
1601        hasBSL = false;
1602        hasAddon = false;
1603        hasDats = false;
1604        category = "";
1605        creator = "";
1606        isEngine = false;
1607        readme = "";
1608        globalNeeded = true;
1609}
1610#ifndef WIN32
1611void Sleep(int ms)
1612{
1613        sleep(ms / 1000);
1614}
1615#endif
1616#ifdef WIN32
1617
1618void RedirectIOToConsole()
1619{
1620        int hConHandle; 
1621        long lStdHandle;
1622        CONSOLE_SCREEN_BUFFER_INFO coninfo;
1623        FILE *fp;
1624       
1625        // allocate a console for this app
1626        AllocConsole();
1627       
1628        // set the screen buffer to be big enough to let us scroll text
1629        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), 
1630                                                           &coninfo);
1631        coninfo.dwSize.Y = MAX_CONSOLE_LINES;
1632        SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), 
1633                                                           coninfo.dwSize);
1634       
1635        // redirect unbuffered STDOUT to the console
1636        lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
1637        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1638        fp = _fdopen( hConHandle, "w" );
1639        *stdout = *fp;
1640        setvbuf( stdout, NULL, _IONBF, 0 );
1641       
1642        // redirect unbuffered STDIN to the console
1643        lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
1644        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1645        fp = _fdopen( hConHandle, "r" );
1646        *stdin = *fp;
1647        setvbuf( stdin, NULL, _IONBF, 0 );
1648       
1649        // redirect unbuffered STDERR to the console
1650        lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
1651        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
1652        fp = _fdopen( hConHandle, "w" );
1653        *stderr = *fp;
1654        setvbuf( stderr, NULL, _IONBF, 0 );
1655       
1656        // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
1657        // point to console as well
1658        ios::sync_with_stdio();
1659}
1660#endif
Note: See TracBrowser for help on using the repository browser.