index ed1ea3bdf3feb7c77de12529d99b0b146c67d94b..fd2d90b9d4d541c4275dc644a211c0e15b6f4c35 100644 (file)
uses sysutils, Classes, md5, g_net, g_netmsg, g_console, g_main, e_log;
-function g_Res_SearchSameWAD(const path, filename: AnsiString; const resMd5: TMD5Digest): AnsiString;
-function g_Res_SearchResWad (asMap: Boolean; const resMd5: TMD5Digest): AnsiString;
// download map wad from server (if necessary)
// download all required map resource wads too
+// registers all required replacement wads
// 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, sfs, utils, wadreader, g_game, hashtable;
-const DOWNLOAD_DIR = 'downloads';
-
-type
- TFileInfo = record
- diskName: AnsiString; // lowercased
- baseName: AnsiString; // lowercased
- md5: TMD5Digest;
- md5valid: Boolean;
- nextBaseNameIndex: Integer;
- end;
-
var
- knownFiles: array of TFileInfo;
- knownHash: THashStrInt = nil; // key: base name; value: index
- scannedDirs: THashStrInt = nil; // key: lowercased dir name
+ // cvars
+ g_res_ignore_names: AnsiString = 'standart;shrshade';
+ g_res_ignore_enabled: Boolean = true;
+ // other vars
replacements: THashStrStr = nil;
-function findKnownFile (diskName: AnsiString): Integer;
+//==========================================================================
+//
+// getWord
+//
+// get next word from a string
+// words are delimited with ';'
+// ignores leading and trailing spaces
+// returns empty string if there are no more words
+//
+//==========================================================================
+function getWord (var list: AnsiString): AnsiString;
var
- idx: Integer;
- baseName: AnsiString;
+ pos: Integer;
begin
- result := -1;
- if not assigned(knownHash) then exit;
- if (length(diskName) = 0) then exit;
- baseName := toLowerCase1251(ExtractFileName(diskName));
- if (not knownHash.get(baseName, idx)) then exit;
- if (idx < 0) or (idx >= length(knownFiles)) then raise Exception.Create('wutafuck?');
- while (idx >= 0) do
+ result := '';
+ while (length(list) > 0) do
begin
- if (strEquCI1251(knownFiles[idx].diskName, diskName)) then begin result := idx; exit; end; // i found her!
- idx := knownFiles[idx].nextBaseNameIndex;
+ if (ord(list[1]) <= 32) or (list[1] = ';') or (list[1] = ':') then begin Delete(list, 1, 1); continue; end;
+ pos := 1;
+ while (pos <= length(list)) and (list[pos] <> ';') and (list[pos] <> ':') do Inc(pos);
+ result := Copy(list, 1, pos-1);
+ Delete(list, 1, pos);
+ while (length(result) > 0) and (ord(result[length(result)]) <= 32) do Delete(result, length(result), 1);
+ if (length(result) > 0) then exit;
end;
end;
-function addKnownFile (diskName: AnsiString): Integer;
+//==========================================================================
+//
+// isIgnoredResWad
+//
+// checks if the given resource wad can be ignored
+//
+// FIXME: preparse name list?
+//
+//==========================================================================
+function isIgnoredResWad (fname: AnsiString): Boolean;
var
- idx: Integer;
- lastIdx: Integer = -1;
- baseName: AnsiString;
- fi: ^TFileInfo;
+ list: AnsiString;
+ name: AnsiString;
begin
- result := -1;
- if not assigned(knownHash) then knownHash := THashStrInt.Create();
- if (length(diskName) = 0) then exit;
- baseName := toLowerCase1251(ExtractFileName(diskName));
- if (length(baseName) = 0) then exit;
- // check if we already have this file
- if (knownHash.get(baseName, idx)) then
- begin
- if (idx < 0) or (idx >= length(knownFiles)) then raise Exception.Create('wutafuck?');
- while (idx >= 0) do
- begin
- if (strEquCI1251(knownFiles[idx].diskName, diskName)) then
- begin
- // already here
- result := idx;
- exit;
- end;
- lastIdx := idx;
- idx := knownFiles[idx].nextBaseNameIndex;
- end;
- end;
- // this file is not there, append it
- idx := length(knownFiles);
- result := idx;
- SetLength(knownFiles, idx+1); // sorry
- fi := @knownFiles[idx];
- fi.diskName := diskName;
- fi.baseName := baseName;
- fi.md5valid := false;
- fi.nextBaseNameIndex := -1;
- if (lastIdx < 0) then
+ result := false;
+ if (not g_res_ignore_enabled) then exit;
+ fname := forceFilenameExt(ExtractFileName(fname), '');
+ list := g_res_ignore_names;
+ name := getWord(list);
+ while (length(name) > 0) do
begin
- // totally new one
- knownHash.put(baseName, idx);
- end
- else
- begin
- knownFiles[lastIdx].nextBaseNameIndex := idx;
+ name := forceFilenameExt(name, '');
+ //writeln('*** name=[', name, ']; fname=[', fname, ']');
+ if (StrEquCI1251(name, fname)) then begin result := true; exit; end;
+ name := getWord(list);
end;
end;
-function getKnownFileWithMD5 (diskDir: AnsiString; baseName: AnsiString; const md5: TMD5Digest): AnsiString;
-var
- idx: Integer;
+//==========================================================================
+//
+// clearReplacementWads
+//
+// call this before downloading a new map from a server
+//
+//==========================================================================
+procedure clearReplacementWads ();
begin
- result := '';
- if not assigned(knownHash) then exit;
- if (not knownHash.get(toLowerCase1251(baseName), idx)) then exit;
- if (idx < 0) or (idx >= length(knownFiles)) then raise Exception.Create('wutafuck?');
- while (idx >= 0) do
- begin
- if (strEquCI1251(knownFiles[idx].diskName, IncludeTrailingPathDelimiter(diskDir)+baseName)) then
- begin
- if (not knownFiles[idx].md5valid) then
- begin
- knownFiles[idx].md5 := MD5File(knownFiles[idx].diskName);
- knownFiles[idx].md5valid := true;
- end;
- if (MD5Match(knownFiles[idx].md5, md5)) then
- begin
- result := knownFiles[idx].diskName;
- exit;
- end;
- end;
- idx := knownFiles[idx].nextBaseNameIndex;
- end;
+ if assigned(replacements) then replacements.clear();
+ e_LogWriteln('cleared replacement wads');
end;
-// call this before downloading a new map from a server
-procedure g_Res_ClearReplacementWads ();
+//==========================================================================
+//
+// addReplacementWad
+//
+// register new replacement wad
+//
+//==========================================================================
+procedure addReplacementWad (oldname: AnsiString; newDiskName: AnsiString);
begin
- if assigned(replacements) then replacements.clear();
- e_LogWriteln('cleared replacement wads');
+ 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;
-// returns original name, or replacement name
+//==========================================================================
+//
+// g_Res_FindReplacementWad
+//
+// returns original name, or replacement name
+//
+//==========================================================================
function g_Res_FindReplacementWad (oldname: AnsiString): AnsiString;
var
fn: AnsiString;
begin
+ //e_LogWritefln('LOOKING for replacement wad for [%s]...', [oldname], TMsgType.Notify);
result := oldname;
if not assigned(replacements) then exit;
- if (replacements.get(toLowerCase1251(ExtractFileName(oldname)), fn)) then result := fn;
-end;
-
-
-procedure g_Res_PutReplacementWad (oldname: AnsiString; newDiskName: AnsiString);
-begin
- e_LogWritefln('adding replacement wad: oldname=%s; newname=%s', [oldname, newDiskName]);
- replacements.put(toLowerCase1251(oldname), newDiskName);
+ if (replacements.get(toLowerCase1251(ExtractFileName(oldname)), fn)) then
+ begin
+ //e_LogWritefln('found replacement wad for [%s] -> [%s]', [oldname, fn], TMsgType.Notify);
+ result := fn;
+ end;
end;
-procedure scanDir (const dirName: AnsiString; calcMD5: Boolean);
+//==========================================================================
+//
+// scanDir
+//
+// look for a wad to match the hash
+// scans subdirs, ignores known wad extensions
+//
+// returns found wad disk name, or empty string
+//
+//==========================================================================
+function scanDir (dirName: AnsiString; baseName: AnsiString; const resMd5: TMD5Digest): AnsiString;
var
searchResult: TSearchRec;
dfn: AnsiString;
- idx: Integer;
+ md5: TMD5Digest;
+ dirs: array of AnsiString;
+ f: Integer;
begin
- if not assigned(scannedDirs) then scannedDirs := THashStrInt.Create();
- dfn := toLowerCase1251(IncludeTrailingPathDelimiter(dirName));
- if scannedDirs.has(dfn) then exit;
- scannedDirs.put(dfn, 42);
+ result := '';
+ SetLength(dirs, 0);
+ if (length(baseName) = 0) then exit;
+ dirName := IncludeTrailingPathDelimiter(dirName);
+ e_LogWritefln('scanning dir `%s` for file `%s`...', [dirName, baseName]);
- if (FindFirst(dirName+'/*', faAnyFile, searchResult) <> 0) then exit;
+ // scan files
+ if (FindFirst(dirName+'*', faAnyFile, searchResult) <> 0) then exit;
try
repeat
- if (searchResult.Attr and faDirectory) = 0 then
+ if ((searchResult.Attr and faDirectory) = 0) then
begin
- dfn := dirName+'/'+searchResult.Name;
- idx := addKnownFile(dfn);
- if (calcMD5) and (idx >= 0) then
+ if (isWadNamesEqu(searchResult.Name, baseName)) then
begin
- if (not knownFiles[idx].md5valid) then
+ dfn := dirName+searchResult.Name;
+ if FileExists(dfn) then
begin
- knownFiles[idx].md5 := MD5File(knownFiles[idx].diskName);
- knownFiles[idx].md5valid := true;
+ 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 if (searchResult.Name <> '.') and (searchResult.Name <> '..') then
+ else
begin
- scanDir(IncludeTrailingPathDelimiter(dirName)+searchResult.Name, calcMD5);
+ 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;
-end;
-
-
-function CompareFileHash(const filename: AnsiString; const resMd5: TMD5Digest): Boolean;
-var
- gResHash: TMD5Digest;
- fname: AnsiString;
-begin
- fname := findDiskWad(filename);
- if length(fname) = 0 then begin result := false; exit; end;
- gResHash := MD5File(fname);
- Result := MD5Match(gResHash, resMd5);
-end;
-function CheckFileHash(const path, filename: AnsiString; const resMd5: TMD5Digest): Boolean;
-var
- fname: AnsiString;
-begin
- fname := findDiskWad(path+filename);
- if length(fname) = 0 then begin result := false; exit; end;
- Result := FileExists(fname) and CompareFileHash(fname, resMd5);
+ // scan subdirs
+ for f := 0 to High(dirs) do
+ begin
+ dfn := dirs[f];
+ result := scanDir(dfn, baseName, resMd5);
+ if (length(result) <> 0) then begin SetLength(dirs, 0); exit; end;
+ end;
+ SetLength(dirs, 0);
end;
-function g_Res_SearchResWad (asMap: Boolean; const resMd5: TMD5Digest): AnsiString;
-var
- f: Integer;
+//==========================================================================
+//
+// findExistingMapWadWithHash
+//
+// find map or resource wad using its base name and hash
+//
+// returns found wad disk name, or empty string
+//
+//==========================================================================
+function findExistingMapWadWithHash (fname: AnsiString; const resMd5: TMD5Digest): AnsiString;
begin
- result := '';
- //if not assigned(scannedDirs) then scannedDirs := THashStrInt.Create();
- if (asMap) then
- begin
- scanDir(GameDir+'/maps/downloads', true);
- end
- else
- begin
- scanDir(GameDir+'/wads/downloads', true);
- end;
- for f := Low(knownFiles) to High(knownFiles) do
- begin
- if (not knownFiles[f].md5valid) then continue;
- if (MD5Match(knownFiles[f].md5, resMd5)) then
- begin
- result := knownFiles[f].diskName;
- exit;
- end;
- end;
- //resStream := createDiskFile(GameDir+'/wads/'+mapData.ExternalResources[i].Name);
+ result := scanDir(GameDir+'/maps', ExtractFileName(fname), resMd5);
end;
-function g_Res_SearchSameWAD (const path, filename: AnsiString; const resMd5: TMD5Digest): AnsiString;
+//==========================================================================
+//
+// findExistingResWadWithHash
+//
+// find map or resource wad using its base name and hash
+//
+// returns found wad disk name, or empty string
+//
+//==========================================================================
+function findExistingResWadWithHash (fname: AnsiString; const resMd5: TMD5Digest): AnsiString;
begin
- scanDir(path, false);
- result := getKnownFileWithMD5(path, filename, resMd5);
+ result := scanDir(GameDir+'/wads', ExtractFileName(fname), resMd5);
end;
+//==========================================================================
+//
+// g_Res_DownloadMapWAD
+//
+// download map wad from server (if necessary)
+// download all required map resource wads too
+// registers all required replacement wads
+//
+// returns name of the map wad (relative to mapdir), or empty string on error
+//
+//==========================================================================
function g_Res_DownloadMapWAD (FileName: AnsiString; const mapHash: TMD5Digest): AnsiString;
var
tf: TNetFileTransfer;
resList: TStringList;
f, res: Integer;
strm: TStream;
- mmd5: TMD5Digest;
fname: AnsiString;
- idx: Integer;
wadname: AnsiString;
+ md5: TMD5Digest;
begin
- //SetLength(mapData.ExternalResources, 0);
- //result := g_Res_SearchResWad(true{asMap}, mapHash);
result := '';
- g_Res_ClearReplacementWads();
- g_Res_received_map_start := false;
-
- try
- CreateDir(GameDir+'/maps/downloads');
- except
- end;
-
- try
- CreateDir(GameDir+'/wads/downloads');
- except
- end;
+ clearReplacementWads();
resList := TStringList.Create();
try
+ g_Res_received_map_start := 1;
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 (res <> 0) then exit;
// find or download a map
- result := g_Res_SearchResWad(true{asMap}, mapHash);
+ result := findExistingMapWadWithHash(tf.diskName, mapHash);
if (length(result) = 0) then
begin
// download map
result := '';
exit;
end;
+ try
+ CreateDir(GameDir+'/maps/downloads');
+ except
+ end;
fname := GameDir+'/maps/downloads/'+FileName;
try
- strm := createDiskFile(fname);
+ 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
result := '';
exit;
end;
- mmd5 := MD5File(fname);
- if (not MD5Match(mmd5, mapHash)) then
+ // if it was resumed, check md5 and initiate full download if necessary
+ if tf.resumed then
begin
- e_WriteLog('error downloading map file `'+FileName+'` (bad hash)', TMsgType.Fatal);
- result := '';
- exit;
- end;
- idx := addKnownFile(fname);
- if (idx < 0) then
- begin
- e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
- result := '';
- exit;
+ 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;
- knownFiles[idx].md5 := mmd5;
- knownFiles[idx].md5valid := true;
result := fname;
end;
begin
res := g_Net_RequestResFileInfo(f, tf);
if (res <> 0) then begin result := ''; exit; end;
- wadname := g_Res_SearchResWad(false{asMap}, tf.hash);
+ if (isIgnoredResWad(tf.diskName)) then
+ begin
+ // ignored file, abort download
+ g_Net_AbortResTransfer(tf);
+ e_LogWritefln('ignoring wad resource `%s` by user request', [tf.diskName]);
+ continue;
+ end;
+ wadname := findExistingResWadWithHash(tf.diskName, tf.hash);
if (length(wadname) <> 0) then
begin
// already here
g_Net_AbortResTransfer(tf);
- g_Res_PutReplacementWad(tf.diskName, wadname);
+ addReplacementWad(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 := createDiskFile(fname);
+ strm := openDiskFileRW(fname);
except
e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal);
result := '';
result := '';
exit;
end;
- idx := addKnownFile(fname);
- if (idx < 0) then
+ // if it was resumed, check md5 and initiate full download if necessary
+ if tf.resumed then
begin
- e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
- result := '';
- exit;
+ 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;
- knownFiles[idx].md5 := tf.hash;
- knownFiles[idx].md5valid := true;
- g_Res_PutReplacementWad(tf.diskName, fname);
+ addReplacementWad(tf.diskName, fname);
end;
end;
finally
resList.Free;
+ g_Res_received_map_start := 0;
end;
end;
+initialization
+ conRegVar('rdl_ignore_names', @g_res_ignore_names, 'list of resource wad names (without extensions) to ignore in dl hash checks', 'dl ignore wads');
+ conRegVar('rdl_ignore_enabled', @g_res_ignore_enabled, 'enable dl hash check ignore list', 'dl hash check ignore list active');
end.