DEADSOFTWARE

net: map downloading seems to work
[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 try
171 CreateDir(GameDir+'/maps/downloads');
172 except
173 end;
175 try
176 CreateDir(GameDir+'/wads/downloads');
177 except
178 end;
180 resList := TStringList.Create();
182 try
183 g_Console_Add(Format(_lc[I_NET_MAP_DL], [FileName]));
184 e_WriteLog('Downloading map `' + FileName + '` from server', TMsgType.Notify);
185 g_Game_SetLoadingText(FileName + '...', 0, False);
186 //MC_SEND_MapRequest();
187 if (not g_Net_SendMapRequest()) then exit;
189 FileName := ExtractFileName(FileName);
190 if (length(FileName) = 0) then FileName := 'fucked_map_wad.wad';
191 res := g_Net_Wait_MapInfo(tf, resList);
192 if (res <> 0) then exit;
194 // find or download a map
195 result := g_Res_SearchResWad(true{asMap}, tf.diskName, mapHash);
196 if (length(result) = 0) then
197 begin
198 // download map
199 res := g_Net_RequestResFileInfo(-1{map}, tf);
200 if (res <> 0) then
201 begin
202 e_LogWriteln('error requesting map wad');
203 result := '';
204 exit;
205 end;
206 fname := GameDir+'/maps/downloads/'+FileName;
207 try
208 strm := createDiskFile(fname);
209 except
210 e_WriteLog('cannot create map file `'+FileName+'`', TMsgType.Fatal);
211 result := '';
212 exit;
213 end;
214 tf.diskName := fname;
215 try
216 res := g_Net_ReceiveResourceFile(-1{map}, tf, strm);
217 except
218 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
219 strm.Free;
220 result := '';
221 exit;
222 end;
223 strm.Free;
224 if (res <> 0) then
225 begin
226 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
227 result := '';
228 exit;
229 end;
230 result := fname;
231 end;
233 // download resources
234 for f := 0 to resList.Count-1 do
235 begin
236 res := g_Net_RequestResFileInfo(f, tf);
237 if (res <> 0) then begin result := ''; exit; end;
238 wadname := g_Res_SearchResWad(false{asMap}, tf.diskName, tf.hash);
239 if (length(wadname) <> 0) then
240 begin
241 // already here
242 g_Net_AbortResTransfer(tf);
243 g_Res_PutReplacementWad(tf.diskName, wadname);
244 end
245 else
246 begin
247 fname := GameDir+'/wads/downloads/'+tf.diskName;
248 e_LogWritefln('downloading resource `%s` to `%s`...', [tf.diskName, fname]);
249 try
250 strm := createDiskFile(fname);
251 except
252 e_WriteLog('cannot create resource file `'+fname+'`', TMsgType.Fatal);
253 result := '';
254 exit;
255 end;
256 try
257 res := g_Net_ReceiveResourceFile(f, tf, strm);
258 except
259 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
260 strm.Free;
261 result := '';
262 exit;
263 end;
264 strm.Free;
265 if (res <> 0) then
266 begin
267 e_WriteLog('error downloading map file `'+FileName+'`', TMsgType.Fatal);
268 result := '';
269 exit;
270 end;
271 g_Res_PutReplacementWad(tf.diskName, fname);
272 end;
273 end;
274 finally
275 resList.Free;
276 end;
277 end;
280 end.