2 #include "login_patch.h"
3 #include "client_cfg.h"
4 #include "user_agent.h"
6 #include "nel/misc/cmd_args.h"
16 using namespace NLMISC
;
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
33 CClientConfig ClientCfg
;
35 // stuff which is defined as extern in other .cpp files
36 void quitCrashReport()
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
50 string LoginLogin
, LoginPassword
;
51 uint32 LoginShardId
= 0xFFFFFFFF;
58 HANDLE hStdout
= NULL
;
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
72 printf("\033[1;31mError: %s\033[0m\n", str
.c_str());
77 if (hStdout
!= INVALID_HANDLE_VALUE
&& hStdout
)
78 SetConsoleTextAttribute(hStdout
, FOREGROUND_RED
|FOREGROUND_INTENSITY
);
81 printf("Error: %s\n", str
.c_str());
84 if (hStdout
!= INVALID_HANDLE_VALUE
&& hStdout
)
85 SetConsoleTextAttribute(hStdout
, attributes
);
90 void printCheck(const std::string
&str
)
93 printf("%s\n", str
.c_str());
96 void printDownload(const std::string
&str
)
98 static char spaces
[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
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);
122 // add padding with spaces
123 memset(spaces
, ' ', maxLength
);
124 spaces
[maxLength
- length
] = '\0';
126 // display download in purple
129 printf("\033[1;35m%s%s\033[0m\r", nstr
.c_str(), spaces
);
134 if (hStdout
!= INVALID_HANDLE_VALUE
&& hStdout
)
135 SetConsoleTextAttribute(hStdout
, FOREGROUND_RED
|FOREGROUND_BLUE
|FOREGROUND_INTENSITY
);
138 printf("%s%s\r", nstr
.c_str(), spaces
);
141 if (hStdout
!= INVALID_HANDLE_VALUE
&& hStdout
)
142 SetConsoleTextAttribute(hStdout
, attributes
);
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
)
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"
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("", "url", "PatchUrl", "Patch server url, ie 'https://dl.ryzom.com/patch_live'");
209 Args
.addArg("", "app", "Application", "Patch application name for version file, ie 'ryzom_live' requests ryzom_live.version from PatchUrl");
211 if (!Args
.parse(argc
, argv
)) return 1;
213 // create logs in temporary directory
214 createDebug(CPath::getTemporaryDirectory().c_str(), true, true);
216 // disable log display on stdout
217 INelContext::getInstance().getDebugLog()->removeDisplayer("DEFAULT_SD");
218 INelContext::getInstance().getInfoLog()->removeDisplayer("DEFAULT_SD");
219 INelContext::getInstance().getWarningLog()->removeDisplayer("DEFAULT_SD");
221 // check if console supports colors
222 std::string term
= toLowerAscii(std::string(getenv("TERM") ? getenv("TERM"):""));
223 useEsc
= (term
.find("xterm") != string::npos
|| term
.find("linux") != string::npos
);
226 // setup Windows console
227 hStdout
= GetStdHandle(STD_OUTPUT_HANDLE
);
229 if (hStdout
!= INVALID_HANDLE_VALUE
)
231 CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo
;
233 if (GetConsoleScreenBufferInfo(hStdout
, &consoleScreenBufferInfo
))
234 attributes
= consoleScreenBufferInfo
.wAttributes
;
238 // allocate translations proxy
239 CClientPatcherTranslations
*trans
= new CClientPatcherTranslations();
242 CI18N::setLoadProxy(trans
);
244 // load english translations
247 // now translations are read, we don't need it anymore
250 // create minimal client.cfg file in memory for patcher
252 CConfigFile::CVar patchUrl
;
253 patchUrl
.forceAsString(DefaultPatchUrl
);
254 if (Args
.haveLongArg("url") && !Args
.getLongArg("url").empty())
255 patchUrl
.forceAsString(Args
.getLongArg("url").front());
257 CConfigFile::CVar appName
;
258 appName
.forceAsString(DefaultAppName
);
259 if (Args
.haveLongArg("app") && !Args
.getLongArg("app").empty())
260 appName
.forceAsString(Args
.getLongArg("app").front());
262 ClientCfg
.ConfigFile
.insertVar("PatchUrl", patchUrl
);
263 ClientCfg
.ConfigFile
.insertVar("Application", appName
);
267 Args
.displayVersion();
269 printf("Checking %s files to patch...\n", convert(CI18N::get("TheSagaOfRyzom")).c_str());
270 printf("Using '%s/%s.version'\n", ClientCfg
.ConfigFile
.getVar("PatchUrl").asString().c_str(),
271 ClientCfg
.ConfigFile
.getVar("Application").asString().c_str());
273 // initialize patch manager and set the ryzom full path, before it's used
274 CPatchManager
*pPM
= CPatchManager::getInstance();
277 vector
<string
> patchURLs
;
278 pPM
->init(patchURLs
, ClientCfg
.ConfigFile
.getVar("PatchUrl").asString(), "");
279 pPM
->startCheckThread(true /* include background patchs */);
284 bool finished
= false;
290 finished
= pPM
->isCheckThreadEnded(res
);
292 if (pPM
->getThreadState(state
, log
))
294 for(uint i
= 0; i
< log
.size(); ++i
)
296 printCheck(convert(log
[i
]));
301 if (!res
&& !pPM
->getLastErrorMessage().empty())
303 printError(convert(CI18N::get("uiErrChecking") + " " + pPM
->getLastErrorMessage()));
307 CPatchManager::SPatchInfo InfoOnPatch
;
309 // Check is good now ask the player if he wants to apply the patch
310 pPM
->getInfoToDisp(InfoOnPatch
);
312 // Get the list of optional categories to patch
313 vector
<string
> vCategories
;
315 for(uint i
= 0; i
< InfoOnPatch
.OptCat
.size(); i
++)
317 // Ok for the moment all optional categories must be patched even if the player
318 // does not want it. Because we can't detect that a continent have to be patched ingame.
319 vCategories
.push_back(InfoOnPatch
.OptCat
[i
].Name
);
322 // start patch thread
323 pPM
->startPatchThread(vCategories
, true);
332 finished
= pPM
->isPatchThreadEnded(res
);
334 if (pPM
->getThreadState(state
, log
))
336 printDownload(convert(state
));
338 for(uint i
= 0; i
< log
.size(); ++i
)
340 printCheck(convert(log
[i
]));
345 if (!res
&& !pPM
->getLastErrorMessage().empty())
347 printError(convert(CI18N::get("uiErrPatchApply") + " " + pPM
->getLastErrorMessage()));
351 if (CPatchManager::getInstance()->mustLaunchBatFile())
357 // move downloaded files to final location
358 // batch file will not be created
359 pPM
->createBatchFile(pPM
->getDescFile(), false, false);
360 CFile::createEmptyFile("show_eula");
362 if (!pPM
->getLastErrorMessage().empty())
364 error
= convert(pPM
->getLastErrorMessage());
367 catch(const EDiskFullError
&)
369 error
= convert(CI18N::get("uiPatchDiskFull"));;
371 catch(const EWriteError
&)
373 error
= convert(CI18N::get("uiPatchWriteError"));;
375 catch(const Exception
&e
)
377 error
= convert(CI18N::get("uiCheckEndWithErr") + " " + e
.what());
381 error
= "unknown exception";
386 printError(convert(CI18N::get("uiErrPatchApply")) + " " + error
);
391 // upgd_nl.sh will normally take care of the permissions
393 // for linux/macOS (no-op on windows)
394 // Set for current executable (might be 'dev' version),
395 // and also 'ryzom_client_patcher' directly (from patched files)
396 CFile::setExecutable(Args
.getProgramPath() + Args
.getProgramName());
397 CFile::setExecutable("ryzom_client_patcher");
399 CFile::setExecutable("crash_report");
400 CFile::setExecutable("ryzom_client");
401 CFile::setExecutable("ryzom_installer_qt");
402 CFile::setExecutable("ryzom_configuration_qt");