1 // NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <curl/curl.h>
29 #include "nel/misc/debug.h"
30 #include "nel/misc/common.h"
31 #include "nel/misc/path.h"
32 #include "nel/misc/thread.h"
35 #include "nel_launcher_dlg.h"
44 using namespace NLMISC
;
51 HINTERNET RootInternet
= NULL
;
53 static const string DirFilename
= "dir.ngz";
54 static const string NelLauncherFilename
= "nel_launcher.exe";
55 static const string NelLauncherConfigFilename
= "nel_launcher.cfg";
57 // The file is auto generated by the nel launcher (and deleted) when the .cfg or .exe has been changed in the root directory
58 static const string UpdateNelLauncherBatchFilename
= "updt_nl.bat";
60 // This file must be in the patch server, will be execute after killing nel_launcher in the root directory
61 static const string RelaunchNelLauncherBatchFilename
= "rlnch_nl.bat";
63 // This file must be in the patch server, will be execute after the patching and waiting the end of the .bat to continue to execute the launcher in the root directory
64 static const string FinalizeNelLauncherBatchFilename
= "fnlz_ptc.bat";
75 CEntry(const string
&fn
, uint32 s
, uint32 d
) : Filename(fn
), Size(s
), Date(d
) { }
83 void setRWAccess (const string
&filename
)
85 if(VerboseLog
) nlinfo("setRWAccess to '%s'", filename
.c_str());
87 if (!NLMISC::CFile::setRWAccess(filename
))
89 nlwarning ("Can't have read/write access to '%s' file : code=%d %s", filename
.c_str(), errno
, strerror(errno
));
90 throw Exception ("Can't have read/write access to '%s' file : code=%d %s (error code 18)", filename
.c_str(), errno
, strerror(errno
));
94 string
deleteFile (const string
&filename
, bool throwException
=true)
96 if(VerboseLog
) nlinfo("delete file '%s'", filename
.c_str());
98 if (!NLMISC::CFile::deleteFile(filename
))
100 string str
= toString("Can't delete '%s' file : code=%d %s (error code 19)", filename
.c_str(), errno
, strerror(errno
));
101 nlwarning (str
.c_str());
103 throw Exception (str
);
109 void setVersion(const std::string
&version
)
111 if(VerboseLog
) nlinfo("setVersion to '%s'", version
.c_str());
113 string fn
= "VERSION";
116 FILE *fp
= fopen (fn
.c_str(), "wb");
119 throw Exception ("Can't open file '%s' : code=%d %s (error code 20)", fn
.c_str (), errno
, strerror(errno
));
122 if (fputs (version
.c_str (), fp
) == EOF
)
124 throw Exception ("Can't write file '%s' : code=%d %s (error code 21)", fn
.c_str (), errno
, strerror(errno
));
131 if(VerboseLog
) nlinfo("getVersion");
133 string fn
= "VERSION";
134 FILE *fp
= fopen (fn
.c_str (), "rb");
138 if (fgets (ver
, 998, fp
) != NULL
)
144 throw Exception ("Can't read file '%s' : code=%d %s (error code 22)", fn
.c_str (), errno
, strerror(errno
));
150 nlwarning ("Can't open file '%s' : code=%d %s", fn
.c_str (), errno
, strerror(errno
));
155 static string currentFile
;
156 int myProgressFunc(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
158 class CPatchThread
: public IRunnable
162 CPatchThread(const string
&sp
, const string
&sv
, const std::string
&urlOk
, const std::string
&urlFailed
, const std::string
&logSeparator
) :
163 ServerPath (sp
), ServerVersion(sv
), UrlOk(urlOk
), UrlFailed(urlFailed
), Ended(false), StateChanged(true), LogSeparator(logSeparator
)
167 bool Ended
; // true if the thread have ended the patch
168 bool PatchOk
; // true if the patch was good
169 string Url
; // url to display after the patch
177 string ClientRootPath
; // the root client path (c:\ryzom)
178 string ClientPatchPath
; // the patch path (c:\ryzom\patch)
179 string ServerRootPath
; // the root server path (http://www.toto.com)
180 string DisplayedServerRootPath
; // contains the serverpath without login and password
182 // get a file and decompress it in the patch directory
183 void getFile (const CEntry
&e
)
185 string path
= ClientPatchPath
+ e
.Filename
;
186 nlinfo ("Get the file from '%s' to '%s'", string(DisplayedServerRootPath
+e
.Filename
).c_str(), path
.c_str());
188 downloadFile (ServerRootPath
+e
.Filename
+".ngz", path
+".ngz");
190 decompressFile (path
+".ngz", e
.Date
);
196 nlinfo("CPathThread::run called");
200 CurrentFilesToGet
= 0;
201 CurrentBytesToGet
= 0;
205 bool executeFinalizeBat
= false;
207 ClientRootPath
= "./";
208 ClientPatchPath
= "./patch/";
209 ServerRootPath
= CPath::standardizePath (ServerPath
)+ServerVersion
+"/";
210 DisplayedServerRootPath
; // contains the serverpath without login and password
212 uint pos
= ServerRootPath
.find ("@");
213 if (pos
!= string::npos
)
215 DisplayedServerRootPath
= "http://"+ServerRootPath
.substr (pos
+1);
219 DisplayedServerRootPath
= ServerRootPath
;
222 setState(true, true, "Patching from '%s'", DisplayedServerRootPath
.c_str());
224 // create the patch directory if not exists
225 if (!NLMISC::CFile::isExists ("patch"))
227 setState(true, true, "Creating patch directory");
228 if (_mkdir ("patch") == -1)
230 throw Exception ("Can't create patch directory : code=%d %s (error code 23)", errno
, strerror(errno
));
234 // first, get the file that contains all files (dir.ngz)
235 deleteFile (DirFilename
.c_str(), false);
236 downloadFile (ServerRootPath
+DirFilename
, DirFilename
);
238 // now parse the file
239 gzFile gz
= gzopen (DirFilename
.c_str (), "rb");
243 const char *gzerr
= gzerror (gz
, &gzerrno
);
244 throw Exception ("Can't open file '%s': code=%d %s (error code 24)", DirFilename
.c_str(), gzerrno
, gzerr
);
247 vector
<CEntry
> filesList
;
248 vector
<CEntry
> needToGetFilesList
;
250 setState(true, true, "Parsing %s...", DirFilename
.c_str());
253 if (gzgets (gz
, buffer
, 2000) == NULL
)
256 const char *gzerr
= gzerror (gz
, &gzerrno
);
257 throw Exception ("Can't read header of'%s' : code=%d %s (error code 25)", DirFilename
.c_str(), gzerrno
, gzerr
);
260 if (string(buffer
) != "FILESLIST\n")
262 throw Exception ("%s has not a valid content '%s' : code=8888 (error code 26)", DirFilename
.c_str(), buffer
);
267 if (gzgets (gz
, buffer
, 2000) == NULL
)
270 const char *gzerr
= gzerror (gz
, &gzerrno
);
271 throw Exception ("Can't read '%s' : code=%d %s (error code 27)", DirFilename
.c_str(), gzerrno
, gzerr
);
275 uint pos1
= b
.find ("/");
276 uint pos2
= b
.find ("/", pos1
+1);
278 if (pos1
!= string::npos
|| pos2
!= string::npos
)
280 string filename
= b
.substr (0, pos1
);
281 uint32 size
= atoi(b
.substr (pos1
+1, pos2
-pos1
).c_str());
282 uint32 date
= atoi(b
.substr (pos2
+1).c_str());
284 string path
= ClientRootPath
+filename
;
285 if (!NLMISC::CFile::fileExists (path
))
287 path
= ClientPatchPath
+ filename
;
290 // we have to check around 2 seconds because some windows file system store
291 // their time is less bits
292 uint32 moddate
= NLMISC::CFile::getFileModificationDate(path
);
293 uint32 delta
= (moddate
>date
) ? (moddate
-date
) : (date
-moddate
);
295 nlinfo("'%s' local %u %uB server %u %uB delta %u", filename
.c_str(), moddate
, NLMISC::CFile::getFileSize (path
), date
, size
, delta
);
297 if (delta
> 2 || NLMISC::CFile::getFileSize (path
) != size
)
299 nlinfo("file '%s' is not the same date/size than on server, need to get it", filename
.c_str());
300 needToGetFilesList
.push_back (CEntry(filename
, size
, date
));
302 TotalBytesToGet
+= size
;
304 filesList
.push_back (CEntry(filename
, size
, date
));
309 // if we need to update nel_launcher.exe don't patch other file now, get
310 // nel_launcher.exe and relaunch it now
312 bool patchExe
= false, patchCfg
= false, patchBat
= false;
316 for (i
= 0; i
< needToGetFilesList
.size(); i
++)
318 string fn
= needToGetFilesList
[i
].Filename
;
320 if (fn
== NelLauncherFilename
)
322 getFile (needToGetFilesList
[i
]);
325 else if (fn
== NelLauncherConfigFilename
)
327 getFile (needToGetFilesList
[i
]);
330 else if (fn
== RelaunchNelLauncherBatchFilename
)
332 getFile (needToGetFilesList
[i
]);
339 setState (true, true, "Launching %s", RelaunchNelLauncherBatchFilename
.c_str());
341 //if (_execlp ("update_nel_launcher.bat", "update_nel_launcher.bat", NULL) == -1)
343 PROCESS_INFORMATION pi
;
345 ZeroMemory( &si
, sizeof(si
) );
346 // Flag permettant de prendre en compte wShowWindow
347 si
.dwFlags
= STARTF_USESHOWWINDOW
;
348 si
.wShowWindow
= SW_HIDE
;
352 ZeroMemory( &pi
, sizeof(pi
) );
354 // Start the child process.
355 if( !CreateProcess( NULL
, // No module name (use command line).
356 (char*)RelaunchNelLauncherBatchFilename
.c_str(), // Command line.
357 NULL
, // Process handle not inheritable.
358 NULL
, // Thread handle not inheritable.
359 FALSE
, // Set handle inheritance to FALSE.
360 0, // No creation flags.
361 NULL
, // Use parent's environment block.
362 NULL
, // Use parent's starting directory.
363 &si
, // Pointer to STARTUPINFO structure.
364 &pi
) // Pointer to PROCESS_INFORMATION structure.
367 // error occurs during the launch
368 string str
= toString("Can't execute '%s': code=%d %s (error code 28)", RelaunchNelLauncherBatchFilename
.c_str(), errno
, strerror(errno
));
369 throw Exception (str
);
372 // Close process and thread handles.
373 CloseHandle( pi
.hProcess
);
374 CloseHandle( pi
.hThread
);
378 else if (patchExe
|| patchCfg
)
380 FILE *fp
= fopen (UpdateNelLauncherBatchFilename
.c_str(), "wt");
383 string err
= toString("Can't open file '%s' for writing: code=%d %s (error code 29)", UpdateNelLauncherBatchFilename
.c_str(), errno
, strerror(errno
));
384 throw Exception (err
);
387 fprintf(fp
, "@echo off\n");
391 nlinfo ("Need to special patch '%s'",NelLauncherFilename
.c_str());
393 fprintf(fp
, ":loopexe\n");
394 fprintf(fp
, "attrib -r -a -s -h %s\n", NelLauncherFilename
.c_str());
395 fprintf(fp
, "del %s\n", NelLauncherFilename
.c_str());
396 fprintf(fp
, "if exist %s goto loopexe\n", NelLauncherFilename
.c_str());
397 fprintf(fp
, "move patch\\%s .\n", NelLauncherFilename
.c_str());
402 nlinfo ("Need to special patch '%s'",NelLauncherConfigFilename
.c_str());
404 fprintf(fp
, ":loopcfg\n");
405 fprintf(fp
, "attrib -r -a -s -h %s\n", NelLauncherConfigFilename
.c_str());
406 fprintf(fp
, "del %s\n", NelLauncherConfigFilename
.c_str());
407 fprintf(fp
, "if exist %s goto loopcfg\n", NelLauncherConfigFilename
.c_str());
408 fprintf(fp
, "move patch\\%s .\n", NelLauncherConfigFilename
.c_str());
411 fprintf(fp
, "start %s\n", NelLauncherFilename
.c_str());
415 // remove the files list file
416 setState (true, true, "Deleting %s", DirFilename
.c_str());
417 string err
= deleteFile (DirFilename
, false);
418 if (!err
.empty()) setState(true, true, err
.c_str());
420 // launching the .bat
421 setState (true, true, "Launching %s", UpdateNelLauncherBatchFilename
.c_str());
423 //if (_execlp ("update_nel_launcher.bat", "update_nel_launcher.bat", NULL) == -1)
425 PROCESS_INFORMATION pi
;
427 ZeroMemory( &si
, sizeof(si
) );
428 // Flag permettant de prendre en compte wShowWindow
429 si
.dwFlags
= STARTF_USESHOWWINDOW
;
430 si
.wShowWindow
= SW_HIDE
;
434 ZeroMemory( &pi
, sizeof(pi
) );
436 // Start the child process.
437 if( !CreateProcess( NULL
, // No module name (use command line).
438 (char*)UpdateNelLauncherBatchFilename
.c_str(), // Command line.
439 NULL
, // Process handle not inheritable.
440 NULL
, // Thread handle not inheritable.
441 FALSE
, // Set handle inheritance to FALSE.
442 0, // No creation flags.
443 NULL
, // Use parent's environment block.
444 NULL
, // Use parent's starting directory.
445 &si
, // Pointer to STARTUPINFO structure.
446 &pi
) // Pointer to PROCESS_INFORMATION structure.
449 // error occurs during the launch
450 string str
= toString("Can't execute '%s': code=%d %s (error code 30)", UpdateNelLauncherBatchFilename
.c_str(), errno
, strerror(errno
));
451 throw Exception (str
);
454 // Close process and thread handles.
455 CloseHandle( pi
.hProcess
);
456 CloseHandle( pi
.hThread
);
461 // get files if necessary
462 for (i
= 0; i
< needToGetFilesList
.size (); i
++)
464 // we already get these file, don't get it again (should never happen because already get on the last launch)
465 if (needToGetFilesList
[i
].Filename
== NelLauncherFilename
||
466 needToGetFilesList
[i
].Filename
== NelLauncherConfigFilename
||
467 needToGetFilesList
[i
].Filename
== RelaunchNelLauncherBatchFilename
471 if (needToGetFilesList
[i
].Filename
== FinalizeNelLauncherBatchFilename
)
473 executeFinalizeBat
= true;
476 // put the file in the ryzom patch directory
477 string path
= ClientPatchPath
+ needToGetFilesList
[i
].Filename
;
479 //nldebug ("path '%s' -> %d %s", path.c_str(), NLMISC::CFile::fileExists (ClientRootPath + needToGetFilesList[i].Filename), strlwr(NLMISC::CFile::getExtension(needToGetFilesList[i].Filename)).c_str());
481 // move dll exe and already existing file in the root directory
482 if (NLMISC::CFile::fileExists (ClientRootPath
+ needToGetFilesList
[i
].Filename
) ||
483 strlwr(NLMISC::CFile::getExtension(needToGetFilesList
[i
].Filename
)) == "dll" ||
484 strlwr(NLMISC::CFile::getExtension(needToGetFilesList
[i
].Filename
)) == "exe")
486 path
= ClientRootPath
+ needToGetFilesList
[i
].Filename
;
489 //setState (true, true, "Get the file from '%s' to '%s'", string(DisplayedServerRootPath+needToGetFilesList[i].Filename).c_str(), path.c_str());
492 downloadFile (ServerRootPath
+needToGetFilesList
[i
].Filename
+".ngz", path
+".ngz");
494 decompressFile (path
+".ngz", needToGetFilesList
[i
].Date
);
497 if (RootInternet
!= NULL
)
499 InternetCloseHandle(RootInternet
);
503 // now, we have to delete files that are not in the server list
505 setState(true, true, "Scanning patch directory");
507 CPath::getPathContent(ClientPatchPath
, false, false, true, res
);
509 for (i
= 0; i
< res
.size (); i
++)
511 string fn
= strlwr(NLMISC::CFile::getFilename (res
[i
]));
513 for (j
= 0; j
< filesList
.size (); j
++)
515 if (fn
== strlwr(filesList
[j
].Filename
))
520 if (j
== filesList
.size ())
522 string file
= ClientPatchPath
+NLMISC::CFile::getFilename (res
[i
]);
523 setState(true, true, "Deleting %s", file
.c_str());
524 string err
= deleteFile (file
, false);
525 if (!err
.empty()) setState(true, true, err
.c_str());
529 // remove the files list file
530 setState (true, true, "Deleting %s", DirFilename
.c_str());
531 string err
= deleteFile (DirFilename
, false);
532 if (!err
.empty()) setState(true, true, err
.c_str());
534 // now that all is ok, we set the new client version
535 setState (true, true, "set client version to %s", ServerVersion
.c_str ());
536 setVersion (ServerVersion
);
538 if (executeFinalizeBat
)
540 // execute the configurator if necessary
541 string fn
= "patch\\"+FinalizeNelLauncherBatchFilename
;
542 setState (true, true, "Launching %s", fn
.c_str());
546 // it s the end of the patch process
547 setState (true, true, "Patching completed");
562 void decompressFile (const string
&filename
, uint32 date
)
564 setState(true, true, "Decompressing %s...", NLMISC::CFile::getFilename(filename
).c_str ());
566 if(VerboseLog
) nlinfo("Calling gzopen('%s','rb')", filename
.c_str());
567 gzFile gz
= gzopen (filename
.c_str (), "rb");
570 string err
= toString("Can't open compressed file '%s' : ", filename
.c_str());
575 const char *gzerr
= gzerror (gz
, &gzerrno
);
576 err
+= toString("code=%d %s", gzerrno
, gzerr
);
580 err
+= toString("code=%d %s", errno
, strerror (errno
));
582 err
+= " (error code 31)";
583 deleteFile (filename
);
584 throw Exception (err
);
587 string dest
= filename
.substr(0, filename
.size ()-4);
589 if(VerboseLog
) nlinfo("Calling fopen('%s','wb')", dest
.c_str());
590 FILE *fp
= nlfopen (dest
, "wb");
593 string err
= toString("Can't open file '%s' : code=%d %s, (error code 32)", dest
.c_str(), errno
, strerror(errno
));
596 deleteFile (filename
);
597 throw Exception (err
);
600 if(VerboseLog
) nlinfo("Entering the while loop decompression");
602 uint32 currentSize
= 0;
606 if(VerboseLog
) nlinfo("Calling gzread");
607 int res
= gzread (gz
, buffer
, 10000);
608 if(VerboseLog
) nlinfo("gzread returns %d", res
);
612 const char *gzerr
= gzerror (gz
, &gzerrno
);
615 //deleteFile (filename);
616 throw Exception ("Can't read compressed file '%s' (after %d bytes) : code=%d %s, (error code 33)", filename
.c_str(), currentSize
, gzerrno
, gzerr
);
621 if(VerboseLog
) nlinfo("Calling fwrite for %d bytes", res
);
622 int res2
= fwrite (buffer
, 1, res
, fp
);
623 if(VerboseLog
) nlinfo("fwrite returns %d", res2
);
626 string err
= toString("Can't write file '%s' : code=%d %s (error code 34)", dest
.c_str(), errno
, strerror(errno
));
630 deleteFile (filename
);
631 throw Exception (err
);
635 if(VerboseLog
) nlinfo("Exiting the while loop decompression");
637 if(VerboseLog
) nlinfo("Calling gzclose");
639 if(VerboseLog
) nlinfo("Calling fclose");
641 deleteFile(filename
);
643 // change the file time for having the same as the server side
648 utb
.actime
= utb
.modtime
= date
;
650 if(VerboseLog
) nlinfo("Calling _utime");
651 nlinfo("'%s' changing the mod date from %u into %u", dest
.c_str(), NLMISC::CFile::getFileModificationDate (dest
), date
);
652 if (_utime (dest
.c_str (), &utb
) == -1)
654 nlwarning ("Can't change file time for '%s' : code=%d %s", dest
.c_str (), errno
, strerror(errno
));
656 nlinfo("'%s' now the date is %u", dest
.c_str(), NLMISC::CFile::getFileModificationDate (dest
));
658 if(VerboseLog
) nlinfo("Exiting the decompressing file");
661 void downloadFileWithCurl (const string
&source
, const string
&dest
)
665 if(VerboseLog
) nlinfo("downloadFileWithCurl file '%s'", dest
.c_str());
667 // user agent = nel_launcher
672 setState(true, true, "Getting %s", NLMISC::CFile::getFilename (source
).c_str ());
673 currentFile
= NLMISC::CFile::getFilename (source
);
675 curl_global_init(CURL_GLOBAL_ALL
);
676 curl
= curl_easy_init();
679 // file not found, delete local file
680 throw Exception ("curl init failed");
682 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, FALSE
);
683 curl_easy_setopt(curl
, CURLOPT_PROGRESSFUNCTION
, myProgressFunc
);
684 curl_easy_setopt(curl
, CURLOPT_URL
, source
.c_str());
686 // create the local file
688 FILE *fp
= fopen (dest
.c_str(), "wb");
691 throw Exception ("Can't open file '%s' for writing: code=%d %s (error code 37)", dest
.c_str (), errno
, strerror(errno
));
693 curl_easy_setopt(curl
, CURLOPT_FILE
, fp
);
697 res
= curl_easy_perform(curl
);
700 curl_easy_getinfo(curl
, CURLINFO_RESPONSE_CODE
, &r
);
702 curl_easy_cleanup(curl
);
705 curl_global_cleanup();
709 if(CURLE_FTP_COULDNT_RETR_FILE
== res
)
711 // file not found, delete local file
712 NLMISC::CFile::deleteFile(dest
.c_str());
713 throw Exception ("curl download failed: (ec %d %d)", res
, r
);
718 NLMISC::CFile::deleteFile(dest
.c_str());
719 throw Exception ("curl download failed: (ec %d %d)", res
, r
);
724 // file not found, delete it
725 NLMISC::CFile::deleteFile(dest
.c_str());
726 throw Exception ("curl download failed: (ec %d %d)", res
, r
);
729 throw Exception("USE_CURL is not defined, no curl method");
733 void downloadFile (const string
&source
, const string
&dest
)
735 // only use curl to test
738 downloadFileWithCurl(source
, dest
);
739 // download ok, don't continue;
744 nlwarning("downloadFileWithCurl failed '%s', try with windows method", e
.what());
747 const uint32 bufferSize
= 8000;
748 uint8 buffer
[bufferSize
];
750 if(VerboseLog
) nlinfo("downloadFile '%s'", dest
.c_str());
752 if (RootInternet
== NULL
)
754 RootInternet
= InternetOpen("nel_launcher", INTERNET_OPEN_TYPE_PRECONFIG
, NULL
, NULL
, 0);
755 if (RootInternet
== NULL
)
760 DWORD errcode
= GetLastError ();
761 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
762 errcode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
763 (LPTSTR
) &lpMsgBuf
, 0, NULL
) > 0)
765 errorstr
= (LPCTSTR
)lpMsgBuf
;
770 errorstr
= "FormatMessage can't get the message";
773 throw Exception ("InternetOpen() failed: %s (ec %d) (error code 35)", errorstr
.c_str(), errcode
);
777 HINTERNET hUrlDump
= InternetOpenUrl(RootInternet
, source
.c_str(), NULL
, NULL
, INTERNET_FLAG_NO_AUTO_REDIRECT
| INTERNET_FLAG_RAW_DATA
, 0);
778 if (hUrlDump
== NULL
)
783 DWORD errcode
= GetLastError ();
784 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
785 errcode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
786 (LPTSTR
) &lpMsgBuf
, 0, NULL
) > 0)
788 errorstr
= (LPCTSTR
)lpMsgBuf
;
793 errorstr
= "FormatMessage can't get the message";
796 throw Exception ("InternetOpenUrl() failed on file '%s': %s (ec %d) (error code 36)", source
.c_str (), errorstr
.c_str(), errcode
);
800 FILE *fp
= fopen (dest
.c_str(), "wb");
803 throw Exception ("Can't open file '%s' for writing: code=%d %s (error code 37)", dest
.c_str (), errno
, strerror(errno
));
808 setState(true, true, "Getting %s", NLMISC::CFile::getFilename (source
).c_str ());
814 if(!InternetReadFile(hUrlDump
,(LPVOID
)buffer
, bufferSize
, &realSize
))
818 DWORD errcode
= GetLastError ();
819 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
820 errcode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
821 (LPTSTR
) &lpMsgBuf
, 0, NULL
) > 0)
823 errorstr
= (LPCTSTR
)lpMsgBuf
;
828 errorstr
= "FormatMessage can't get the message";
834 throw Exception ("InternetOpenUrl() failed on file '%s': %s (ec %d) (error code 38)", source
.c_str (), errorstr
.c_str(), errcode
);
840 // download complete successfully
844 int res2
= fwrite (buffer
, 1, realSize
, fp
);
845 if ((DWORD
)res2
!= realSize
)
847 string err
= toString("Can't write file '%s' : code=%d %s (error code 39)", dest
.c_str(), errno
, strerror(errno
));
851 throw Exception (err
);
854 CurrentBytesToGet
+= realSize
;
856 if (TotalBytesToGet
== 0 && TotalFilesToGet
== 0)
857 setState(false, false, "Getting %s, %d bytes downloaded", NLMISC::CFile::getFilename (source
).c_str (), CurrentBytesToGet
);
859 setState(false, false, "Getting file %d on %d, %d bytes, filename %s", CurrentFilesToGet
, TotalFilesToGet
, CurrentBytesToGet
, NLMISC::CFile::getFilename (source
).c_str ());
866 if (!InternetCloseHandle(hUrlDump
))
870 DWORD errcode
= GetLastError ();
871 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
, NULL
,
872 errcode
, MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), // Default language
873 (LPTSTR
) &lpMsgBuf
, 0, NULL
) > 0)
875 errorstr
= (LPCTSTR
)lpMsgBuf
;
880 errorstr
= "FormatMessage can't get the message";
883 throw Exception ("InternetCloseHandle() failed on file '%s': %s (ec %d) (error code 40)", source
.c_str (), errorstr
.c_str(), errcode
);
888 void setState (bool info
, bool log
, const char *format
, ...)
891 NLMISC_CONVERT_VARGS (str
, format
, 256);
892 if (info
&& VerboseLog
)
898 StateLog
+= LogSeparator
;
906 string ServerVersion
;
911 friend int myProgressFunc(void *foo
, double t
, double d
, double ultotal
, double ulnow
);
914 uint TotalFilesToGet
;
915 uint TotalBytesToGet
;
916 uint CurrentFilesToGet
;
917 uint CurrentBytesToGet
;
920 CPatchThread
*PatchThread
= NULL
;
921 IThread
*thread
= NULL
;
923 int myProgressFunc(void *foo
, double t
, double d
, double ultotal
, double ulnow
)
925 double pour1
= t
!=0.0?d
*100.0/t
:0.0;
926 double pour2
= ultotal
!=0.0?ulnow
*100.0/ultotal
:0.0;
927 //changeSplash("Progression : dl %s / %s (%5.02f %%) ul %s / %s (%5.02f %%)\n", NLMISC::bytesToHumanReadable((uint32)d).c_str(), NLMISC::bytesToHumanReadable((uint32)t).c_str(), pour1, NLMISC::bytesToHumanReadable((uint32)ulnow).c_str(), NLMISC::bytesToHumanReadable((uint32)ultotal).c_str(), pour2);
929 PatchThread
->setState(false, false, "Getting file %s : %s / %s (%5.02f %%)", currentFile
.c_str(), NLMISC::bytesToHumanReadable((uint32
)d
).c_str(), NLMISC::bytesToHumanReadable((uint32
)t
).c_str(), pour1
);
933 void startPatchThread (const std::string
&serverPath
, const std::string
&serverVersion
, const std::string
&urlOk
, const std::string
&urlFailed
, const std::string
&logSeparator
)
935 if (PatchThread
!= NULL
)
937 nlwarning ("patch thread already running");
941 PatchThread
= new CPatchThread (serverPath
, serverVersion
, urlOk
, urlFailed
, logSeparator
);
942 nlassert (PatchThread
!= NULL
);
944 thread
= IThread::create (PatchThread
);
945 nlassert (thread
!= NULL
);
949 bool patchEnded (string
&url
, bool &ok
)
951 nlassert (PatchThread
!= NULL
);
953 bool end
= PatchThread
->Ended
;
956 url
= PatchThread
->Url
;
957 ok
= PatchThread
->PatchOk
;
966 bool patchState (string
&state
, std::string
&stateLog
)
968 if (PatchThread
== NULL
)
971 bool statechanged
= PatchThread
->StateChanged
;
974 state
= PatchThread
->State
;
975 stateLog
= PatchThread
->StateLog
;
976 PatchThread
->StateChanged
= false;
982 int getTotalFilesToGet()
985 return PatchThread
->TotalFilesToGet
;
989 int getCurrentFilesToGet()
992 return PatchThread
->CurrentFilesToGet
;
996 int getTotalBytesToGet()
999 return PatchThread
->TotalBytesToGet
;
1003 int getCurrentBytesToGet()
1006 return PatchThread
->CurrentBytesToGet
;
1012 if(PatchThread
&& thread
)
1014 thread
->terminate();