DEADSOFTWARE

sfs, wadreader: much better searching for files inside archives with extra dirs in...
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 22 Apr 2016 15:03:11 +0000 (18:03 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 22 Apr 2016 15:06:39 +0000 (18:06 +0300)
src/game/g_gui.pas
src/game/g_map.pas
src/sfs/sfs.pas
src/sfs/sfsZipFS.pas
src/shared/wadreader.pas

index 9319e45a7b9e29550da578fa07bf4af5ac32dc25..cd9d3b93244a371f0c89cc751575f78bb0106474 100644 (file)
@@ -2484,7 +2484,7 @@ begin
   end;
 
   //k8: ignores path again
-  if not WAD.GetResource(g_ExtractFileName(Res), Data, Len) then
+  if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
   begin
     WAD.Free();
     Exit;
index 6c156caf5c973912a371ba6e4c0fc9397dce1467..2c611442025585a94d1c42d998ff5c900c6d2f6d 100644 (file)
@@ -799,7 +799,7 @@ begin
     end;
     //k8: why loader ignores path here?
     mapResName := g_ExtractFileName(Res);
-    if not WAD.GetResource(mapResName, Data, Len) then
+    if not WAD.GetMapResource(mapResName, Data, Len) then
     begin
       g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
       WAD.Free();
@@ -1267,7 +1267,7 @@ begin
   end;
 
   //k8: it ignores path again
-  if not WAD.GetResource(g_ExtractFileName(Res), Data, Len) then
+  if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
   begin
     WAD.Free();
     Exit;
@@ -1305,37 +1305,23 @@ var
   WAD: TWADFile;
   a: Integer;
   ResList: SArray;
-  Data: Pointer;
-  Len: Integer;
-  Sign: Array [0..2] of Char;
 begin
   Result := nil;
-
   WAD := TWADFile.Create();
   if not WAD.ReadFile(WADName) then
   begin
     WAD.Free();
     Exit;
   end;
-
-  ResList := WAD.GetRootResources();
-
+  ResList := WAD.GetMapResources();
   if ResList <> nil then
+  begin
     for a := 0 to High(ResList) do
     begin
-      if not WAD.GetResource(ResList[a], Data, Len) then Continue;
-      CopyMemory(@Sign[0], Data, 3);
-      FreeMem(Data);
-
-      if Sign = MAP_SIGNATURE then
-      begin
-        SetLength(Result, Length(Result)+1);
-        Result[High(Result)] := ResList[a];
-      end;
-
-      Sign := '';
+      SetLength(Result, Length(Result)+1);
+      Result[High(Result)] := ResList[a];
     end;
-
+  end;
   WAD.Free();
 end;
 
@@ -1357,7 +1343,7 @@ begin
     Exit;
   end;
 
-  ResList := WAD.GetRootResources();
+  ResList := WAD.GetMapResources();
   WAD.Free();
 
   mnn := g_ExtractFileName(Res);
index 948b470c4f554901e00fce4f6a8b2e640c675498..d4ebdebe6c8cd1cc0bba60e9d61f3468dc625e97 100644 (file)
@@ -70,8 +70,6 @@ type
     // íèêàêèõ ïàäåíèé íà íåïðàâèëüíûå èíäåêñû!
     function GetFiles (index: Integer): TSFSFileInfo; virtual;
 
-    procedure removeCommonPath (); virtual;
-
   public
     // pSt íå îáÿçàòåëüíî çàïîìèíàòü, åñëè îí íå íóæåí.
     constructor Create (const pFileName: AnsiString; pSt: TStream); virtual;
@@ -744,10 +742,6 @@ begin
   fFiles := TObjectList.Create(true);
 end;
 
-procedure TSFSVolume.removeCommonPath ();
-begin
-end;
-
 procedure TSFSVolume.DoDirectoryRead ();
 var
   f, c: Integer;
@@ -779,7 +773,6 @@ begin
     sfi.fPath := normalizePath(sfi.fPath);
     if (length(sfi.fPath) = 0) and (length(sfi.fName) = 0) then sfi.Free else Inc(f);
   end;
-  removeCommonPath();
 end;
 
 destructor TSFSVolume.Destroy ();
index c39eb4361e3e5fcfd4ff67ac4de62eacffa54734..d76114c5f5bb760a5f6152708e6a32eca66079af 100644 (file)
@@ -28,7 +28,6 @@ type
     procedure DFWADReadDirectory ();
 
     procedure ReadDirectory (); override;
-    procedure removeCommonPath (); override;
 
   public
     function OpenFileByIndex (const index: Integer): TStream; override;
@@ -114,65 +113,6 @@ begin
 end;
 
 
-function maxPrefix (s0: string; s1: string): Integer;
-var
-  f: Integer;
-begin
-  for f := 1 to length(s0) do
-  begin
-    if f > length(s1) then begin result := f; exit; end;
-    if UpCase1251(s0[f]) <> UpCase1251(s1[f]) then begin result := f; exit; end;
-  end;
-  result := length(s0);
-end;
-
-
-procedure TSFSZipVolume.removeCommonPath ();
-var
-  f, pl, maxsc, sc, c: integer;
-  cp, s: string;
-  fi: TSFSZipFileInfo;
-begin
-  if fType <> sfszvZIP then exit;
-  maxsc := 0;
-  if fFiles.Count = 0 then exit;
-  cp := '';
-  for f := 0 to fFiles.Count-1 do
-  begin
-    fi := TSFSZipFileInfo(fFiles[f]);
-    s := fi.fPath;
-    if length(s) > 0 then begin cp := s; break; end;
-  end;
-  if length(cp) = 0 then exit;
-  for f := 0 to fFiles.Count-1 do
-  begin
-    fi := TSFSZipFileInfo(fFiles[f]);
-    s := fi.fPath;
-    if length(s) = 0 then continue;
-    pl := maxPrefix(cp, s);
-    //writeln('s=[', s, ']; cp=[', cp, ']; pl=', pl);
-    if pl = 0 then exit; // no common prefix at all
-    cp := Copy(cp, 1, pl);
-    sc := 0;
-    for c := 1 to length(s) do if s[c] = '/' then Inc(sc);
-    if sc > maxsc then maxsc := sc;
-  end;
-  if maxsc < 2 then exit; // alas
-  while (length(cp) > 0) and (cp[length(cp)] <> '/') do cp := Copy(cp, 1, length(cp)-1);
-  if length(cp) < 2 then exit; // nothing to do
-  for f := 0 to fFiles.Count-1 do
-  begin
-    fi := TSFSZipFileInfo(fFiles[f]);
-    if length(fi.fPath) >= length(cp) then
-    begin
-      s := fi.fPath;
-      fi.fPath := Copy(fi.fPath, length(cp)+1, length(fi.fPath));
-      //writeln('FIXED [', s, '] -> [', fi.fPath, ']');
-    end;
-  end;
-end;
-
-
 { TSFSZipVolume }
 procedure TSFSZipVolume.ZIPReadDirectory ();
 var
index ade53adcf7f9343c6ed882a20c7df2b626b0ac7e..1c5fb19e123bded268c49037fb551d2efac87d9f 100644 (file)
@@ -2,6 +2,7 @@
 unit wadreader;
 
 {$DEFINE SFS_DWFAD_DEBUG}
+{$DEFINE SFS_MAPDETECT_FX}
 
 interface
 
@@ -18,6 +19,9 @@ type
     fIter: TSFSFileList;
 
     function getIsOpen (): Boolean;
+    function isMapResource (idx: Integer): Boolean;
+
+    function GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer): Boolean;
 
    public
     constructor Create();
