X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_res_downloader.pas;h=b2844c651539202b622fdcdea8e46fd063b60652;hb=651f063582ce4ebc8eb1b36025d7553675005995;hp=9547335009a7296361e17b018196f7e6846c3278;hpb=dfd3e97bffa213f5b21206b8a292b80180bab948;p=d2df-sdl.git diff --git a/src/game/g_res_downloader.pas b/src/game/g_res_downloader.pas index 9547335..b2844c6 100644 --- a/src/game/g_res_downloader.pas +++ b/src/game/g_res_downloader.pas @@ -1,152 +1,345 @@ +(* Copyright (C) Doom 2D: Forever Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License ONLY. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *) +{$INCLUDE ../shared/a_modes.inc} unit g_res_downloader; interface uses sysutils, Classes, md5, g_net, g_netmsg, g_console, g_main, e_log; -function g_Res_SearchSameWAD(const path, filename: string; const resMd5: TMD5Digest): string; -function g_Res_DownloadWAD(const FileName: string): string; +function g_Res_SearchSameWAD(const path, filename: AnsiString; const resMd5: TMD5Digest): AnsiString; + +// download map wad from server (if necessary) +// download all required map resource wads too +// returns name of the map wad (relative to mapdir), or empty string on error +function g_Res_DownloadMapWAD (FileName: AnsiString; const mapHash: TMD5Digest): AnsiString; + +// call this before downloading a new map from a server +procedure g_Res_ClearReplacementWads (); +// returns original name, or replacement name +function g_Res_FindReplacementWad (oldname: AnsiString): AnsiString; +procedure g_Res_PutReplacementWad (oldname: AnsiString; newDiskName: AnsiString); + implementation -uses g_language; +uses g_language, sfs, utils, wadreader, g_game, hashtable; -const DOWNLOAD_DIR = 'downloads'; +//const DOWNLOAD_DIR = 'downloads'; -procedure FindFiles(const dirName, filename: string; var files: TStringList); var - searchResult: TSearchRec; + replacements: THashStrStr = nil; + + +// call this before downloading a new map from a server +procedure g_Res_ClearReplacementWads (); begin - if FindFirst(dirName+'/*', faAnyFile, searchResult) = 0 then - begin - try - repeat - if (searchResult.Attr and faDirectory) = 0 then - begin - if searchResult.Name = filename then - begin - files.Add(dirName+'/'+filename); - Exit; - end; - end - else if (searchResult.Name <> '.') and (searchResult.Name <> '..') then - FindFiles(IncludeTrailingPathDelimiter(dirName)+searchResult.Name, - filename, files); - until FindNext(searchResult) <> 0; - finally - FindClose(searchResult); - end; - end; + if assigned(replacements) then replacements.clear(); + e_LogWriteln('cleared replacement wads'); end; -function CompareFileHash(const filename: string; const resMd5: TMD5Digest): Boolean; + +// returns original name, or replacement name +function g_Res_FindReplacementWad (oldname: AnsiString): AnsiString; var - gResHash: TMD5Digest; + fn: AnsiString; begin - gResHash := MD5File(filename); - Result := MD5Match(gResHash, resMd5); + result := oldname; + if not assigned(replacements) then exit; + if (replacements.get(toLowerCase1251(ExtractFileName(oldname)), fn)) then result := fn; end; -function CheckFileHash(const path, filename: string; const resMd5: TMD5Digest): Boolean; + +procedure g_Res_PutReplacementWad (oldname: AnsiString; newDiskName: AnsiString); begin - Result := FileExists(path + filename) and CompareFileHash(path + filename, resMd5); + e_LogWritefln('adding replacement wad: oldname=%s; newname=%s', [oldname, newDiskName]); + if not assigned(replacements) then replacements := THashStrStr.Create(); + replacements.put(toLowerCase1251(oldname), newDiskName); end; -function g_Res_SearchSameWAD(const path, filename: string; const resMd5: TMD5Digest): string; + +function scanDir (dirName: AnsiString; baseName: AnsiString; const resMd5: TMD5Digest): AnsiString; var - res: string; - files: TStringList; - i: Integer; + searchResult: TSearchRec; + dfn: AnsiString; + md5: TMD5Digest; + dirs: array of AnsiString; + f: Integer; begin - Result := ''; + result := ''; + SetLength(dirs, 0); + if (length(baseName) = 0) then exit; + dirName := IncludeTrailingPathDelimiter(dirName); + e_LogWritefln('scanning dir `%s` for file `%s`...', [dirName, baseName]); + + // scan files + if (FindFirst(dirName+'*', faAnyFile, searchResult) <> 0) then exit; + try + repeat + if ((searchResult.Attr and faDirectory) = 0) then + begin + if (isWadNamesEqu(searchResult.Name, baseName)) then + begin + dfn := dirName+searchResult.Name; + if FileExists(dfn) then + begin + e_LogWritefln(' found `%s`...', [dfn]); + md5 := MD5File(dfn); + if MD5Match(md5, resMd5) then + begin + e_LogWritefln(' MATCH `%s`...', [dfn]); + SetLength(dirs, 0); + result := dfn; + exit; + end; + end; + end; + end + else + begin + if (searchResult.Name <> '.') and (searchResult.Name <> '..') then + begin + dfn := dirName+searchResult.Name; + SetLength(dirs, Length(dirs)+1); + dirs[length(dirs)-1] := dfn; + end; + end; + until (FindNext(searchResult) <> 0); + finally + FindClose(searchResult); + end; - if CheckFileHash(path, filename, resMd5) then + // scan subdirs + for f := 0 to High(dirs) do begin - Result := path + filename; - Exit; + dfn := dirs[f]; + result := scanDir(dfn, baseName, resMd5); + if (length(result) <> 0) then begin SetLength(dirs, 0); exit; end; end; + SetLength(dirs, 0); +end; - files := TStringList.Create; - FindFiles(path, filename, files); - for i := 0 to files.Count - 1 do +function g_Res_SearchResWad (asMap: Boolean; fname: AnsiString; const resMd5: TMD5Digest): AnsiString; +begin + result := ''; + //if not assigned(scannedDirs) then scannedDirs := THashStrInt.Create(); + if (asMap) then begin - res := files.Strings[i]; - if CompareFileHash(res, resMd5) then - begin - Result := res; - Break; - end; + result := scanDir(GameDir+'/maps', ExtractFileName(fname), resMd5); + end + else + begin + result := scanDir(GameDir+'/wads', ExtractFileName(fname), resMd5); end; - - files.Free; end; -function SaveWAD(const path, filename: string; const data: array of Byte): string; -var - resFile: TFileStream; + +function g_Res_SearchSameWAD (const path, filename: AnsiString; const resMd5: TMD5Digest): AnsiString; begin - try - Result := path + DOWNLOAD_DIR + '/' + filename; - if not DirectoryExists(path + DOWNLOAD_DIR) then - begin - CreateDir(path + DOWNLOAD_DIR); - end; - resFile := TFileStream.Create(Result, fmCreate); - resFile.WriteBuffer(data[0], Length(data)); - resFile.Free - except - Result := ''; - end; + result := scanDir(path, filename, resMd5); end; -function g_Res_DownloadWAD(const FileName: string): string; + +function g_Res_DownloadMapWAD (FileName: AnsiString; const mapHash: TMD5Digest): AnsiString; var - msgStream: TMemoryStream; - resStream: TFileStream; - mapData: TMapDataMsg; - i: Integer; - resData: TResDataMsg; + tf: TNetFileTransfer; + resList: TStringList; + f, res: Integer; + strm: TStream; + fname: AnsiString; + wadname: AnsiString; + md5: TMD5Digest; begin - SetLength(mapData.ExternalResources, 0); - g_Console_Add(Format(_lc[I_NET_MAP_DL], [FileName])); - e_WriteLog('Downloading map `' + FileName + '` from server', MSG_NOTIFY); - MC_SEND_MapRequest(); + //SetLength(mapData.ExternalResources, 0); + result := ''; + g_Res_ClearReplacementWads(); + g_Res_received_map_start := false; - msgStream := g_Net_Wait_Event(NET_MSG_MAP_RESPONSE); - if msgStream <> nil then - begin - mapData := MapDataFromMsgStream(msgStream); - msgStream.Free; - end; + resList := TStringList.Create(); - for i := 0 to High(mapData.ExternalResources) do - begin - if not CheckFileHash(GameDir + '/wads/', - mapData.ExternalResources[i].Name, - mapData.ExternalResources[i].md5) then + try + g_Console_Add(Format(_lc[I_NET_MAP_DL], [FileName])); + e_WriteLog('Downloading map `' + FileName + '` from server', TMsgType.Notify); + g_Game_SetLoadingText(FileName + '...', 0, False); + //MC_SEND_MapRequest(); + if (not g_Net_SendMapRequest()) then exit; + + FileName := ExtractFileName(FileName); + if (length(FileName) = 0) then FileName := 'fucked_map_wad.wad'; + res := g_Net_Wait_MapInfo(tf, resList); + if (res <> 0) then exit; + + // find or download a map + result := g_Res_SearchResWad(true{asMap}, tf.diskName, mapHash); + if (length(result) = 0) then begin - g_Console_Add(Format(_lc[I_NET_WAD_DL], - [mapData.ExternalResources[i].Name])); - e_WriteLog('Downloading Wad `' + mapData.ExternalResources[i].Name + - '` from server', MSG_NOTIFY); - MC_SEND_ResRequest(mapData.ExternalResources[i].Name); - - msgStream := g_Net_Wait_Event(NET_MSG_RES_RESPONSE); - resData := ResDataFromMsgStream(msgStream); - - resStream := TFileStream.Create(GameDir+'/wads/'+ - mapData.ExternalResources[i].Name, - fmCreate); - resStream.WriteBuffer(resData.FileData[0], resData.FileSize); - - resData.FileData := nil; - resStream.Free; - msgStream.Free; + // download map + res := g_Net_RequestResFileInfo(-1{map}, tf); + if (res <> 0) then + begin + e_LogWriteln('error requesting map wad'); + result := ''; + exit; + end; + try + CreateDir(GameDir+'/maps/downloads'); + except + end; + fname := GameDir+'/maps/downloads/'+FileName; + try + strm := openDiskFileRW(fname); + except + e_WriteLog('cannot create map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + tf.diskName := fname; + try + res := g_Net_ReceiveResourceFile(-1{map}, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + // if it was resumed, check md5 and initiate full download if necessary + if tf.resumed then + begin + md5 := MD5File(fname); + // sorry for pasta, i am asshole + if not MD5Match(md5, tf.hash) then + begin + e_LogWritefln('resuming failed; downloading map `%s` from scratch...', [fname]); + try + DeleteFile(fname); + strm := createDiskFile(fname); + except + e_WriteLog('cannot create map file `'+fname+'`', TMsgType.Fatal); + result := ''; + exit; + end; + try + res := g_Net_ReceiveResourceFile(-1{map}, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + end; + end; + result := fname; end; - end; - Result := SaveWAD(MapsDir, ExtractFileName(FileName), mapData.FileData); + // download resources + for f := 0 to resList.Count-1 do + begin + res := g_Net_RequestResFileInfo(f, tf); + if (res <> 0) then begin result := ''; exit; end; + wadname := g_Res_SearchResWad(false{asMap}, tf.diskName, tf.hash); + if (length(wadname) <> 0) then + begin + // already here + g_Net_AbortResTransfer(tf); + g_Res_PutReplacementWad(tf.diskName, wadname); + end + else + begin + try + CreateDir(GameDir+'/wads/downloads'); + except + end; + fname := GameDir+'/wads/downloads/'+tf.diskName; + e_LogWritefln('downloading resource `%s` to `%s`...', [tf.diskName, fname]); + try + strm := openDiskFileRW(fname); + except + e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal); + result := ''; + exit; + end; + try + res := g_Net_ReceiveResourceFile(f, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + // if it was resumed, check md5 and initiate full download if necessary + if tf.resumed then + begin + md5 := MD5File(fname); + // sorry for pasta, i am asshole + if not MD5Match(md5, tf.hash) then + begin + e_LogWritefln('resuming failed; downloading resource `%s` to `%s` from scratch...', [tf.diskName, fname]); + try + DeleteFile(fname); + strm := createDiskFile(fname); + except + e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal); + result := ''; + exit; + end; + try + res := g_Net_ReceiveResourceFile(f, tf, strm); + except + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + strm.Free; + result := ''; + exit; + end; + strm.Free; + if (res <> 0) then + begin + e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal); + result := ''; + exit; + end; + end; + end; + g_Res_PutReplacementWad(tf.diskName, fname); + end; + end; + finally + resList.Free; + end; end; + end.