DEADSOFTWARE

ae645a1196677443c505ee10f75dcd0e16f63193
[d2df-sdl.git] / src / game / g_res_downloader.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
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.
6 *
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.
11 *
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/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_res_downloader;
18 interface
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);
36 implementation
38 uses g_language, sfs, utils, wadreader, g_game, hashtable;
40 //const DOWNLOAD_DIR = 'downloads';
42 var
43 replacements: THashStrStr = nil;
46 // call this before downloading a new map from a server
47 procedure g_Res_ClearReplacementWads ();
48 begin
49 if assigned(replacements) then replacements.clear();
50 e_LogWriteln('cleared replacement wads');
51 end;
54 // returns original name, or replacement name
55 function g_Res_FindReplacementWad (oldname: AnsiString): AnsiString;
56 var
57 fn: AnsiString;
58 begin
59 result := oldname;
60 if not assigned(replacements) then exit;
61 if (replacements.get(toLowerCase1251(ExtractFileName(oldname)), fn)) then result := fn;
62 end;
65 procedure g_Res_PutReplacementWad (oldname: AnsiString; newDiskName: AnsiString);
66 begin
67 e_LogWritefln('adding replacement wad: oldname=%s; newname=%s', [oldname, newDiskName]);
68 if not assigned(replacements) then replacements := THashStrStr.Create();
69 replacements.put(toLowerCase1251(oldname), newDiskName);
70 end;
73 function scanDir (dirName: AnsiString; baseName: AnsiString; const resMd5: TMD5Digest): AnsiString;
74 var
75 searchResult: TSearchRec;
76 dfn: AnsiString;
77 md5: TMD5Digest;
78 dirs: array of AnsiString;
79 f: Integer;
80 begin
81 result := '';
82 SetLength(dirs, 0);
83 if (length(baseName) = 0) then exit;
84 dirName := IncludeTrailingPathDelimiter(dirName);
85 e_LogWritefln('scanning dir `%s` for file `%s`...', [dirName, baseName]);
87 // scan files
88 if (FindFirst(dirName+'*', faAnyFile, searchResult) <> 0) then exit;
89 try
90 repeat
91 if ((searchResult.Attr and faDirectory) = 0) then
92 begin
93 if (isWadNamesEqu(searchResult.Name, baseName)) then
94 begin
95 dfn := dirName+searchResult.Name;
96 if FileExists(dfn) then
97 begin
98 e_LogWritefln(' found `%s`...', [dfn]);
99 md5 := MD5File(dfn);
100 if MD5Match(md5, resMd5) then
101 begin
102 e_LogWritefln(' MATCH `%s`...', [dfn]);
103 SetLength(dirs, 0);
104 result := dfn;
105 exit;
106 end;
107 end;
108 end;
109 end
110 else
111 begin
112 if (searchResult.Name <> '.') and (searchResult.Name <> '..') then
113 begin
114 dfn := dirName+searchResult.Name;
115 SetLength(dirs, Length(dirs)+1);
116 dirs[length(dirs)-1] := dfn;
117 end;
118 end;
119 until (FindNext(searchResult) <> 0);
120 finally
121 FindClose(searchResult);
122 end;
124 // scan subdirs
125 for f := 0 to High(dirs) do
126 begin
127 dfn := dirs[f];
128 result := scanDir(dfn, baseName, resMd5);
129 if (length(result) <> 0) then begin SetLength(dirs, 0); exit; end;
130 end;
131 SetLength(dirs, 0);
132 end;
135 function g_Res_SearchResWad (asMap: Boolean; fname: AnsiString; const resMd5: TMD5Digest): AnsiString;
136 begin
137 result := '';
138 //if not assigned(scannedDirs) then scannedDirs := THashStrInt.Create();
139 if (asMap) then
140 begin
141 result := scanDir(GameDir+'/maps', ExtractFileName(fname), resMd5);
142 end
143 else
144 begin
145 result := scanDir(GameDir+'/wads', ExtractFileName(fname), resMd5);
146 end;
147 end;
150 function g_Res_SearchSameWAD (const path, filename: AnsiString; const resMd5: TMD5Digest): AnsiString;
151 begin
152 result := scanDir(path, filename, resMd5);
153 end;
156 function g_Res_DownloadMapWAD (FileName: AnsiString; const mapHash: TMD5Digest): AnsiString;
157 var
158 tf: TNetFileTransfer;
159 resList: TStringList;
160 f, res: Integer;
161 strm: TStream;
162 fname: AnsiString;
163 wadname: AnsiString;
164 begin
165 //SetLength(mapData.ExternalResources, 0);
166 result := '';
167 g_Res_ClearReplacementWads();
168 g_Res_received_map_start := false;
170 resList := TStringList.Create();
172 try
173 g_Console_Add(Format(_lc[I_NET_MAP_DL], [FileName]));
174 e_WriteLog('Downloading map `' + FileName + '` from server', TMsgType.Notify);
175 g_Game_SetLoadingText(FileName + '...', 0, False);
176 //MC_SEND_MapRequest();
177 if (not g_Net_SendMapRequest()) then exit;
179 FileName := ExtractFileName(FileName);
180 if (length(FileName) = 0) then FileName := 'fucked_map_wad.wad';
181 res := g_Net_Wait_MapInfo(tf, resList);
182 if (res <> 0) then exit;
184 // find or download a map
185 result := g_Res_SearchResWad(true{asMap}, tf.diskName, mapHash);
186 if (length(result) = 0) then
187 begin
188 // download map
189 res := g_Net_RequestResFileInfo(-1{map}, tf);
190 if (res <> 0) then
191 begin
192 e_LogWriteln('error requesting map wad');
193 result := '';
194 exit;
195 end;
196 try
197 CreateDir(GameDir+'/maps/downloads');
198 except
199 end;
200 fname := GameDir+'/maps/downloads/'+FileName;
201 try
202 strm := createDiskFile(fname);
203 except
204 e_WriteLog('cannot create map file `'+FileName+'`', TMsgType.Fatal);
205 result := '';
206 exit;
207 end;
208 tf.diskName := fname;
209 try
210 res := g_Net_ReceiveResourceFile(-1{map}, tf, strm);
211 except
212 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
213 strm.Free;
214 result := '';
215 exit;
216 end;
217 strm.Free;
218 if (res <> 0) then
219 begin
220 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
221 result := '';
222 exit;
223 end;
224 result := fname;
225 end;
227 // download resources
228 for f := 0 to resList.Count-1 do
229 begin
230 res := g_Net_RequestResFileInfo(f, tf);
231 if (res <> 0) then begin result := ''; exit; end;
232 wadname := g_Res_SearchResWad(false{asMap}, tf.diskName, tf.hash);
233 if (length(wadname) <> 0) then
234 begin
235 // already here
236 g_Net_AbortResTransfer(tf);
237 g_Res_PutReplacementWad(tf.diskName, wadname);
238 end
239 else
240 begin
241 try
242 CreateDir(GameDir+'/wads/downloads');
243 except
244 end;
245 fname := GameDir+'/wads/downloads/'+tf.diskName;
246 e_LogWritefln('downloading resource `%s` to `%s`...', [tf.diskName, fname]);
247 try
248 strm := createDiskFile(fname);
249 except
250 e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal);
251 result := '';
252 exit;
253 end;
254 try
255 res := g_Net_ReceiveResourceFile(f, tf, strm);
256 except
257 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
258 strm.Free;
259 result := '';
260 exit;
261 end;
262 strm.Free;
263 if (res <> 0) then
264 begin
265 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
266 result := '';
267 exit;
268 end;
269 g_Res_PutReplacementWad(tf.diskName, fname);
270 end;
271 end;
272 finally
273 resList.Free;
274 end;
275 end;
278 end.