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

Last change on this file since 505 was 505, checked in by gumby, 15 years ago

More improvements.

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