@@ -29,7 +33,8 @@ type
     function ReadMemory (Data: Pointer; Len: LongWord): Boolean;
 
     function GetResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
-    function GetRootResources (): SArray;
+    function GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+    function GetMapResources (): SArray;
 
     property isOpen: Boolean read getIsOpen;
   end;
@@ -48,7 +53,7 @@ function findDiskWad (fname: AnsiString): AnsiString;
 implementation
 
 uses
-  SysUtils, Classes, BinEditor, e_log, g_options, utils;
+  SysUtils, Classes, BinEditor, e_log, g_options, utils, MAPSTRUCT;
 
 
 function findDiskWad (fname: AnsiString): AnsiString;
@@ -199,6 +204,25 @@ begin
   fFileName := '';
 end;
 
+function TWADFile.isMapResource (idx: Integer): Boolean;
+var
+  sign: packed array [0..2] of Char;
+  fs: TStream;
+begin
+  result := false;
+  if not isOpen or (fIter = nil) then exit;
+  if (idx < 0) or (idx >= fIter.Count) then exit;
+  fs := nil;
+  try
+    fs := fIter.volume.OpenFileByIndex(idx);
+    fs.readBuffer(sign, 3);
+    result := (sign = MAP_SIGNATURE);
+  except
+    if fs <> nil then fs.Free();
+    exit;
+  end;
+  fs.Free();
+end;
 
 function removeExt (s: AnsiString): AnsiString;
 var
@@ -214,14 +238,15 @@ begin
   result := s;
 end;
 
-function TWADFile.GetResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+function TWADFile.GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer): Boolean;
 var
   f, lastSlash: Integer;
   fi: TSFSFileInfo;
   fs: TStream;
   fpp: Pointer;
   rpath, rname: AnsiString;
-  //fn: AnsiString;
+  sign: array [0..2] of Char;
+  goodMap: Boolean;
 begin
   Result := False;
   if not isOpen or (fIter = nil) then Exit;
