#include "stdpch.h" #include "login_patch.h" #include "client_cfg.h" #include #ifdef NL_OS_WINDOWS #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif using namespace NLMISC; using namespace std; // stuff which is defined as extern in other .cpp files void quitCrashReport() { } /// domain server version for patch string R2ServerVersion; /// name of the version (used to alias many version under the same name), /// the value is used to get the release not if not empty string VersionName; string LoginLogin, LoginPassword; uint32 LoginShardId = 0xFFFFFFFF; // stuff which is defined in other .cpp files extern void tmpFlagRemovedPatchCategories(NLMISC::CConfigFile &cf); bool useUtf8 = false; bool useEsc = false; #ifdef NL_OS_WINDOWS HANDLE hStdout = NULL; sint attributes = 0; #endif std::string convert(const ucstring &str) { if (useUtf8) return str.toUtf8(); return str.toString(); } void printError(const std::string &str) { // display error in red if possible if (useEsc) { printf("\033[1;31mError: %s\033[0m\n", str.c_str()); } else { #ifdef NL_OS_WINDOWS if (hStdout != INVALID_HANDLE_VALUE && hStdout) SetConsoleTextAttribute(hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY); #endif printf("Error: %s\n", str.c_str()); #ifdef NL_OS_WINDOWS if (hStdout != INVALID_HANDLE_VALUE && hStdout) SetConsoleTextAttribute(hStdout, attributes); #endif } } void printCheck(const std::string &str) { // display check printf("%s\n", str.c_str()); } void printDownload(const std::string &str) { static uint previousLength = 0; static const uint maxLength = 160; static char spaces[maxLength]; uint length = 0; if (useUtf8) { ucstring utf8Str; utf8Str.fromUtf8(str); length = (uint)utf8Str.length(); } else { length = (uint)str.length(); } sint diff = length - previousLength; if (diff > 0 && length < maxLength) { memset(spaces, ' ', length); spaces[length] = '\0'; // "erase" previous line printf("%s\r", spaces); fflush(stdout); } // display download in purple if (useEsc) { printf("\033[1;35m%s\033[0m\r", str.c_str()); } else { #ifdef NL_OS_WINDOWS if (hStdout != INVALID_HANDLE_VALUE && hStdout) SetConsoleTextAttribute(hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY); #endif printf("%s\r", str.c_str()); #ifdef NL_OS_WINDOWS if (hStdout != INVALID_HANDLE_VALUE && hStdout) SetConsoleTextAttribute(hStdout, attributes); #endif } fflush(stdout); previousLength = length; } int main(int argc, char *argv[]) { // init the Nel context CApplicationContext appContext; // create logs in TEMP directory createDebug(getenv("TEMP"), true, true); // disable log display on stdout INelContext::getInstance().getDebugLog()->removeDisplayer("DEFAULT_SD"); INelContext::getInstance().getInfoLog()->removeDisplayer("DEFAULT_SD"); INelContext::getInstance().getWarningLog()->removeDisplayer("DEFAULT_SD"); std::string config = "client.cfg"; // if client.cfg is not in current directory, use client_default.cfg if (!CFile::isExists(config)) config = "client_default.cfg"; #ifdef RYZOM_ETC_PREFIX // if client_default.cfg is not in current directory, use application default directory if (!CFile::isExists(config)) config = CPath::standardizePath(RYZOM_ETC_PREFIX) + config; #endif if (!CFile::isExists(config)) { printError(config + " not found, aborting patch."); return 1; } // check if console supports utf-8 std::string lang = toLower(std::string(setlocale(LC_CTYPE, ""))); useUtf8 = (lang.find("utf8") != string::npos || lang.find("utf-8") != string::npos); lang = lang.substr(0, 2); // check if console supports colors std::string term = toLower(std::string(getenv("TERM") ? getenv("TERM"):"")); useEsc = (term.find("xterm") != string::npos || term.find("linux") != string::npos); #ifdef NL_OS_WINDOWS // setup Windows console hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo; if (GetConsoleScreenBufferInfo(hStdout, &consoleScreenBufferInfo)) attributes = consoleScreenBufferInfo.wAttributes; } #endif // create or load client.cfg ClientCfg.init(config); // check if PatchServer is defined if (ClientCfg.PatchServer.empty()) { printError("PatchServer not defined in " + config); return 1; } // set default paths std::string dataPath = "./data/"; std::string rootPath = "./"; // use custom data path if specified if (!ClientCfg.DataPath.empty()) { dataPath = CPath::standardizePath(ClientCfg.DataPath.front()); string::size_type pos = dataPath.rfind('/', dataPath.length()-2); if (pos != string::npos) rootPath = dataPath.substr(0, pos+1); } std::string unpackPath = CPath::standardizePath(rootPath + "unpack"); // check if user can write in data directory if (!CFile::isExists(unpackPath)) { if (!CFile::createDirectoryTree(unpackPath)) { printError("You don't have permission to create " + unpackPath); return 1; } } else { if (!CFile::createEmptyFile(unpackPath + "empty")) { printError("You don't have write permission in " + unpackPath); return 1; } CFile::deleteFile(unpackPath + "empty"); } // only use PreDataPath for looking paths if (!ClientCfg.PreDataPath.empty()) { for(uint i = 0; i < ClientCfg.PreDataPath.size(); ++i) { CPath::addSearchPath(ClientCfg.PreDataPath[i], true, false); } } // add more search paths if translation is not found if (!CPath::exists(lang + ".uxt")) { CPath::addSearchPath("patcher", true, false); #ifdef RYZOM_SHARE_PREFIX CPath::addSearchPath(RYZOM_SHARE_PREFIX"/patcher", true, false); #endif } // load translation CI18N::load(lang); printf("Checking %s files to patch...\n", convert(CI18N::get("TheSagaOfRyzom")).c_str()); #ifdef NL_OS_UNIX // don't use cfg, exe and dll from Windows version CConfigFile::CVar var; var.Type = CConfigFile::CVar::T_STRING; std::vector cats; cats.push_back("main_exedll"); cats.push_back("main_cfg"); var.setAsString(cats); ClientCfg.ConfigFile.insertVar("RemovePatchCategories", var); // add categories to remove tmpFlagRemovedPatchCategories(ClientCfg.ConfigFile); #endif // initialize patch manager and set the ryzom full path, before it's used CPatchManager *pPM = CPatchManager::getInstance(); // set the correct root path pPM->setClientRootPath(rootPath); // use PatchServer URL vector patchURLs; pPM->init(patchURLs, ClientCfg.PatchServer, ClientCfg.PatchVersion); pPM->startCheckThread(true /* include background patchs */); ucstring state; vector log; bool res = false; bool finished = false; while (!finished) { nlSleep(100); finished = pPM->isCheckThreadEnded(res); if (pPM->getThreadState(state, log)) { for(uint i = 0; i < log.size(); ++i) { printCheck(convert(log[i])); } } } if (!res && !pPM->getLastErrorMessage().empty()) { printError(convert(CI18N::get("uiErrChecking") + " " + pPM->getLastErrorMessage())); return 1; } CPatchManager::SPatchInfo InfoOnPatch; // Check is good now ask the player if he wants to apply the patch pPM->getInfoToDisp(InfoOnPatch); // Get the list of optional categories to patch vector vCategories; for(uint i = 0; i < InfoOnPatch.OptCat.size(); i++) { // Ok for the moment all optional categories must be patched even if the player // does not want it. Because we can't detect that a continent have to be patched ingame. vCategories.push_back(InfoOnPatch.OptCat[i].Name); } // start patch thread pPM->startPatchThread(vCategories, true); res = false; finished = false; while (!finished) { nlSleep(100); finished = pPM->isPatchThreadEnded(res); if (pPM->getThreadState(state, log)) { printDownload(convert(state)); for(uint i = 0; i < log.size(); ++i) { printCheck(convert(log[i])); } } } if (!res && !pPM->getLastErrorMessage().empty()) { printError(convert(CI18N::get("uiErrPatching") + " " + pPM->getLastErrorMessage())); return 1; } if (CPatchManager::getInstance()->mustLaunchBatFile()) { std::string error; try { // move downloaded files to final location pPM->createBatchFile(pPM->getDescFile(), false, false); CFile::createEmptyFile("show_eula"); if (!pPM->getLastErrorMessage().empty()) { error = convert(pPM->getLastErrorMessage()); } } catch(const EDiskFullError &) { error = convert(CI18N::get("uiPatchDiskFull"));; } catch(const EWriteError &) { error = convert(CI18N::get("uiPatchWriteError"));; } catch(const Exception &e) { error = convert(CI18N::get("uiCheckEndWithErr") + " " + e.what()); } catch(...) { error = "unknown exception"; } if (!error.empty()) { printError(convert(CI18N::get("uiErrPatchApply")) + " " + error); return 1; } } /* // Start Scanning pPM->startScanDataThread(); // request to stop the thread pPM->askForStopScanDataThread(); */ return 0; }