DEADSOFTWARE

sfs and wad code refactoring: part 1
[d2df-sdl.git] / src / sfs / sfs.pas
index 0f2ff81379c0802bb6936867fa1ee693d17dc1e5..157fbac3c2ccb3d20d5a811fecc34e9a1b6ea142 100644 (file)
@@ -1,6 +1,7 @@
 // streaming file system (virtual)
 {$MODE DELPHI}
 {.$R-}
+{.$DEFINE SFS_VOLDEBUG}
 unit sfs;
 
 interface
@@ -168,6 +169,9 @@ procedure SFSUnregisterVolumeFactory (factory: TSFSVolumeFactory);
 // ïðèíèìàåòñÿ âî âíèìàíèå òîëüêî ïîñëåäíÿÿ òðóáà.
 function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
 
+// äîáàâèòü ñáîðíèê âðåìåííî
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+
 // äîáàâèòü â ïîñòîÿííûé ñïèñîê ñáîðíèê èç ïîòîêà ds.
 // åñëè âîçâðàùàåò èñòèíó, òî SFS ñòàíîâèòñÿ âëÿäåëüöåì ïîòîêà ds è ñàìà
 // óãðîáèò ñåé ïîòîê ïî íåîáõîäèìîñòè.
@@ -195,21 +199,21 @@ function SFSFileOpen (const fName: TSFSString): TStream;
 // ïîñëå èñïîëüçîâàíèÿ, íàòóðàëüíî, èòåðàòîð íàäî ãðîõíóòü %-)
 function SFSFileList (const dataFileName: TSFSString): TSFSFileList;
 
+// çàïðåòèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCDisable ();
+
+// ðàçðåøèòü îñâîáîæäåíèå âðåìåííûõ òîìîâ (ìîæíî âûçûâàòü ðåêóðñèâíî)
+procedure sfsGCEnable ();
+
+// for completeness sake
+procedure sfsGCCollect ();
+
 function SFSReplacePathDelims (const s: TSFSString; newDelim: TSFSChar): TSFSString;
-// èãíîðèðóåò ðåãèñòð ñèìâîëîâ
-function SFSStrEqu (const s0, s1: TSFSString): Boolean;
 
 // ðàçîáðàòü òîëñòîå èìÿ ôàéëà, âåðíóòü âèðòóàëüíîå èìÿ ïîñëåäíåãî ñïèñêà
 // èëè ïóñòóþ ñòîðîêó, åñëè ñïèñêîâ íå áûëî.
 function SFSGetLastVirtualName (const fn: TSFSString): string;
 
-// ïðåîáðàçîâàòü ÷èñëî â ñòðîêó, êðàñèâî ðàçáàâëÿÿ çàïÿòûìè
-function Int64ToStrComma (i: Int64): string;
-
-// `name` will be modified
-// return `true` if file was found
-function sfsFindFileCI (path: string; var name: string): Boolean;
-
 // Wildcard matching
 // this code is meant to allow wildcard pattern matches. tt is VERY useful
 // for matching filename wildcard patterns. tt allows unix grep-like pattern
@@ -230,11 +234,6 @@ function WildMatch (pattern, text: TSFSString): Boolean;
 function WildListMatch (wildList, text: TSFSString; delimChar: AnsiChar=':'): Integer;
 function HasWildcards (const pattern: TSFSString): Boolean;
 
-// this will compare only last path element from sfspath
-function SFSDFPathEqu (sfspath: string; path: string): Boolean;
-
-function SFSUpCase (ch: Char): Char;
-
 
 var
   // ïðàâäà: ðàçðåøåíî èñêàòü ôàéëî íå òîëüêî â ôàéëàõ äàííûõ, íî è íà äèñêå.
@@ -255,47 +254,7 @@ var
 implementation
 
 uses
-  xstreams;
-
-
-function Int64ToStrComma (i: Int64): string;
-var
-  f: Integer;
-begin
-  Str(i, result);
-  f := Length(result)+1;
-  while f > 4 do
-  begin
-    Dec(f, 3); Insert(',', result, f);
-  end;
-end;
-
-
-// `name` will be modified
-function sfsFindFileCI (path: string; var name: string): Boolean;
-var
-  sr: TSearchRec;
-  bestname: string = '';
-begin
-  if length(path) = 0 then path := '.';
-  while (length(path) > 0) and (path[length(path)] = '/') do Delete(path, length(path), 1);
-  if (length(path) = 0) or (path[length(path)] <> '/') then path := path+'/';
-  if FileExists(path+name) then begin result := true; exit; end;
-  if FindFirst(path+'*', faAnyFile, sr) = 0 then
-  repeat
-    if (sr.name = '.') or (sr.name = '..') then continue;
-    if (sr.attr and faDirectory) <> 0 then continue;
-    if sr.name = name then
-    begin
-      FindClose(sr);
-      result := true;
-      exit;
-    end;
-    if (length(bestname) = 0) and SFSStrEqu(sr.name, name) then bestname := sr.name;
-  until FindNext(sr) <> 0;
-  FindClose(sr);
-  if length(bestname) > 0 then begin result := true; name := bestname; end else result := false;
-end;
+  xstreams, utils;
 
 
 const
