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

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

Allowed single packages to be updated
Changed the modversion to be a float
Fixed a possible crash on Macs
Fixed batch file to move instead of copy

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