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

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

Changed 'Installer replacement failed' message so user can choose not to quit.
Fixed BSL mod package-detecting bug and other dubious flag-detecting code.
Added check for required Installer version when scanning package info files.

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