From: Ketmar Dark Date: Sun, 20 Oct 2019 21:49:53 +0000 (+0300) Subject: game: `e_GetDir()` -> `e_GetWriteableDir()`, with slight changes in logic X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=commitdiff_plain;h=c7827dd408b445f025117f2c5df2a3c0f4622298 game: `e_GetDir()` -> `e_GetWriteableDir()`, with slight changes in logic --- diff --git a/src/engine/e_res.pas b/src/engine/e_res.pas index e5d4756..e2f8889 100644 --- a/src/engine/e_res.pas +++ b/src/engine/e_res.pas @@ -54,16 +54,22 @@ interface {--- same as SysUtils.FinFirst ---} function e_FindFirst (dirs: SSArray; name: AnsiString; attr: LongInt; out Rslt: TSearchRec): LongInt; - {--- try to create directory from list, throws if no one directory created ---} - function e_GetDir (dirs: SSArray): AnsiString; + {--- try to get a writeable directory from list, throws if no one directory created ---} + {--- (unless `required` is `false`: in this case, returns empty string) ---} + {--- creates all necessary subdirs, if it can ---} + function e_GetWriteableDir (dirs: SSArray; required: Boolean=true): AnsiString; implementation - uses WadReader, e_log; + uses WadReader, e_log, hashtable; type SpawnProc = function (pathname: AnsiString): Tstream; + var + writeableDirs: THashStrCIStr = nil; + + function e_UpperDir (path: AnsiString): AnsiString; var i: Integer; begin @@ -214,7 +220,7 @@ implementation begin if debug_e_res then e_LogWritefln('e_GetResourcePath0 %s (%s)', [path, defWad]); - assert(dirs <> nil); + assert(length(dirs) > 0); assert(path <> ''); assert(defWad <> ''); diskName := g_ExtractWadName(path); @@ -244,16 +250,78 @@ implementation end end; - function e_GetDir (dirs: SSArray): AnsiString; - var i: Integer; + // k8: sorry. i know that this sux, but checking directory access rights is unreliable (unportable). + function canCreateFiles (dir: AnsiString): Boolean; + var + f: Integer; + st: TStream = nil; + sr: TSearchRec; + fn: AnsiString; + begin + result := false; + for f := 0 to $7fffffff do + begin + fn := Format('%s/$$$temptest$$$_%d.$$$%d$$$', [dir, f, f]); + if (FindFirst(fn, faAnyFile, sr) = 0) then + begin + FindClose(sr); + continue; + end; + FindClose(sr); + try + st := TFileStream.Create(fn, fmCreate); + except // sorry + st := nil; // just in case + end; + if assigned(st) then + begin + st.Free(); + try DeleteFile(fn); except end; + result := true; + end; + exit; + end; + end; + + function e_GetWriteableDir (dirs: SSArray; required: Boolean=true): AnsiString; + var + f: Integer; begin + assert(length(dirs) > 0); + result := ''; + if assigned(writeableDirs) then + begin + for f := High(dirs) downto Low(dirs) do + begin + if (writeableDirs.get(dirs[f], result)) then + begin + //writeln('*** KNOWN WRITEABLE DIR: "', result, '"'); + exit; + end; + end; + end; + for f := High(dirs) downto Low(dirs) do + begin + try + if ForceDirectories(dirs[f]) then + begin + result := dirs[f]; + if (findFileCI(result, true)) then + begin + if canCreateFiles(result) then + begin + if not assigned(writeableDirs) then writeableDirs := THashStrCIStr.Create(); + writeableDirs.put(dirs[f], result); + //writeln('*** NEW WRITEABLE DIR: "', result, '" ("', dirs[f], '"); rq=', required); + exit; + end; + end; + end; + except // sorry + end; + end; + if required then raise Exception.Create(Format('unable to create directory "%s"', [dirs[High(dirs)]])); result := ''; - i := High(dirs); - while (i >= 0) and (ForceDirectories(dirs[i]) = false) do Dec(i); - if i >= 0 then - result := dirs[i] - else - raise Exception.Create('unable to create directory') end; end. diff --git a/src/game/g_console.pas b/src/game/g_console.pas index 6b8ddc8..fb76d51 100644 --- a/src/game/g_console.pas +++ b/src/game/g_console.pas @@ -594,7 +594,7 @@ begin // writeconfig if Length(p) = 2 then begin - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); g_Console_WriteConfig(e_CatPath(s, p[1])) end else @@ -1965,7 +1965,7 @@ procedure g_Console_WriteGameConfig; begin if gParsingBinds = false then begin - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); g_Console_WriteConfig(e_CatPath(s, configScript)) end end; diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 4fd6057..80f53cc 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -4784,7 +4784,7 @@ end; procedure g_Game_SaveOptions; var s: AnsiString; begin - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Video(s + '/' + CONFIG_FILENAME) else @@ -5473,7 +5473,7 @@ begin if (Length(P) > 1) then NetInterpLevel := StrToIntDef(P[1], NetInterpLevel); g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel)); - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then begin config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); @@ -5492,7 +5492,7 @@ begin else g_Console_Add('net_forceplayerupdate = 0'); - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then begin config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); @@ -5512,7 +5512,7 @@ begin else g_Console_Add('net_predictself = 0'); - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then begin config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME); diff --git a/src/game/g_main.pas b/src/game/g_main.pas index 24dac52..b064804 100644 --- a/src/game/g_main.pas +++ b/src/game/g_main.pas @@ -29,7 +29,7 @@ procedure CharPress (C: AnsiChar); var {--- TO REMOVE ---} - GameDir: string; + GameDir: string; {-----------------} {--- Read-only dirs ---} @@ -148,14 +148,14 @@ begin if LogFileName = '' then begin - rwdir := e_GetDir(LogDirs); + rwdir := e_GetWriteableDir(LogDirs, false); if rwdir <> '' then begin {$IFDEF HEADLESS} LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log'); {$ELSE} LogFileName := e_Catpath(rwdir, 'Doom2DF.log'); - {$ENDIF} + {$ENDIF} end end end; diff --git a/src/game/g_menu.pas b/src/game/g_menu.pas index 53a1cbc..d808bf3 100644 --- a/src/game/g_menu.pas +++ b/src/game/g_menu.pas @@ -375,7 +375,7 @@ begin if g_Game_IsClient then MC_SEND_PlayerSettings; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write(s + '/' + CONFIG_FILENAME); g_Console_WriteGameConfig; @@ -699,7 +699,7 @@ begin gcMap := Map; end; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Gameplay_Custom(s + '/' + CONFIG_FILENAME); @@ -768,7 +768,7 @@ begin NetUseMaster := TGUISwitch(GetControl('swUseMaster')).ItemIndex = 0; end; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then begin g_Options_Write_Net_Server(s + '/' + CONFIG_FILENAME); @@ -791,7 +791,7 @@ begin PW := TGUIEdit(GetControl('edPW')).Text; end; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Net_Client(s + '/' + CONFIG_FILENAME); g_Game_StartClient(NetClientIP, NetClientPort, PW); @@ -809,7 +809,7 @@ begin PW := TGUIEdit(GetControl('edPW')).Text; end; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Net_Client(s + '/' + CONFIG_FILENAME); g_Game_StartClient(NetClientIP, NetClientPort, PW); @@ -1661,7 +1661,7 @@ begin gLanguageChange := True; gAskLanguage := False; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Language(s + '/' + CONFIG_FILENAME); @@ -1679,7 +1679,7 @@ begin gLanguageChange := True; gAskLanguage := False; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Language(s + '/' + CONFIG_FILENAME); @@ -1925,7 +1925,7 @@ begin gLanguageChange := True; gAskLanguage := False; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Language(s + '/' + CONFIG_FILENAME) end; @@ -1937,7 +1937,7 @@ begin gLanguageChange := True; gAskLanguage := False; - s := e_GetDir(ConfigDirs); + s := e_GetWriteableDir(ConfigDirs); if s <> '' then g_Options_Write_Language(s + '/' + CONFIG_FILENAME) end; diff --git a/src/game/g_net.pas b/src/game/g_net.pas index 6b3c8f7..099ab76 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -2189,7 +2189,7 @@ var I: Integer; path: AnsiString; begin - path := e_GetDir(DataDirs); + path := e_GetWriteableDir(DataDirs); if path <> '' then begin path := e_CatPath(path, BANLIST_FILENAME); diff --git a/src/shared/hashtable.pas b/src/shared/hashtable.pas index ec7f54b..cb6d35c 100644 --- a/src/shared/hashtable.pas +++ b/src/shared/hashtable.pas @@ -204,9 +204,12 @@ type type THashIntInt = specialize THashBase; THashStrInt = specialize THashBase; + THashStrCIInt = specialize THashBase; THashIntStr = specialize THashBase; THashStrStr = specialize THashBase; + THashStrCIStr = specialize THashBase; THashStrVariant = specialize THashBase; + THashStrCIVariant = specialize THashBase; function u32Hash (a: LongWord): LongWord; inline;