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, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 {$DEFINE SFS_DWFAD_DEBUG}
20 {$DEFINE SFS_MAPDETECT_FX}
29 SArray
= array of ShortString;
31 TWADFile
= class(TObject
)
33 fFileName
: AnsiString; // empty: not opened
36 function getIsOpen (): Boolean;
37 function isMapResource (idx
: Integer): Boolean;
39 function GetResourceEx (name
: AnsiString; wantMap
: Boolean; var pData
: Pointer; var Len
: Integer): Boolean;
43 destructor Destroy(); override;
47 function ReadFile (FileName
: AnsiString): Boolean;
48 function ReadMemory (Data
: Pointer; Len
: LongWord): Boolean;
50 function GetResource (name
: AnsiString; var pData
: Pointer; var Len
: Integer): Boolean;
51 function GetMapResource (name
: AnsiString; var pData
: Pointer; var Len
: Integer): Boolean;
52 function GetMapResources (): SArray
;
54 property isOpen
: Boolean read getIsOpen
;
58 function g_ExtractWadName (resourceStr
: AnsiString): AnsiString;
59 function g_ExtractWadNameNoPath (resourceStr
: AnsiString): AnsiString;
60 function g_ExtractFilePath (resourceStr
: AnsiString): AnsiString;
61 function g_ExtractFileName (resourceStr
: AnsiString): AnsiString; // without path
62 function g_ExtractFilePathName (resourceStr
: AnsiString): AnsiString;
64 // return fixed AnsiString or empty AnsiString
65 function findDiskWad (fname
: AnsiString): AnsiString;
69 wadoptDebug
: Boolean = false;
70 wadoptFast
: Boolean = false;
76 SysUtils
, Classes
{, BinEditor}, e_log
{, g_options}, utils
, MAPSTRUCT
;
79 function findDiskWad (fname
: AnsiString): AnsiString;
82 if not findFileCI(fname
) then
84 //e_WriteLog(Format('findDiskWad: error looking for [%s]', [fname]), MSG_NOTIFY);
85 if StrEquCI1251(ExtractFileExt(fname
), '.wad') then
87 fname
:= ChangeFileExt(fname
, '.pk3');
88 //e_WriteLog(Format(' looking for [%s]', [fname]), MSG_NOTIFY);
89 if not findFileCI(fname
) then
91 fname
:= ChangeFileExt(fname
, '.zip');
92 //e_WriteLog(Format(' looking for [%s]', [fname]), MSG_NOTIFY);
93 if not findFileCI(fname
) then exit
;
101 //e_WriteLog(Format('findDiskWad: FOUND [%s]', [fname]), MSG_NOTIFY);
106 function normSlashes (s
: AnsiString): AnsiString;
110 for f
:= 1 to length(s
) do if s
[f
] = '\' then s
[f
] := '/';
114 function g_ExtractWadNameNoPath (resourceStr
: AnsiString): AnsiString;
118 for f
:= length(resourceStr
) downto 1 do
120 if resourceStr
[f
] = ':' then
122 result
:= normSlashes(Copy(resourceStr
, 1, f
-1));
124 while (c
> 0) and (result
[c
] <> '/') do Dec(c
);
125 if c
> 0 then result
:= Copy(result
, c
+1, length(result
));
132 function g_ExtractWadName (resourceStr
: AnsiString): AnsiString;
136 for f
:= length(resourceStr
) downto 1 do
138 if resourceStr
[f
] = ':' then
140 result
:= normSlashes(Copy(resourceStr
, 1, f
-1));
147 function g_ExtractFilePath (resourceStr
: AnsiString): AnsiString;
149 f
, lastSlash
: Integer;
153 for f
:= length(resourceStr
) downto 1 do
155 if (lastSlash
< 0) and (resourceStr
[f
] = '\') or (resourceStr
[f
] = '/') then lastSlash
:= f
;
156 if resourceStr
[f
] = ':' then
158 if lastSlash
> 0 then
160 result
:= normSlashes(Copy(resourceStr
, f
, lastSlash
-f
));
161 while (length(result
) > 0) and (result
[1] = '/') do Delete(result
, 1, 1);
166 if lastSlash
> 0 then result
:= normSlashes(Copy(resourceStr
, 1, lastSlash
-1));
169 function g_ExtractFileName (resourceStr
: AnsiString): AnsiString; // without path
171 f
, lastSlash
: Integer;
175 for f
:= length(resourceStr
) downto 1 do
177 if (lastSlash
< 0) and (resourceStr
[f
] = '\') or (resourceStr
[f
] = '/') then lastSlash
:= f
;
178 if resourceStr
[f
] = ':' then
180 if lastSlash
> 0 then result
:= Copy(resourceStr
, lastSlash
+1, length(resourceStr
));
184 if lastSlash
> 0 then result
:= Copy(resourceStr
, lastSlash
+1, length(resourceStr
));
187 function g_ExtractFilePathName (resourceStr
: AnsiString): AnsiString;
192 for f
:= length(resourceStr
) downto 1 do
194 if resourceStr
[f
] = ':' then
196 result
:= normSlashes(Copy(resourceStr
, f
+1, length(resourceStr
)));
197 while (length(result
) > 0) and (result
[1] = '/') do Delete(result
, 1, 1);
201 result
:= normSlashes(resourceStr
);
202 while (length(result
) > 0) and (result
[1] = '/') do Delete(result
, 1, 1);
208 constructor TWADFile
.Create();
214 destructor TWADFile
.Destroy();
221 function TWADFile
.getIsOpen (): Boolean;
223 result
:= (fFileName
<> '');
227 procedure TWADFile
.FreeWAD();
229 if fIter
<> nil then FreeAndNil(fIter
);
230 //if fFileName <> '' then e_WriteLog(Format('TWADFile.ReadFile: [%s] closed', [fFileName]), MSG_NOTIFY);
234 function TWADFile
.isMapResource (idx
: Integer): Boolean;
236 sign
: packed array [0..2] of Char;
240 if not isOpen
or (fIter
= nil) then exit
;
241 if (idx
< 0) or (idx
>= fIter
.Count
) then exit
;
244 fs
:= fIter
.volume
.OpenFileByIndex(idx
);
245 fs
.readBuffer(sign
, 3);
246 result
:= (sign
= MAP_SIGNATURE
);
248 if fs
<> nil then fs
.Free();
254 function removeExt (s
: AnsiString): AnsiString;
259 while (i
> 1) and (s
[i
-1] <> '.') and (s
[i
-1] <> '/') do Dec(i
);
260 if (i
> 1) and (s
[i
-1] = '.') then
262 //writeln('[', s, '] -> [', Copy(s, 1, i-2), ']');
263 s
:= Copy(s
, 1, i
-2);
268 function TWADFile
.GetResourceEx (name
: AnsiString; wantMap
: Boolean; var pData
: Pointer; var Len
: Integer): Boolean;
270 f
, lastSlash
: Integer;
274 rpath
, rname
: AnsiString;
275 sign
: array [0..2] of Char;
279 if not isOpen
or (fIter
= nil) then Exit
;
280 rname
:= removeExt(name
);
281 if length(rname
) = 0 then Exit
; // just in case
283 for f
:= 1 to length(rname
) do
285 if rname
[f
] = '\' then rname
[f
] := '/';
286 if rname
[f
] = '/' then lastSlash
:= f
;
288 if lastSlash
> 0 then
290 rpath
:= Copy(rname
, 1, lastSlash
);
291 Delete(rname
, 1, lastSlash
);
297 // backwards, due to possible similar names and such
298 for f
:= fIter
.Count
-1 downto 0 do
300 fi
:= fIter
.Files
[f
];
301 if fi
= nil then continue
;
302 if StrEquCI1251(removeExt(fi
.name
), rname
) then
304 // i found her (maybe)
307 if length(fi
.path
) < length(rpath
) then continue
; // alas
308 if length(fi
.path
) = length(rpath
) then
310 if not StrEquCI1251(fi
.path
, rpath
) then continue
; // alas
314 if fi
.path
[length(fi
.path
)-length(rpath
)] <> '/' then continue
; // alas
315 if not StrEquCI1251(Copy(fi
.path
, length(fi
.path
)+1-length(rpath
), length(fi
.path
)), rpath
) then continue
; // alas
319 fs
:= fIter
.volume
.OpenFileByIndex(f
);
325 if wantMap
then continue
;
326 e_WriteLog(Format('DFWAD: can''t open file [%s] in [%s]', [name
, fFileName
]), MSG_WARNING
);
329 // if we want only maps, check if this is map
330 {$IFDEF SFS_MAPDETECT_FX}
334 //e_WriteLog(Format('DFWAD: checking for good map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
336 fs
.readBuffer(sign
, 3);
337 goodMap
:= (sign
= MAP_SIGNATURE
);
340 e_WriteLog(Format(' GOOD map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY)
342 e_WriteLog(Format(' BAD map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
348 //e_WriteLog(Format(' not a map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
355 Len
:= Integer(fs
.size
);
359 fs
.ReadBuffer(pData
^, Len
);
370 {$IFNDEF SFS_MAPDETECT_FX}
376 Move(pData
^, sign
, 3);
377 goodMap
:= (sign
= MAP_SIGNATURE
);
381 //e_WriteLog(Format(' not a map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
390 {$IFDEF SFS_DWFAD_DEBUG}
392 e_WriteLog(Format('DFWAD: file [%s] FOUND in [%s]; size is %d bytes', [name
, fFileName
, Len
]), MSG_NOTIFY
);
397 e_WriteLog(Format('DFWAD: file [%s] not found in [%s]', [name
, fFileName
]), MSG_WARNING
);
400 function TWADFile
.GetResource (name
: AnsiString; var pData
: Pointer; var Len
: Integer): Boolean;
402 result
:= GetResourceEx(name
, false, pData
, Len
);
405 function TWADFile
.GetMapResource (name
: AnsiString; var pData
: Pointer; var Len
: Integer): Boolean;
407 result
:= GetResourceEx(name
, true, pData
, Len
);
410 function TWADFile
.GetMapResources (): SArray
;
417 if not isOpen
or (fIter
= nil) then Exit
;
418 for f
:= fIter
.Count
-1 downto 0 do
420 fi
:= fIter
.Files
[f
];
421 if fi
= nil then continue
;
422 if length(fi
.name
) = 0 then continue
;
423 //e_WriteLog(Format('DFWAD: checking for map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
424 if isMapResource(f
) then
426 s
:= removeExt(fi
.name
);
430 if StrEquCI1251(result
[c
], s
) then break
;
435 SetLength(result
, Length(result
)+1);
436 result
[high(result
)] := removeExt(fi
.name
);
443 function TWADFile
.ReadFile (FileName
: AnsiString): Boolean;
450 //e_WriteLog(Format('TWADFile.ReadFile: [%s]', [FileName]), MSG_NOTIFY);
452 rfn
:= findDiskWad(FileName
);
453 if length(rfn
) = 0 then
455 e_WriteLog(Format('TWADFile.ReadFile: error looking for [%s]', [FileName
]), MSG_NOTIFY
);
458 {$IFDEF SFS_DWFAD_DEBUG}
459 if wadoptDebug
then e_WriteLog(Format('TWADFile.ReadFile: FOUND [%s]', [rfn
]), MSG_NOTIFY
);
465 if not SFSAddDataFile(rfn
, true) then exit
;
469 if not SFSAddDataFileTemp(rfn
, true) then exit
;
474 fIter
:= SFSFileList(rfn
);
475 if fIter
= nil then Exit
;
477 {$IFDEF SFS_DWFAD_DEBUG}
478 if wadoptDebug
then e_WriteLog(Format('TWADFile.ReadFile: [%s] opened', [fFileName
]), MSG_NOTIFY
);
485 uniqueCounter
: Integer = 0;
487 function TWADFile
.ReadMemory (Data
: Pointer; Len
: LongWord): Boolean;
496 if (Data
= nil) or (Len
= 0) then
498 e_WriteLog('TWADFile.ReadMemory: EMPTY SUBWAD!', MSG_WARNING
);
502 fn
:= Format(' -- memwad %d -- ', [uniqueCounter
]);
504 {$IFDEF SFS_DWFAD_DEBUG}
505 e_WriteLog(Format('TWADFile.ReadMemory: [%s]', [fn
]), MSG_NOTIFY
);
509 st
:= TSFSMemoryStreamRO
.Create(Data
, Len
);
510 if not SFSAddSubDataFile(fn
, st
, true) then
520 fIter
:= SFSFileList(fn
);
521 if fIter
= nil then Exit
;
524 {$IFDEF SFS_DWFAD_DEBUG}
525 e_WriteLog(Format('TWADFile.ReadMemory: [%s] opened', [fFileName
]), MSG_NOTIFY
);
529 for f := 0 to fIter.Count-1 do
531 fi := fIter.Files[f];
532 if fi = nil then continue;
533 st := fIter.volume.OpenFileByIndex(f);
536 e_WriteLog(Format('[%s]: [%s : %s] CAN''T OPEN', [fFileName, fi.path, fi.name]), MSG_NOTIFY);
540 e_WriteLog(Format('[%s]: [%s : %s] %u', [fFileName, fi.path, fi.name, st.size]), MSG_NOTIFY);
544 //fIter.volume.OpenFileByIndex(0);
552 sfsDiskDirs
:= '<exedir>/data'; //FIXME