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

Last change on this file since 487 was 487, checked in by iritscen, 13 years ago

Adding update feature; moving/neatening some code; adding globals.h

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