setVpx accept now all VPX using ; as delimiter
[ryzomcore.git] / ryzom / tools / client / client_patcher / main.cpp
blob1f19eeadfd1c043fa5d71365f9268ff72abf2be5
1 #include "stdpch.h"
2 #include "login_patch.h"
3 #include "client_cfg.h"
4 #include "user_agent.h"
6 #include "nel/misc/cmd_args.h"
8 #ifdef NL_OS_WINDOWS
9 #include <windows.h>
10 #endif
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
16 using namespace NLMISC;
17 using namespace std;
19 // simplified implementation to not depend on client_cfg.cpp
20 CClientConfig::CClientConfig()
24 void CClientConfig::serial(NLMISC::IStream &/* f */)
28 bool CClientConfig::getDefaultConfigLocation(std::string&/* p_name */) const
30 return false;
33 CClientConfig ClientCfg;
35 // stuff which is defined as extern in other .cpp files
36 void quitCrashReport()
40 void stopSoundMngr()
44 /// domain server version for patch
45 string R2ServerVersion;
46 /// name of the version (used to alias many version under the same name),
47 /// the value is used to get the release not if not empty
48 string VersionName;
50 string LoginLogin, LoginPassword;
51 uint32 LoginShardId = 0xFFFFFFFF;
53 CCmdArgs Args;
55 bool useEsc = false;
57 #ifdef NL_OS_WINDOWS
58 HANDLE hStdout = NULL;
59 sint attributes = 0;
60 #endif
62 std::string convert(const ucstring &str)
64 return str.toString();
67 void printError(const std::string &str)
69 // display error in red if possible
70 if (useEsc)
72 printf("\033[1;31mError: %s\033[0m\n", str.c_str());
74 else
76 #ifdef NL_OS_WINDOWS
77 if (hStdout != INVALID_HANDLE_VALUE && hStdout)
78 SetConsoleTextAttribute(hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY);
79 #endif
81 printf("Error: %s\n", str.c_str());
83 #ifdef NL_OS_WINDOWS
84 if (hStdout != INVALID_HANDLE_VALUE && hStdout)
85 SetConsoleTextAttribute(hStdout, attributes);
86 #endif
90 void printCheck(const std::string &str)
92 // display check
93 printf("%s\n", str.c_str());
96 void printDownload(const std::string &str)
98 static char spaces[80];
100 uint maxLength = 80;
102 // if "COLUMNS" environnement variable is defined, use it
103 if (getenv("COLUMNS"))
105 NLMISC::fromString(std::string(getenv("COLUMNS")), maxLength);
108 // only use 79 columns to not wrap
109 --maxLength;
111 // temporary modified string
112 std::string nstr = str;
114 uint length = (uint)nstr.length();
116 if (length > maxLength)
118 nstr = std::string("...") + nstr.substr(length - maxLength + 3);
119 length = maxLength;
122 // add padding with spaces
123 memset(spaces, ' ', maxLength);
124 spaces[maxLength - length] = '\0';
126 // display download in purple
127 if (useEsc)
129 printf("\033[1;35m%s%s\033[0m\r", nstr.c_str(), spaces);
131 else
133 #ifdef NL_OS_WINDOWS
134 if (hStdout != INVALID_HANDLE_VALUE && hStdout)
135 SetConsoleTextAttribute(hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
136 #endif
138 printf("%s%s\r", nstr.c_str(), spaces);
140 #ifdef NL_OS_WINDOWS
141 if (hStdout != INVALID_HANDLE_VALUE && hStdout)
142 SetConsoleTextAttribute(hStdout, attributes);
143 #endif
146 fflush(stdout);
149 // hardcoded english translations to not depend on external files
150 struct CClientPatcherTranslations : public NLMISC::CI18N::ILoadProxy
152 virtual void loadStringFile(const std::string &filename, ucstring &text)
154 text.fromUtf8(
155 "TheSagaOfRyzom [Ryzom]\n"
156 "uiErrPatchApply [Error: Patch process ended but the patch has not been successfully applied.]\n"
157 "uiErrChecking [Error: Patch files failed - checking.]\n"
158 "uiByte [B]\n"
159 "uiKb [KiB]\n"
160 "uiMb [MiB]\n"
161 "uiLoginGetFile [Getting File:]\n"
162 "uiDLWithCurl [Downloading File With Curl:]\n"
163 "uiDecompressing [Decompressing File:]\n"
164 "uiCheckInt [Checking Integrity:]\n"
165 "uiNoVersionFound [No Version Found]\n"
166 "uiVersionFound [Version Found:]\n"
167 "uiApplyingDelta [Applying Delta:]\n"
168 "uiClientVersion [Client Version]\n"
169 "uiServerVersion [Server Version]\n"
170 "uiCheckingFile [Checking File]\n"
171 "uiNeededPatches [Required Patches:]\n"
172 "uiCheckInBNP [Checking inside BNP:]\n"
173 "uiSHA1Diff [Force BNP Unpacking: checksums do not correspond:]\n"
174 "uiCheckEndNoErr [Checking file ended with no errors]\n"
175 "uiCheckEndWithErr [Checking file ended with errors:]\n"
176 "uiPatchEndNoErr [Patching file ended with no errors]\n"
177 "uiPatchEndWithErr [Patch failed!]\n"
178 "uiPatchDiskFull [Disk full!]\n"
179 "uiPatchWriteError [Disk write error! (disk full?)]\n"
180 "uiProcessing [Processing file:]\n"
181 "uiUnpack [BNP Unpacking:]\n"
182 "uiUnpackErrHead [Cannot read bnp header:]\n"
183 "uiChangeDate [Changing the mod date:]\n"
184 "uiChgDateErr [Cannot change file time:]\n"
185 "uiNowDate [Now the date is:]\n"
186 "uiSetAttrib [Set file attributes:]\n"
187 "uiAttribErr [Cannot have read/write access:]\n"
188 "uiDelFile [Delete file:]\n"
189 "uiDelErr [Cannot delete file:]\n"
190 "uiDelNoFile [Delete file (no file)]\n"
191 "uiRenameFile [Rename File:]\n"
192 "uiRenameErr [Cannot rename file:]\n"
197 // hardcoded URL to not depend on external files
198 static const std::string DefaultPatchUrl = RYZOM_CLIENT_PATCH_URL; // "https://cdn.ryzom.dev/open/patch";
199 static const std::string DefaultAppName = RYZOM_CLIENT_APP_NAME; // "default"
201 int main(int argc, char *argv[])
203 // init the Nel context
204 CApplicationContext appContext;
206 Args.setVersion(getDisplayVersion());
207 Args.setDescription("Ryzom client");
208 Args.addArg("p", "patch", "patch", "Name of the file to use tp xdelta the source file");
209 Args.addArg("s", "source", "source", "Name of source file to xdelta with patch file");
210 Args.addArg("d", "destination", "destination", "Name of destination operation (patch or unpack)");
211 Args.addArg("u", "unpack", "unpack", "Name of bnp file to unpack");
212 Args.addArg("", "url", "PatchUrl", "Patch server url, ie 'https://dl.ryzom.com/patch_live'");
213 Args.addArg("", "app", "Application", "Patch application name for version file, ie 'ryzom_live' requests ryzom_live.version from PatchUrl");
215 if (!Args.parse(argc, argv)) return 1;
217 if (Args.haveArg("p") && Args.haveArg("s") && Args.haveArg("d"))
219 string patchName = Args.getArg("p").front();
220 string sourceName = Args.getArg("s").front();
221 string destinationName = Args.getArg("d").front();
223 std::string errorMsg;
224 CXDeltaPatch::TApplyResult ar = CXDeltaPatch::apply(patchName, sourceName, destinationName, errorMsg);
225 nlinfo("%s", errorMsg.c_str());
226 return ar;
229 // initialize patch manager and set the ryzom full path, before it's used
230 CPatchManager *pPM = CPatchManager::getInstance();
232 if (Args.haveArg("u") && Args.haveArg("d"))
234 string bnpName = Args.getArg("u").front();
235 string destinationName = Args.getArg("d").front();
236 vector<string> vFilenames;
237 if (pPM->bnpUnpack(bnpName, destinationName, vFilenames))
238 return 0;
239 return 1;
242 // create logs in temporary directory
243 createDebug(CPath::getTemporaryDirectory().c_str(), true, true);
245 // disable log display on stdout
246 INelContext::getInstance().getDebugLog()->removeDisplayer("DEFAULT_SD");
247 INelContext::getInstance().getInfoLog()->removeDisplayer("DEFAULT_SD");
248 INelContext::getInstance().getWarningLog()->removeDisplayer("DEFAULT_SD");
250 // check if console supports colors
251 std::string term = toLowerAscii(std::string(getenv("TERM") ? getenv("TERM"):""));
252 useEsc = (term.find("xterm") != string::npos || term.find("linux") != string::npos);
254 #ifdef NL_OS_WINDOWS
255 // setup Windows console
256 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
258 if (hStdout != INVALID_HANDLE_VALUE)
260 CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo;
262 if (GetConsoleScreenBufferInfo(hStdout, &consoleScreenBufferInfo))
263 attributes = consoleScreenBufferInfo.wAttributes;
265 #endif
267 // allocate translations proxy
268 CClientPatcherTranslations *trans = new CClientPatcherTranslations();
270 // use proxy
271 CI18N::setLoadProxy(trans);
273 // load english translations
274 CI18N::load("en");
276 // now translations are read, we don't need it anymore
277 delete trans;
279 // create minimal client.cfg file in memory for patcher
281 CConfigFile::CVar patchUrl;
282 patchUrl.forceAsString(DefaultPatchUrl);
283 if (Args.haveLongArg("url") && !Args.getLongArg("url").empty())
284 patchUrl.forceAsString(Args.getLongArg("url").front());
286 CConfigFile::CVar appName;
287 appName.forceAsString(DefaultAppName);
288 if (Args.haveLongArg("app") && !Args.getLongArg("app").empty())
289 appName.forceAsString(Args.getLongArg("app").front());
291 ClientCfg.ConfigFile.insertVar("PatchUrl", patchUrl);
292 ClientCfg.ConfigFile.insertVar("Application", appName);
296 Args.displayVersion();
297 printf("\n");
298 printf("Checking %s files to patch...\n", convert(CI18N::get("TheSagaOfRyzom")).c_str());
299 printf("Using '%s/%s.version'\n", ClientCfg.ConfigFile.getVar("PatchUrl").asString().c_str(),
300 ClientCfg.ConfigFile.getVar("Application").asString().c_str());
302 // use PatchUrl
303 vector<string> patchURLs;
304 pPM->init(patchURLs, ClientCfg.ConfigFile.getVar("PatchUrl").asString(), "");
305 pPM->startCheckThread(true /* include background patchs */);
307 string state;
308 vector<string> log;
309 bool res = false;
310 bool finished = false;
312 while (!finished)
314 nlSleep(100);
316 finished = pPM->isCheckThreadEnded(res);
318 if (pPM->getThreadState(state, log))
320 for(uint i = 0; i < log.size(); ++i)
322 printCheck(convert(log[i]));
327 if (!res && !pPM->getLastErrorMessage().empty())
329 printError(convert(CI18N::get("uiErrChecking") + " " + pPM->getLastErrorMessage()));
330 return 1;
333 CPatchManager::SPatchInfo InfoOnPatch;
335 // Check is good now ask the player if he wants to apply the patch
336 pPM->getInfoToDisp(InfoOnPatch);
338 // Get the list of optional categories to patch
339 vector<string> vCategories;
341 for(uint i = 0; i < InfoOnPatch.OptCat.size(); i++)
343 // Ok for the moment all optional categories must be patched even if the player
344 // does not want it. Because we can't detect that a continent have to be patched ingame.
345 vCategories.push_back(InfoOnPatch.OptCat[i].Name);
348 // start patch thread
349 pPM->startPatchThread(vCategories, true);
351 res = false;
352 finished = false;
354 while (!finished)
356 nlSleep(100);
358 finished = pPM->isPatchThreadEnded(res);
360 if (pPM->getThreadState(state, log))
362 printDownload(convert(state));
364 for(uint i = 0; i < log.size(); ++i)
366 printCheck(convert(log[i]));
371 if (!res && !pPM->getLastErrorMessage().empty())
373 printError(convert(CI18N::get("uiErrPatchApply") + " " + pPM->getLastErrorMessage()));
374 return 1;
377 if (CPatchManager::getInstance()->mustLaunchBatFile())
379 std::string error;
383 // move downloaded files to final location
384 // batch file will not be created
385 pPM->createBatchFile(pPM->getDescFile(), false, false);
386 CFile::createEmptyFile("show_eula");
388 if (!pPM->getLastErrorMessage().empty())
390 error = convert(pPM->getLastErrorMessage());
393 catch(const EDiskFullError &)
395 error = convert(CI18N::get("uiPatchDiskFull"));;
397 catch(const EWriteError &)
399 error = convert(CI18N::get("uiPatchWriteError"));;
401 catch(const Exception &e)
403 error = convert(CI18N::get("uiCheckEndWithErr") + " " + e.what());
405 catch(...)
407 error = "unknown exception";
410 if (!error.empty())
412 printError(convert(CI18N::get("uiErrPatchApply")) + " " + error);
413 return 1;
417 // upgd_nl.sh will normally take care of the permissions
419 // for linux/macOS (no-op on windows)
420 // Set for current executable (might be 'dev' version),
421 // and also 'ryzom_client_patcher' directly (from patched files)
422 CFile::setExecutable(Args.getProgramPath() + Args.getProgramName());
423 CFile::setExecutable("ryzom_client_patcher");
424 // other
425 CFile::setExecutable("crash_report");
426 CFile::setExecutable("ryzom_client");
427 CFile::setExecutable("ryzom_installer_qt");
428 CFile::setExecutable("ryzom_configuration_qt");
430 return 0;