72717573b0b9b39303a0d8f4d707891ac350c5b1
1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_res_downloader
;
20 uses sysutils
, Classes
, md5
, g_net
, g_netmsg
, g_console
, g_main
, e_log
;
22 function g_Res_SearchSameWAD(const path
, filename
: AnsiString; const resMd5
: TMD5Digest
): AnsiString;
24 // download map wad from server (if necessary)
25 // download all required map resource wads too
26 // returns name of the map wad (relative to mapdir), or empty string on error
27 function g_Res_DownloadMapWAD (FileName
: AnsiString; const mapHash
: TMD5Digest
): AnsiString;
29 // call this before downloading a new map from a server
30 procedure g_Res_ClearReplacementWads ();
31 // returns original name, or replacement name
32 function g_Res_FindReplacementWad (oldname
: AnsiString): AnsiString;
33 procedure g_Res_PutReplacementWad (oldname
: AnsiString; newDiskName
: AnsiString);
38 uses g_language
, sfs
, utils
, wadreader
, g_game
, hashtable
;
40 const DOWNLOAD_DIR
= 'downloads';
44 diskName
: AnsiString; // lowercased
45 baseName
: AnsiString; // lowercased
48 nextBaseNameIndex
: Integer;
52 knownFiles
: array of TFileInfo
;
53 knownHash
: THashStrInt
= nil; // key: base name; value: index
54 scannedDirs
: THashStrInt
= nil; // key: lowercased dir name
55 replacements
: THashStrStr
= nil;
58 function findKnownFile (diskName
: AnsiString): Integer;
64 if not assigned(knownHash
) then exit
;
65 if (length(diskName
) = 0) then exit
;
66 baseName
:= toLowerCase1251(ExtractFileName(diskName
));
67 if (not knownHash
.get(baseName
, idx
)) then exit
;
68 if (idx
< 0) or (idx
>= length(knownFiles
)) then raise Exception
.Create('wutafuck?');
71 if (strEquCI1251(knownFiles
[idx
].diskName
, diskName
)) then begin result
:= idx
; exit
; end; // i found her!
72 idx
:= knownFiles
[idx
].nextBaseNameIndex
;
77 function addKnownFile (diskName
: AnsiString): Integer;
80 lastIdx
: Integer = -1;
85 if not assigned(knownHash
) then knownHash
:= THashStrInt
.Create();
86 if (length(diskName
) = 0) then exit
;
87 baseName
:= toLowerCase1251(ExtractFileName(diskName
));
88 if (length(baseName
) = 0) then exit
;
89 // check if we already have this file
90 if (knownHash
.get(baseName
, idx
)) then
92 if (idx
< 0) or (idx
>= length(knownFiles
)) then raise Exception
.Create('wutafuck?');
95 if (strEquCI1251(knownFiles
[idx
].diskName
, diskName
)) then
102 idx
:= knownFiles
[idx
].nextBaseNameIndex
;
105 // this file is not there, append it
106 idx
:= length(knownFiles
);
108 SetLength(knownFiles
, idx
+1); // sorry
109 fi
:= @knownFiles
[idx
];
110 fi
.diskName
:= diskName
;
111 fi
.baseName
:= baseName
;
112 fi
.md5valid
:= false;
113 fi
.nextBaseNameIndex
:= -1;
114 if (lastIdx
< 0) then
117 knownHash
.put(baseName
, idx
);
121 knownFiles
[lastIdx
].nextBaseNameIndex
:= idx
;
126 function getKnownFileWithMD5 (diskDir
: AnsiString; baseName
: AnsiString; const md5
: TMD5Digest
): AnsiString;
131 if not assigned(knownHash
) then exit
;
132 if (not knownHash
.get(toLowerCase1251(baseName
), idx
)) then exit
;
133 if (idx
< 0) or (idx
>= length(knownFiles
)) then raise Exception
.Create('wutafuck?');
136 if (strEquCI1251(knownFiles
[idx
].diskName
, IncludeTrailingPathDelimiter(diskDir
)+baseName
)) then
138 if (not knownFiles
[idx
].md5valid
) then
140 knownFiles
[idx
].md5
:= MD5File(knownFiles
[idx
].diskName
);
141 knownFiles
[idx
].md5valid
:= true;
143 if (MD5Match(knownFiles
[idx
].md5
, md5
)) then
145 result
:= knownFiles
[idx
].diskName
;
149 idx
:= knownFiles
[idx
].nextBaseNameIndex
;
154 // call this before downloading a new map from a server
155 procedure g_Res_ClearReplacementWads ();
157 if assigned(replacements
) then replacements
.clear();
158 e_LogWriteln('cleared replacement wads');
162 // returns original name, or replacement name
163 function g_Res_FindReplacementWad (oldname
: AnsiString): AnsiString;
168 if not assigned(replacements
) then exit
;
169 if (replacements
.get(toLowerCase1251(ExtractFileName(oldname
)), fn
)) then result
:= fn
;
173 procedure g_Res_PutReplacementWad (oldname
: AnsiString; newDiskName
: AnsiString);
175 e_LogWritefln('adding replacement wad: oldname=%s; newname=%s', [oldname
, newDiskName
]);
176 if not assigned(replacements
) then replacements
:= THashStrStr
.Create();
177 replacements
.put(toLowerCase1251(oldname
), newDiskName
);
181 procedure scanDir (const dirName
: AnsiString; calcMD5
: Boolean);
183 searchResult
: TSearchRec
;
187 if not assigned(scannedDirs
) then scannedDirs
:= THashStrInt
.Create();
188 dfn
:= toLowerCase1251(IncludeTrailingPathDelimiter(dirName
));
189 if scannedDirs
.has(dfn
) then exit
;
190 scannedDirs
.put(dfn
, 42);
192 if (FindFirst(dirName
+'/*', faAnyFile
, searchResult
) <> 0) then exit
;
195 if (searchResult
.Attr
and faDirectory
) = 0 then
197 dfn
:= dirName
+'/'+searchResult
.Name
;
198 idx
:= addKnownFile(dfn
);
199 if (calcMD5
) and (idx
>= 0) then
201 if (not knownFiles
[idx
].md5valid
) then
203 knownFiles
[idx
].md5
:= MD5File(knownFiles
[idx
].diskName
);
204 knownFiles
[idx
].md5valid
:= true;
208 else if (searchResult
.Name
<> '.') and (searchResult
.Name
<> '..') then
210 scanDir(IncludeTrailingPathDelimiter(dirName
)+searchResult
.Name
, calcMD5
);
212 until (FindNext(searchResult
) <> 0);
214 FindClose(searchResult
);
219 function CompareFileHash(const filename
: AnsiString; const resMd5
: TMD5Digest
): Boolean;
221 gResHash
: TMD5Digest
;
224 fname
:= findDiskWad(filename
);
225 if length(fname
) = 0 then begin result
:= false; exit
; end;
226 gResHash
:= MD5File(fname
);
227 Result
:= MD5Match(gResHash
, resMd5
);
230 function CheckFileHash(const path
, filename
: AnsiString; const resMd5
: TMD5Digest
): Boolean;
234 fname
:= findDiskWad(path
+filename
);
235 if length(fname
) = 0 then begin result
:= false; exit
; end;
236 Result
:= FileExists(fname
) and CompareFileHash(fname
, resMd5
);
240 function g_Res_SearchResWad (asMap
: Boolean; fname
: AnsiString; const resMd5
: TMD5Digest
): AnsiString;
245 //if not assigned(scannedDirs) then scannedDirs := THashStrInt.Create();
248 if CheckFileHash(GameDir
+'/maps', fname
, resMd5
) then
250 result
:= findDiskWad(GameDir
+'/maps/'+fname
);
251 if (length(result
) <> 0) then exit
;
253 scanDir(GameDir
+'/maps/downloads', true);
257 if CheckFileHash(GameDir
+'/wads', fname
, resMd5
) then
259 result
:= findDiskWad(GameDir
+'/wads/'+fname
);
260 if (length(result
) <> 0) then exit
;
262 scanDir(GameDir
+'/wads/downloads', true);
264 for f
:= Low(knownFiles
) to High(knownFiles
) do
266 if (not knownFiles
[f
].md5valid
) then continue
;
267 if (MD5Match(knownFiles
[f
].md5
, resMd5
)) then
269 result
:= knownFiles
[f
].diskName
;
273 //resStream := createDiskFile(GameDir+'/wads/'+mapData.ExternalResources[i].Name);
277 function g_Res_SearchSameWAD (const path
, filename
: AnsiString; const resMd5
: TMD5Digest
): AnsiString;
279 scanDir(path
, false);
280 result
:= getKnownFileWithMD5(path
, filename
, resMd5
);
284 function g_Res_DownloadMapWAD (FileName
: AnsiString; const mapHash
: TMD5Digest
): AnsiString;
286 tf
: TNetFileTransfer
;
287 resList
: TStringList
;
295 //SetLength(mapData.ExternalResources, 0);
297 g_Res_ClearReplacementWads();
298 g_Res_received_map_start
:= false;
301 CreateDir(GameDir
+'/maps/downloads');
306 CreateDir(GameDir
+'/wads/downloads');
310 resList
:= TStringList
.Create();
313 g_Console_Add(Format(_lc
[I_NET_MAP_DL
], [FileName
]));
314 e_WriteLog('Downloading map `' + FileName
+ '` from server', TMsgType
.Notify
);
315 g_Game_SetLoadingText(FileName
+ '...', 0, False);
316 //MC_SEND_MapRequest();
317 if (not g_Net_SendMapRequest()) then exit
;
319 FileName
:= ExtractFileName(FileName
);
320 if (length(FileName
) = 0) then FileName
:= 'fucked_map_wad.wad';
321 res
:= g_Net_Wait_MapInfo(tf
, resList
);
322 if (res
<> 0) then exit
;
324 // find or download a map
325 result
:= g_Res_SearchResWad(true{asMap}, tf
.diskName
, mapHash
);
326 if (length(result
) = 0) then
329 res
:= g_Net_RequestResFileInfo(-1{map}, tf
);
332 e_LogWriteln('error requesting map wad');
336 fname
:= GameDir
+'/maps/downloads/'+FileName
;
338 strm
:= createDiskFile(fname
);
340 e_WriteLog('cannot create map file `'+FileName
+'`', TMsgType
.Fatal
);
344 tf
.diskName
:= fname
;
346 res
:= g_Net_ReceiveResourceFile(-1{map}, tf
, strm
);
348 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
356 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
360 mmd5
:= MD5File(fname
);
361 if (not MD5Match(mmd5
, mapHash
)) then
363 e_WriteLog('error downloading map file `'+FileName
+'` (bad hash)', TMsgType
.Fatal
);
367 idx
:= addKnownFile(fname
);
370 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
374 knownFiles
[idx
].md5
:= mmd5
;
375 knownFiles
[idx
].md5valid
:= true;
379 // download resources
380 for f
:= 0 to resList
.Count
-1 do
382 res
:= g_Net_RequestResFileInfo(f
, tf
);
383 if (res
<> 0) then begin result
:= ''; exit
; end;
384 wadname
:= g_Res_SearchResWad(false{asMap}, tf
.diskName
, tf
.hash
);
385 if (length(wadname
) <> 0) then
388 g_Net_AbortResTransfer(tf
);
389 g_Res_PutReplacementWad(tf
.diskName
, wadname
);
393 fname
:= GameDir
+'/wads/downloads/'+tf
.diskName
;
395 strm
:= createDiskFile(fname
);
397 e_WriteLog('cannot create resource file `'+fname
+'`', TMsgType
.Fatal
);
402 res
:= g_Net_ReceiveResourceFile(f
, tf
, strm
);
404 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
412 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
416 idx
:= addKnownFile(fname
);
419 e_WriteLog('error downloading map file `'+FileName
+'`', TMsgType
.Fatal
);
423 knownFiles
[idx
].md5
:= tf
.hash
;
424 knownFiles
[idx
].md5valid
:= true;
425 g_Res_PutReplacementWad(tf
.diskName
, fname
);