--- AE/Installer/trunk/source/installer.cpp 2009/12/30 01:51:38 487 +++ AE/Installer/trunk/source/installer.cpp 2010/03/14 23:31:14 500 @@ -9,8 +9,11 @@ // TODO: Load credits from text resource file // TODO: Clear mod info fields when mod is de-selected -#define DEBUG - +//#define DEBUG +#ifdef WIN32 +//#include +#define popen _popen +#endif #include "boost/date_time/gregorian/gregorian.hpp" #include "boost/date_time/date_parsing.hpp" #include "boost/date_time/posix_time/posix_time.hpp" @@ -299,9 +302,9 @@ int globalizeData(void) /* 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). 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 run Oni before :-p */ - string fullAEpath = escapePath(system_complete(".").parent_path().parent_path().string()); // get full path for edition/ + string fullAEpath = escapePath(system_complete(".").parent_path().parent_path().string()); // get full path for Edition/ (Oni wants the folder that *contains* the GDF) char prefsCommand[300] = "[ -f ~/Library/Preferences/com.godgames.oni.plist ] && defaults write com.godgames.oni RetailInstallationPath -string '"; - strcat(prefsCommand, fullAEpath.c_str()); // get path of Edition/ folder (Oni wants the folder that *contains* the GDF) + strcat(prefsCommand, fullAEpath.c_str()); strcat(prefsCommand, "'"); // path string is enclosed in single quotes to avoid the need to escape UNIX-unfriendly characters system(prefsCommand); #endif @@ -323,6 +326,7 @@ int globalizeData(void) vector getPackages(string packageDir) { vector packages; + ModPackage package; packages.reserve(256); fstream file; string filename = "\0"; @@ -334,10 +338,11 @@ vector getPackages(string pa { file.open((dir_itr->path().string() + "/" + MODINFO_CFG).c_str()); - if(!file.fail()) + if (!file.fail()) { - //would prefer to push a pointer to a package, but this will do for now - packages.push_back(fileToModPackage(file)); + package = fileToModPackage(file); + if (package.installerVersion.compare(INSTALLER_VERSION) < 1) // if mod requires newer version of the Installer, we won't add it to the list + packages.push_back(package); } file.close(); file.clear(); @@ -364,10 +369,10 @@ ModPackage fileToModPackage(fstream &fil */ ModPackage package; string line; - static string NameOfMod = "NameOfMod"; //used for comparing to the current token... - //I could have done it in reverse (*iter).compare("ModString") or - static string ARROW = "->"; //did something like "ModString".compare(*iter), and it would have been - static string ModString = "ModString"; //functionably the same. + static string AEInstallVersion = "AEInstallVersion"; // used for comparing to the current token... + static string NameOfMod = "NameOfMod"; + static string ARROW = "->"; + static string ModString = "ModString"; static string HasOnis = "HasOnis"; static string HasDeltas = "HasDeltas"; static string HasBSL = "HasBSL"; @@ -377,81 +382,96 @@ ModPackage fileToModPackage(fstream &fil static string GlobalNeeded = "GlobalNeeded"; static string Category = "Category"; static string Creator = "Creator"; - while (! file.eof() ) + while (!file.eof()) { - getline (file,line); + getline(file,line); vector tokens; vector::iterator iter; - tokenize(line, tokens); //string to vector of "words" - if (tokens.capacity() >= 3) { //make sure they are using enough stuff - iter = tokens.begin(); //what word we are on, starts at first word - /* TODO: Get this "required Installer version" code working - if (!AEInstallVersion.compare(*iter)) - If mod is too old, skip this mod. - */ - /*else*/if (!NameOfMod.compare(*iter)) { //if it contains the name - for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) { // iterates through the words, ends if it reaches the end of the line or a "//" comment - if (ARROW.compare(*iter) && NameOfMod.compare(*iter)) { // ignores "->" and "NameOfMod" + tokenize(line, tokens); + if (tokens.capacity() >= 3) + { + iter = tokens.begin(); + + if (!AEInstallVersion.compare(*iter)) + { + iter++; iter++; + package.installerVersion = *iter; + } + else if (!NameOfMod.compare(*iter)) + { + for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) // iterates through the words, ends if it reaches the end of the line or a "//" comment + { + if (ARROW.compare(*iter) && NameOfMod.compare(*iter)) // ignores "->" and "NameOfMod" package.name += *iter + " "; - } } - } - else if (!ModString.compare(*iter)) { + else if (!ModString.compare(*iter)) + { iter++; iter++; package.modStringName = *iter; iter++; package.modStringVersion = atoi((*iter).c_str()); } - else if (!HasOnis.compare(*iter)) { + else if (!HasOnis.compare(*iter)) + { iter++; iter++; - if ( boost::iequals(*iter, "Yes")) package.hasOnis = 1; + if (boost::iequals(*iter, "Yes")) package.hasOnis = 1; } - else if (!HasBSL.compare(*iter)) { - if(toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasBSL = true; - else if ( boost::iequals(*iter, "Addon")) package.hasAddon = true; + else if (!HasBSL.compare(*iter)) + { + iter++; iter++; + if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasBSL = true; + else if (boost::iequals(*iter, "Addon")) package.hasAddon = true; } - else if (!HasDeltas.compare(*iter)) { + else if (!HasDeltas.compare(*iter)) + { iter++; iter++; - if (toupper((*iter)[0]) + toupper((*iter)[1]) + toupper((*iter)[2]) == 'Y' + 'E' + 'S') package.hasDeltas = 1; + if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDeltas = 1; } - else if (!HasDats.compare(*iter)) { + else if (!HasDats.compare(*iter)) + { iter++; iter++; - if (toupper((*iter)[0]) + toupper((*iter)[1]) + toupper((*iter)[2]) == 'Y' + 'E' + 'S') package.hasDats = 1; + if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.hasDats = 1; } - else if (!IsEngine.compare(*iter)) { + else if (!IsEngine.compare(*iter)) + { iter++; iter++; - if (toupper((*iter)[0]) + toupper((*iter)[1]) + toupper((*iter)[2]) == 'Y' + 'E' + 'S') package.isEngine = 1; + if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.isEngine = 1; } - else if (!GlobalNeeded.compare(*iter)) { + else if (!GlobalNeeded.compare(*iter)) + { iter++; iter++; - if (toupper((*iter)[0]) + toupper((*iter)[1]) + toupper((*iter)[2]) == 'Y' + 'E' + 'S') package.globalNeeded = 1; - else if (toupper((*iter)[0]) + toupper((*iter)[1]) == 'N' + 'O') package.globalNeeded = 1; // only place where checking for "No" is important atm. + if (toupper((*iter)[0]) == 'Y' && toupper((*iter)[1]) == 'E' && toupper((*iter)[2]) == 'S') package.globalNeeded = 1; + else if (toupper((*iter)[0]) == 'N' && toupper((*iter)[1]) == 'O') package.globalNeeded = 1; // only place where checking for "No" is important atm } - else if (!Category.compare(*iter)) { - for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) { // iterates through the words, ends if it reaches end of line or a "//" comment - if (ARROW.compare(*iter) && Category.compare(*iter)) { // ignores "->" and "Category" + else if (!Category.compare(*iter)) + { + for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) + { + if (ARROW.compare(*iter) && Category.compare(*iter)) // ignores "->" and "Category" package.category += *iter + " "; - } } } - else if (!Creator.compare(*iter)) { //if it contains the name - for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) { // iterates through the words, ends if it reaches end of line or a "//" comment - if (ARROW.compare(*iter) && Creator.compare(*iter)) { // ignores "->" and "Creator" + else if (!Creator.compare(*iter)) + { + for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) + { + if (ARROW.compare(*iter) && Creator.compare(*iter)) // ignores "->" and "Creator" package.creator += *iter + " "; - } } } - else if (!Readme.compare(*iter)) { //if it contains the name - for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) { // iterates through the words, ends if it reaches end of line or a "//" comment - if (ARROW.compare(*iter) && Readme.compare(*iter)) { // ignores "->" and "Readme" - if(!(*iter).compare("\\n")) package.readme += '\n'; + else if (!Readme.compare(*iter)) + { + for ( ; iter !=tokens.end() && SLASHSLASH.compare(*iter); iter++) + { + if (ARROW.compare(*iter) && Readme.compare(*iter)) // ignores "->" and "Readme" + { + if (!(*iter).compare("\\n")) package.readme += '\n'; else package.readme += *iter + " "; } } } } - } return package; @@ -936,6 +956,7 @@ bool ReadInstallInfoCfg(fstream *fileHan while (getline(*fileHandler, line)) { + StripNewlines(&line); tokenize(line, tokens); iter = tokens.begin(); @@ -1139,6 +1160,7 @@ bool ProcessInstallerUpdate(Install_info string popenCommand = "../updates/" + strEUFN + "/install/"; #ifdef WIN32 // TODO: Fill in Windows equivalent of code below :-3 + popenCommand = "replace_installer.bat"; #else // We can't just use '~' to mean "the home directory" because we need to check the path in C... // ...so we actually get the current user's shortname and manually construct the path to home @@ -1160,8 +1182,11 @@ bool ProcessInstallerUpdate(Install_info #endif file.close(); file.clear(); +#ifdef WIN32 + system(popenCommand.c_str()); +#else popen(popenCommand.c_str(), "r"); - +#endif return true; // returning 'true' tells the Installer to quit itself ASAP so it can be replaced by the process that is now running } @@ -1192,7 +1217,7 @@ bool ProcessAEUpdate(Install_info_cfg *c // TODO: Fill in Windows equivalent of code below #ifdef WIN32 - string strTrashDir = "%RECYCLE%"; + string strTrashDir = "Trash\\"; #else FILE *fUserName = NULL; char chrUserName[32]; @@ -1237,7 +1262,7 @@ bool ProcessAEUpdate(Install_info_cfg *c needNewTrashDir = true; } } - +#ifndef WIN32 if (!*installerJustUpdated || needNewTrashDir) // prepare a new directory for deleted files to go to { tm tmStartTime = to_tm(startTime); @@ -1245,6 +1270,7 @@ bool ProcessAEUpdate(Install_info_cfg *c boost::lexical_cast(tmStartTime.tm_min) + "-" + boost::lexical_cast(tmStartTime.tm_sec) + "/"; create_directory(strTrashDir); } +#endif file.close(); file.clear(); @@ -1279,9 +1305,9 @@ bool ProcessAEUpdate(Install_info_cfg *c if (exists(strPathToEUFNInstall + strWinGUI)) { if (exists((path)strWinGUI)) - rename((path)strWinGUI, (path)strcat(strTrashDir, strWinGUI)); + rename((path)strWinGUI, (path)(strTrashDir + strWinGUI)); if (exists(strWinGUILang)) - rename((path)strWinGUILang, (path)strcat(strTrashDir, strWinGUILang)); + rename((path)strWinGUILang, (path)(strTrashDir + strWinGUILang)); rename((path)(strPathToEUFNInstall + strWinGUI), (path)strWinGUI); rename((path)(strPathToEUFNInstall + strWinGUILang), (path)strWinGUILang); } @@ -1320,7 +1346,11 @@ bool ProcessAEUpdate(Install_info_cfg *c curPos = thePath.find("/", lastPos); aParentPath = aParentPath + "/"; } +#ifndef WIN32 rename((path)("../" + thePath), (path)(strTrashDir + thePath)); +#else + remove((path)("../" + thePath)); +#endif } } @@ -1343,7 +1373,11 @@ bool ProcessAEUpdate(Install_info_cfg *c matchFound = true; if (iter1->modStringVersion > iter2->modStringVersion) { +#ifndef WIN32 rename((path)(strPathToPackages + iter2->modStringName), (path)(strTrashDir + iter2->modStringName)); +#else + remove((path)(strPathToPackages + iter2->modStringName)); +#endif rename((path)(strPathToEUFNPackages + iter1->modStringName), (path)(strPathToPackages + iter1->modStringName)); } } @@ -1454,6 +1488,17 @@ void tokenize(const string& str, vector< } } +/* StripNewlines() gets rids of any linebreaks that come from text returned by getline(); \ +| getline() should be stripping those out, but Windows CR/LF files seem to be sneaking | +\ some extra return characters into strings in the ReadInstallInfoCfg() function. */ +void StripNewlines(string *theLine) +{ + int deleteFromHere = 0; + deleteFromHere = theLine->find("\r"); + if (deleteFromHere > 0) + theLine->erase(deleteFromHere, theLine->size()); +} + void clearOldDats(void) { directory_iterator end_iter_gdf; for ( directory_iterator dir_itr_gdf( "../GameDataFolder" ); @@ -1579,12 +1624,12 @@ ModPackage::ModPackage() readme = ""; globalNeeded = true; } - +#ifndef WIN32 void Sleep(int ms) { sleep(ms / 1000); } - +#endif #ifdef WIN32 void RedirectIOToConsole()