@@ -472,6 +431,63 @@ type
 var
   factories: TObjectList; // TSFSVolumeFactory
   volumes: TObjectList;   // TVolumeInfo
+  gcdisabled: Integer = 0; // >0: disabled
+
+
+procedure sfsGCCollect ();
+var
+  f, c: Integer;
+  vi: TVolumeInfo;
+  used: Boolean;
+begin
+  // collect garbage
+  f := 0;
+  while f < volumes.Count do
+  begin
+    vi := TVolumeInfo(volumes[f]);
+    if vi = nil then continue;
+    if (not vi.fPermanent) and (vi.fVolume.fRC = 0) and (vi.fOpenedFilesCount = 0) then
+    begin
+      // this volume probably can be removed
+      used := false;
+      c := volumes.Count-1;
+      while not used and (c >= 0) do
+      begin
+        if (c <> f) and (volumes[c] <> nil) then
+        begin
+          used := (TVolumeInfo(volumes[c]).fStream = vi.fStream);
+          if not used then used := (TVolumeInfo(volumes[c]).fVolume.fFileStream = vi.fStream);
+          if used then break;
+        end;
+        Dec(c);
+      end;
+      if not used then
+      begin
+        {$IFDEF SFS_VOLDEBUG}writeln('000: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+        volumes.extract(vi); // remove from list
+        vi.Free; // and kill
+        f := 0;
+        continue;
+      end;
+    end;
+    Inc(f); // next volume
+  end;
+end;
+
+procedure sfsGCDisable ();
+begin
+  Inc(gcdisabled);
+end;
+
+procedure sfsGCEnable ();
+begin
+  Dec(gcdisabled);
+  if gcdisabled <= 0 then
+  begin
+    gcdisabled := 0;
+    sfsGCCollect();
+  end;
+end;
 
 
 // ðàçáèòü èìÿ ôàéëà íà ÷àñòè: ïðåôèêñ ôàéëîâîé ñèñòåìû, èìÿ ôàéëà äàííûõ,
@@ -554,7 +570,7 @@ begin
       vi := TVolumeInfo(volumes[f]);
       if not onlyPerm or vi.fPermanent then
       begin
-        if SFSStrEqu(vi.fPackName, dataFileName) then
+        if StrEquCI1251(vi.fPackName, dataFileName) then
         begin
           result := f;
           exit;
@@ -581,63 +597,6 @@ begin
   end;
 end;
 
-function SFSUpCase (ch: Char): Char;
-begin
-  if ch < #128 then
-  begin
-    if (ch >= 'a') and (ch <= 'z') then Dec(ch, 32);
-  end
-  else
-  begin
-    if (ch >= #224) and (ch <= #255) then
-    begin
-      Dec(ch, 32);
-    end
-    else
-    begin
-      case ch of
-        #184, #186, #191: Dec(ch, 16);
-        #162, #179: Dec(ch);
-      end;
-    end;
-  end;
-  result := ch;
-end;
-
-function SFSStrEqu (const s0, s1: TSFSString): Boolean;
-var
-  i: Integer;
-begin
-  //result := (AnsiCompareText(s0, s1) == 0);
-  result := false;
-  if length(s0) <> length(s1) then exit;
-  for i := 1 to length(s0) do
-  begin
-    if SFSUpCase(s0[i]) <> SFSUpCase(s1[i]) then exit;
-  end;
-  result := true;
-end;
-
-// this will compare only last path element from sfspath
-function SFSDFPathEqu (sfspath: string; path: string): Boolean;
-{var
-  i: Integer;}
-begin
-  result := SFSStrEqu(sfspath, path);
-(*
-  if not result and (length(sfspath) > 1) then
-  begin
-    i := length(sfspath);
-    while i > 1 do
-    begin
-      while (i > 1) and (sfspath[i-1] <> '/') do Dec(i);
-      if i <= 1 then exit;
-      writeln('{', sfspath, '} [', Copy(sfspath, i, length(sfspath)), '] : [', path, ']');
-      result := SFSStrEqu(Copy(sfspath, i, length(sfspath)), path);
-    end;
-  end;
-*)
-end;
 
 // adds '/' too
 function normalizePath (fn: string): string;
@@ -748,10 +707,14 @@ begin
   if fOwner <> nil then
   begin
     Dec(fOwner.fOpenedFilesCount);
-    if not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
+    if (gcdisabled = 0) and not fOwner.fPermanent and (fOwner.fOpenedFilesCount < 1) then
     begin
       f := volumes.IndexOf(fOwner);
-      if f <> -1 then volumes[f] := nil; // this will destroy the volume
+      if f <> -1 then
+      begin
+        {$IFDEF SFS_VOLDEBUG}writeln('001: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+        volumes[f] := nil; // this will destroy the volume
+      end;
     end;
   end;
 end;
@@ -800,7 +763,8 @@ begin
   ReadDirectory();
   fFiles.Pack();
 
-  for f := 0 to fFiles.Count-1 do
+  f := 0;
+  while f < fFiles.Count do
   begin
     sfi := TSFSFileInfo(fFiles[f]);
     // normalize name & path
@@ -818,6 +782,7 @@ begin
       sfi.fPath := sfi.fPath+tmp;
     end;
     sfi.fPath := normalizePath(sfi.fPath);
+    if (length(sfi.fPath) = 0) and (length(sfi.fName) = 0) then sfi.Free else Inc(f);
   end;
   removeCommonPath();
 end;
@@ -846,8 +811,8 @@ begin
       Dec(result);
       if fFiles[result] <> nil then
       begin
-        if SFSStrEqu(fPath, TSFSFileInfo(fFiles[result]).fPath) and
-           SFSStrEqu(fName, TSFSFileInfo(fFiles[result]).fName) then exit;
+        if StrEquCI1251(fPath, TSFSFileInfo(fFiles[result]).fPath) and
+           StrEquCI1251(fName, TSFSFileInfo(fFiles[result]).fName) then exit;
       end;
     end;
     result := -1;
@@ -914,8 +879,11 @@ begin
   if fVolume <> nil then Dec(fVolume.fRC);
   Dec(TVolumeInfo(volumes[f]).fOpenedFilesCount);
   // óáü¸ì çàïèñü, åñëè îíà âðåìåííàÿ, è â íåé íåò áîëüøå íè÷åãî îòêðûòîãî
-  if not TVolumeInfo(volumes[f]).fPermanent and
-     (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then volumes[f] := nil;
+  if (gcdisabled = 0) and not TVolumeInfo(volumes[f]).fPermanent and (TVolumeInfo(volumes[f]).fOpenedFilesCount < 1) then
+  begin
+    {$IFDEF SFS_VOLDEBUG}writeln('002: destroying volume "', TVolumeInfo(volumes[f]).fPackName, '"');{$ENDIF}
+    volumes[f] := nil;
+  end;
   inherited Destroy();
 end;
 
@@ -1008,7 +976,7 @@ begin
     except
       FreeAndNil(st);
       // óäàëèì íåèñïîëüçóåìûé âðåìåííûé òîì.
-      if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
+      if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[result] := nil;
       raise;
     end;
     // óðà. îòêðûëè ôàéë. êèäàåì â âîçäóõ ÷åï÷èêè, ïðîäîëæàåì ðàçâëå÷åíèå.
@@ -1039,7 +1007,7 @@ begin
   end;
 
   if ds <> nil then st := ds
-  else st := TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite);
+  else st := TFileStream.Create(fn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone);
   st.Position := 0;
 
   volumes.Pack();
@@ -1090,8 +1058,7 @@ begin
   vi.fOpenedFilesCount := 0;
 end;
 
-function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream;
-  top: Boolean = false): Boolean;
+function SFSAddSubDataFile (const virtualName: TSFSString; ds: TStream; top: Boolean=false): Boolean;
 var
   tv: Integer;
 begin
@@ -1105,7 +1072,7 @@ begin
   end;
 end;
 
-function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean = false): Boolean;
+function SFSAddDataFile (const dataFileName: TSFSString; top: Boolean=false): Boolean;
 var
   tv: Integer;
 begin
@@ -1118,6 +1085,20 @@ begin
   end;
 end;
 
+function SFSAddDataFileTemp (const dataFileName: TSFSString; top: Boolean=false): Boolean;
+var
+  tv: Integer;
+begin
+  try
+    if top then tv := -1 else tv := 1;
+    SFSAddDataFileEx(dataFileName, nil, tv, 0);
+    result := true;
+  except
+    result := false;
+  end;
+end;
+
+
 
 function SFSExpandDirName (const s: TSFSString): TSFSString;
 var
@@ -1174,7 +1155,7 @@ var
       cdir := SFSReplacePathDelims(SFSExpandDirName(cdir), '/');
       if cdir[Length(cdir)] <> '/' then cdir := cdir+'/';
       try
-        result := TFileStream.Create(cdir+dfn, fmOpenRead or fmShareDenyWrite);
+        result := TFileStream.Create(cdir+dfn, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone);
         exit;
       except
       end;
@@ -1204,7 +1185,7 @@ begin
       ps := TOwnedPartialStream.Create(vi, result, 0, result.Size, true);
     except
       result.Free();
-      if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+      if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
       result := CheckDisk(); // îáëîì ñ datafile, ïðîâåðèì äèñê
       if result = nil then raise ESFSError.Create('file not found: "'+fName+'"');
       exit;
@@ -1277,7 +1258,7 @@ begin
     result := TSFSFileList.Create(vi.fVolume);
     Inc(vi.fVolume.fRC);
   except
-    if not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
+    if (gcdisabled = 0) and not vi.fPermanent and (vi.fOpenedFilesCount < 1) then volumes[f] := nil;
   end;
 end;