@@ -247,12 +272,22 @@ begin
   begin
     fi := fIter.Files[f];
     if fi = nil then continue;
-    //e_WriteLog(Format('DFWAD: searching for [%s : %s] in [%s]; current is [%s : %s]', [Section, Resource, fFileName, fi.path, fi.name]), MSG_NOTIFY);
-    if StrEquCI1251(fi.path, rpath) and StrEquCI1251(removeExt(fi.name), rname) then
+    if StrEquCI1251(removeExt(fi.name), rname) then
     begin
-      // i found her!
-      //fn := fFileName+'::'+fi.path+fi.name;
-      //fs := SFSFileOpen(fn);
+      // i found her (maybe)
+      if not wantMap then
+      begin
+        if length(fi.path) < length(rpath) then continue; // alas
+        if length(fi.path) = length(rpath) then
+        begin
+          if not StrEquCI1251(fi.path, rpath) then continue; // alas
+        end
+        else
+        begin
+          if fi.path[length(fi.path)-length(rpath)] <> '/' then continue; // alas
+          if not StrEquCI1251(Copy(fi.path, length(fi.path)+1-length(rpath), length(fi.path)), rpath) then continue; // alas
+        end;
+      end;
       try
         fs := fIter.volume.OpenFileByIndex(f);
       except
@@ -260,9 +295,34 @@ begin
       end;
       if fs = nil then
       begin
+        if wantMap then continue;
         e_WriteLog(Format('DFWAD: can''t open file [%s] in [%s]', [name, fFileName]), MSG_WARNING);
         break;
       end;
+      // if we want only maps, check if this is map
+{$IFDEF SFS_MAPDETECT_FX}
+      if wantMap then
+      begin
+        goodMap := false;
+        e_WriteLog(Format('DFWAD: checking for good map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
+        try
+          fs.readBuffer(sign, 3);
+          goodMap := (sign = MAP_SIGNATURE);
+          if goodMap then
+            e_WriteLog(Format('  GOOD map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY)
+          else
+            e_WriteLog(Format('  BAD map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
+        except
+        end;
+        if not goodMap then
+        begin
+          e_WriteLog(Format('  not a map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
+          fs.Free();
+          continue;
+        end;
+        fs.position := 0;
+      end;
+{$ENDIF}
       Len := Integer(fs.size);
       GetMem(pData, Len);
       fpp := pData;
@@ -278,6 +338,25 @@ begin
         end;
         fs.Free;
       end;
+{$IFNDEF SFS_MAPDETECT_FX}
+      if wantMap then
+      begin
+        goodMap := false;
+        if Len >= 3 then
+        begin
+          Move(pData^, sign, 3);
+          goodMap := (sign = MAP_SIGNATURE);
+        end;
+        if not goodMap then
+        begin
+          e_WriteLog(Format('  not a map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
+          FreeMem(pData);
+          pData := nil;
+          Len := 0;
+          continue;
+        end;
+      end;
+{$ENDIF}
       result := true;
       {$IFDEF SFS_DWFAD_DEBUG}
       if gSFSDebug then
@@ -289,23 +368,44 @@ begin
   e_WriteLog(Format('DFWAD: file [%s] not found in [%s]', [name, fFileName]), MSG_WARNING);
 end;
 
+function TWADFile.GetResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+begin
+  result := GetResourceEx(name, false, pData, Len);
+end;
 
-function TWADFile.GetRootResources (): SArray;
+function TWADFile.GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+begin
+  result := GetResourceEx(name, true, pData, Len);
+end;
+
+function TWADFile.GetMapResources (): SArray;
 var
-  f: Integer;
+  f, c: Integer;
   fi: TSFSFileInfo;
+  s: AnsiString;
 begin
   Result := nil;
   if not isOpen or (fIter = nil) then Exit;
-  for f := 0 to fIter.Count-1 do
+  for f := fIter.Count-1 downto 0 do
   begin
     fi := fIter.Files[f];
     if fi = nil then continue;
     if length(fi.name) = 0 then continue;
-    if length(fi.path) = 0 then
+    e_WriteLog(Format('DFWAD: checking for map in wad [%s], file [%s] (#%d)', [fFileName, fi.fname, f]), MSG_NOTIFY);
+    if isMapResource(f) then
     begin
-      SetLength(result, Length(result)+1);
-      result[high(result)] := removeExt(fi.name);
+      s := removeExt(fi.name);
+      c := High(result);
+      while c >= 0 do
+      begin
+        if StrEquCI1251(result[c], s) then break;
+        Dec(c);
+      end;
+      if c < 0 then
+      begin
+        SetLength(result, Length(result)+1);
+        result[high(result)] := removeExt(fi.name);
+      end;
     end;
   end;
 end;