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

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

Added optional OS flag, "Platform".

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