DEADSOFTWARE

allow resources in non current directory (warning: res downloader are broken)
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Sun, 20 Oct 2019 20:01:40 +0000 (23:01 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Sun, 20 Oct 2019 20:08:27 +0000 (23:08 +0300)
15 files changed:
src/engine/e_res.pas [new file with mode: 0644]
src/game/Doom2DF.lpr
src/game/g_console.pas
src/game/g_game.pas
src/game/g_gui.pas
src/game/g_holmes.pas
src/game/g_main.pas
src/game/g_map.pas
src/game/g_menu.pas
src/game/g_net.pas
src/game/g_options.pas
src/game/g_player.pas
src/game/g_saveload.pas
src/game/g_triggers.pas
src/shared/utils.pas

diff --git a/src/engine/e_res.pas b/src/engine/e_res.pas
new file mode 100644 (file)
index 0000000..db32879
--- /dev/null
@@ -0,0 +1,259 @@
+(* Copyright (C)  Doom 2D: Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License ONLY.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$I ../shared/a_modes.inc}
+unit e_res;
+
+interface
+
+  uses SysUtils, Utils, Classes;
+
+  var
+    debug_e_res: Boolean;
+
+  {-------------------------------------------}
+  {--- insert separator beetwin a and b    ---}
+  {--- result are correct if (a or b) = '' ---}
+  {--- - - - - - - - - - - - - - - - - - - ---}
+  function e_CatPath (a, b: AnsiString): AnsiString;
+
+  {--- remove last entry from path ---}
+  function e_UpperDir (path: AnsiString): AnsiString;
+
+  {--- not absolute and have no relative dirs ---}
+  function e_IsValidResourceName (name: AnsiString): Boolean;
+
+  {-----------------------------------------------------------------------}
+  {--- try to open/create file in one dir from `dirs` in reverse order ---}
+  {--- e_OpenResourceRW tries to create if not exists                  ---}
+  {--- create dirs if not exists                                       ---}
+  {--- result <> nil, throws exceptions on errors                      ---}
+  {--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ---}
+  function e_CreateResource (dirs: SSArray; name: AnsiString): TStream;
+  function e_OpenResourceRO (dirs: SSArray; name: AnsiString): TStream;
+  function e_OpenResourceRW (dirs: SSArray; name: AnsiString): TStream;
+
+  {--- same as shared/utils ---}
+  function e_FindResource (dirs: SSArray; var name: AnsiString; nameIsDir: Boolean = false): Boolean;
+  function e_FindWad (dirs: SSArray; name: AnsiString): AnsiString;
+
+  {--- append dirs to 'path.wad:\file'. if disk is void, append defWad ---}
+  function e_GetResourcePath (dirs: SSArray; path: AnsiString; defWad: AnsiString): AnsiString;
+
+  {--- same as SysUtils.FinFirst ---}
+  function e_FindFirst (dirs: SSArray; name: AnsiString; attr: LongInt; out Rslt: TRawbyteSearchRec): LongInt;
+
+  {--- try to create directory from list, throws if no one directory created ---}
+  function e_GetDir (dirs: SSArray): AnsiString;
+
+implementation
+
+  uses WadReader, e_log;
+
+  type
+    SpawnProc = function (pathname: AnsiString): Tstream;
+
+  function e_UpperDir (path: AnsiString): AnsiString;
+    var i: Integer;
+  begin
+    i := High(path);
+    while (i >= 1) and (path[i] <> '/') and (path[i] <> '\') do Dec(i);
+    result := Copy(path, 1, i)
+  end;
+
+  function HasRelativeDirs (name: AnsiString): Boolean;
+    var i: Integer; ch: Char;
+  begin
+    i := 1;
+    result := false;
+    while (result = false) and (name[i] <> #0) do
+    begin
+      ch := name[i];
+      if (ch = '/') or (ch = '\') then
+      begin
+        Inc(i);
+        if name[i] = '.' then
+        begin
+          Inc(i);
+          if name[i] = '.' then
+          begin
+            Inc(i);
+            ch := name[i];
+            result := (ch = #0) or (ch = '/') or (ch = '\')
+          end
+        end
+      end
+      else
+      begin
+        Inc(i)
+      end
+    end
+  end;
+
+  function HasAbsoluteDirs (name: AnsiString): Boolean;
+  begin
+    result := (name = '') or (name[1] = '/') or (name[1] = '\')
+  end;
+
+  function e_IsValidResourceName (name: AnsiString): Boolean;
+  begin
+    result := (HasAbsoluteDirs(name) = false) and (HasRelativeDirs(name) = false)
+  end;
+
+  function SpawnStream (dirs: SSArray; name: AnsiString; p: SpawnProc; createNewDir: Boolean): TStream;
+    var i: Integer;
+  begin
+    result := nil;
+    assert(dirs <> nil);
+    assert(e_IsValidResourceName(name));
+    i := High(dirs);
+    while (i >= 0) and (result = nil) do
+    begin
+      try
+        if debug_e_res then
+          e_LogWritefln('  %s', [dirs[i]]);
+        if (createNewDir = false) or (ForceDirectories(dirs[i]) = true) then
+          result := p(e_CatPath(dirs[i], name))
+      finally
+        Dec(i)
+      end
+    end
+  end;
+
+  function e_CreateResource (dirs: SSArray; name: AnsiString): TStream;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_CreateResource %s', [name]);
+    result := SpawnStream(dirs, name, @createDiskFile, true);
+    if result = nil then
+      raise Exception.Create('can''t create resource "' + name + '"');
+  end;
+
+  function e_OpenResourceRO (dirs: SSArray; name: AnsiString): TStream;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_OpenResourceRO %s', [name]);
+    result := SpawnStream(dirs, name, @openDiskFileRO, false);
+    if result = nil then
+      raise EFileNotFoundException.Create('can''t open resource "' + name + '"')
+  end;
+
+  function e_OpenResourceRW (dirs: SSArray; name: AnsiString): TStream;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_OpenResourceRW %s', [name]);
+    result := SpawnStream(dirs, name, @openDiskFileRW, true);
+    if result = nil then
+      raise Exception.Create('can''t create resource "' + name + '"')
+  end;
+
+  function e_CatPath (a, b: AnsiString): AnsiString;
+  begin
+    if a = '' then
+      result := b
+    else if b = '' then
+      result := a
+    else
+      result := a + '/' + b
+  end;
+
+  function e_FindResource (dirs: SSArray; var name: AnsiString; nameIsDir: Boolean = false): Boolean;
+    var i: Integer; dir: AnsiString;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_FindResource %s (%s)', [name, nameIsDir]);
+    result := false;
+    assert(dirs <> nil);
+    assert(e_IsValidResourceName(name));
+    i := High(dirs); dir := name;
+    while (i >= 0) and (result = false) do
+    begin
+      dir := e_CatPath(dirs[i], name);
+      result := findFileCI(dir, nameIsDir);
+      if debug_e_res then
+        e_LogWritefln('  %s -> %s', [dir, result]);
+      Dec(i)
+    end;
+    if result = true then
+      name := dir;
+    if debug_e_res then
+      e_LogWritefln('  result = %s (%s)', [name, result]);
+  end;
+
+  function e_FindWad (dirs: SSArray; name: AnsiString): AnsiString;
+    var i: Integer;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_FindWad "%s"', [name]);
+    result := '';
+    assert(dirs <> nil);
+    assert(e_IsValidResourceName(name));
+    i := High(dirs);
+    while (i >= 0) and (result = '') do
+    begin
+      result := findDiskWad(dirs[i] + DirectorySeparator + name);
+      if debug_e_res then
+        e_LogWritefln('  %s -> %s', [dirs[i] + DirectorySeparator + name, result]);
+      Dec(i)
+    end
+  end;
+
+  function e_GetResourcePath (dirs: SSArray; path: AnsiString; defWad: AnsiString): AnsiString;
+    var diskName, fileName: AnsiString;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_GetResourcePath0 %s (%s)', [path, defWad]);
+    assert(dirs <> nil);
+    assert(path <> '');
+    assert(defWad <> '');
+    diskName := g_ExtractWadName(path);
+    fileName := g_ExtractFilePathName(path);
+    if diskName = '' then diskName := defWad else diskName := e_FindWad(dirs, diskName);
+    assert(diskName <> '', 'oh fuck, wad "' + diskName + '" not founded');
+    result := diskName + ':\' + fileName;
+    if debug_e_res then
+      e_LogWritefln('  this>>> %s', [result]);
+  end;
+
+  function e_FindFirst (dirs: SSArray; name: AnsiString; attr: LongInt; out Rslt: TRawbyteSearchRec): LongInt;
+    var i: Integer; dir: AnsiString;
+  begin
+    if debug_e_res then
+      e_LogWritefln('e_FindFirst %s', [name]);
+    assert(dirs <> nil);
+    assert(e_IsValidResourceName(name));
+    i := High(dirs); result := -1;
+    while (i >= 0) and (result <> 0) do
+    begin
+      dir := dirs[i] + DirectorySeparator + name;
+      result := FindFirst(dir, attr, Rslt);
+      if debug_e_res then
+        e_LogWritefln('  %s: %s -- %s', [i, dir, result]);
+      Dec(i); 
+    end
+  end;
+
+  function e_GetDir (dirs: SSArray): AnsiString;
+    var i: Integer;
+  begin
+    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.
index 69cf03426e052d182a4c420038dc5eee71d30a8f..561dc361399d0b4b413567cbd33d8318df712d50 100644 (file)
@@ -101,6 +101,7 @@ uses
   e_sound in '../engine/e_sound.pas',
   e_texture in '../engine/e_texture.pas',
   e_msg in '../engine/e_msg.pas',
+  e_res in '../engine/e_res.pas',
   utils in '../shared/utils.pas',
   xstreams in '../shared/xstreams.pas',
   sfs in '../sfs/sfs.pas',
@@ -242,15 +243,6 @@ begin
     Inc(f)
   end;
 
-  if LogFileName = '' then
-  begin
-{$IFDEF HEADLESS}
-    LogFileName := 'Doom2DF_H.log';
-{$ELSE}
-    LogFileName := 'Doom2DF.log';
-{$ENDIF}
-  end;
-
   if noct then
   begin
     Main()
index 4141f27aeb4b6f66d7f2809559ec52fa42c7e184..6b8ddc82c2538e53156457e687d7e2a0fa1ae1c5 100644 (file)
@@ -86,9 +86,13 @@ implementation
 
 uses
   g_textures, g_main, e_graphics, e_input, g_game,
-  SysUtils, g_basic, g_options, Math, g_touch,
+  SysUtils, g_basic, g_options, Math, g_touch, e_res,
   g_menu, g_gui, g_language, g_net, g_netmsg, e_log, conbuf;
 
+const
+  configScript = 'dfconfig.cfg';
+  autoexecScript = 'autoexec.cfg';
+  configComment = 'generated by doom2d, do not modify';
 
 type
   PCommand = ^TCommand;
@@ -580,7 +584,7 @@ begin
   begin
     // exec <filename>
     if Length(p) = 2 then
-      g_Console_ReadConfig(GameDir + '/' + p[1])
+      g_Console_ReadConfig(p[1])
     else
       g_Console_Add('exec <script file>');
   end;
@@ -589,9 +593,14 @@ begin
   begin
     // writeconfig <filename>
     if Length(p) = 2 then
-      g_Console_WriteConfig(GameDir + '/' + p[1])
+    begin
+      s := e_GetDir(ConfigDirs);
+      g_Console_WriteConfig(e_CatPath(s, p[1]))
+    end
     else
-      g_Console_Add('writeconfig <file>');
+    begin
+      g_Console_Add('writeconfig <file>')
+    end
   end;
 
   if (cmd = 'ver') or (cmd = 'version') then
@@ -1015,8 +1024,8 @@ begin
   WhitelistCommand('g_timelimit');
 
   g_Console_ResetBinds;
-  g_Console_ReadConfig(GameDir + '/dfconfig.cfg');
-  g_Console_ReadConfig(GameDir + '/autoexec.cfg');
+  g_Console_ReadConfig(configScript);
+  g_Console_ReadConfig(autoexecScript);
   gParsingBinds := False;
 end;
 
@@ -1881,8 +1890,10 @@ end;
 procedure g_Console_ReadConfig (filename: String);
   var f: TextFile; s: AnsiString; i, len: Integer;
 begin
-  if FileExists(filename) then
+  e_LogWritefln('g_Console_ReadConfig (1) "%s"', [filename]);
+  if e_FindResource(ConfigDirs, filename, false) = true then
   begin
+    e_LogWritefln('g_Console_ReadConfig (2) "%s"', [filename]);
     AssignFile(f, filename);
     Reset(f);
     while not EOF(f) do
@@ -1908,7 +1919,7 @@ procedure g_Console_WriteConfig (filename: String);
 begin
   AssignFile(f, filename);
   Rewrite(f);
-  WriteLn(f, '// generated by doom2d, do not modify');
+  WriteLn(f, '// ' + configComment);
   WriteLn(f, 'unbindall');
   for i := 0 to e_MaxInputKeys - 1 do
     if (Length(gInputBinds[i].down) > 0) or (Length(gInputBinds[i].up) > 0) then
@@ -1950,10 +1961,13 @@ begin
 end;
 
 procedure g_Console_WriteGameConfig;
+  var s: AnsiString;
 begin
-  if gParsingBinds then
-    Exit;
-  g_Console_WriteConfig(GameDir + '/dfconfig.cfg');
+  if gParsingBinds = false then
+  begin
+    s := e_GetDir(ConfigDirs);
+    g_Console_WriteConfig(e_CatPath(s, configScript))
+  end
 end;
 
 initialization
index 22265652546f9a396905fc16ddd1d86ada2eb107..0505dceb74d336c6d8a2afe83ea699fc9c13782c 100644 (file)
@@ -377,7 +377,7 @@ uses
 {$IFDEF ENABLE_HOLMES}
   g_holmes,
 {$ENDIF}
-  e_texture, g_textures, g_main, g_window, g_menu,
+  e_texture, e_res, g_textures, g_main, g_window, g_menu,
   e_input, e_log, g_console, g_items, g_map, g_panel,
   g_playermodel, g_gfx, g_options, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
@@ -762,17 +762,17 @@ var
   cfg: TConfig;
   p: Pointer;
   {b, }len: Integer;
-  s: string;
+  s: AnsiString;
 begin
   g_Game_FreeWAD();
   gGameSettings.WAD := WAD;
   if not (gGameSettings.GameMode in [GM_COOP, GM_SINGLE]) then
     Exit;
 
-  MegaWAD.info := g_Game_GetMegaWADInfo(MapsDir + WAD);
+  MegaWAD.info := g_Game_GetMegaWADInfo(WAD);
 
   w := TWADFile.Create();
-  w.ReadFile(MapsDir + WAD);
+  w.ReadFile(WAD);
 
   if not w.GetResource('INTERSCRIPT', p, len) then
   begin
@@ -811,18 +811,16 @@ begin
   MegaWAD.endpic := cfg.ReadStr('megawad', 'endpic', '');
   if MegaWAD.endpic <> '' then
   begin
-    s := g_ExtractWadName(MegaWAD.endpic);
-    if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
     TEXTUREFILTER := GL_LINEAR;
-    g_Texture_CreateWADEx('TEXTURE_endpic', s+MegaWAD.endpic);
+    s := e_GetResourcePath(WadDirs, MegaWAD.endpic, WAD);
+    g_Texture_CreateWADEx('TEXTURE_endpic', s);
     TEXTUREFILTER := GL_NEAREST;
   end;
   MegaWAD.endmus := cfg.ReadStr('megawad', 'endmus', 'Standart.wad:D2DMUS\ÊÎÍÅÖ');
   if MegaWAD.endmus <> '' then
   begin
-    s := g_ExtractWadName(MegaWAD.endmus);
-    if s = '' then s := MapsDir+WAD else s := GameDir+'/wads/';
-    g_Sound_CreateWADEx('MUSIC_endmus', s+MegaWAD.endmus, True);
+    s := e_GetResourcePath(WadDirs, MegaWAD.endmus, WAD);
+    g_Sound_CreateWADEx('MUSIC_endmus', s, True);
   end;
 
   cfg.Free();
@@ -1304,23 +1302,23 @@ begin
     g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
     g_PlayerModel_LoadData();
 
-    if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
+    if e_FindFirst(ModelDirs, '*.wad', faAnyFile, SR) = 0 then
       repeat
-        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+        if not g_PlayerModel_Load(e_FindWad(ModelDirs, SR.Name)) then
           e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning);
       until FindNext(SR) <> 0;
     FindClose(SR);
 
-    if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
+    if e_FindFirst(ModelDirs, '*.pk3', faAnyFile, SR) = 0 then
       repeat
-        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+        if not g_PlayerModel_Load(e_FindWad(ModelDirs, SR.Name)) then
           e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning);
       until FindNext(SR) <> 0;
     FindClose(SR);
 
-    if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
+    if e_FindFirst(ModelDirs, '*.zip', faAnyFile, SR) = 0 then
       repeat
-        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+        if not g_PlayerModel_Load(e_FindWad(ModelDirs, SR.Name)) then
           e_WriteLog(Format('Error loading model %s', [SR.Name]), TMsgType.Warning);
       until FindNext(SR) <> 0;
     FindClose(SR);
@@ -4685,7 +4683,8 @@ begin
             end;
             e_LogWritefln('using downloaded map wad [%s] for [%s]`', [newResPath, WadName], TMsgType.Notify);
           end;
-          newResPath := ExtractRelativePath(MapsDir, newResPath);
+          //newResPath := ExtractRelativePath(MapsDir, newResPath);
+          
 
           gPlayer1 := g_Player_Get(g_Player_Create(gPlayer1Settings.Model,
                                                    gPlayer1Settings.Color,
@@ -4762,9 +4761,14 @@ begin
   e_WriteLog('NET: Connection successful.', TMsgType.Notify);
 end;
 
-procedure g_Game_SaveOptions();
+procedure g_Game_SaveOptions;
+  var s: AnsiString;
 begin
-  g_Options_Write_Video(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Video(s + '/' + CONFIG_FILENAME)
+  else
+    e_LogWritefln('unable to find or create directory for configs', []);
 end;
 
 procedure g_Game_ChangeMap(const MapPath: String);
@@ -4791,6 +4795,7 @@ begin
   if g_Game_IsClient then
     Exit;
   map := g_ExtractFileName(gMapInfo.Map);
+  e_LogWritefln('g_Game_Restart: map = "%s" gCurrentMapFileName = "%s"', [map, gCurrentMapFileName]);
 
   MessageTime := 0;
   gGameOn := False;
@@ -4826,7 +4831,8 @@ begin
     ResName := g_ExtractFileName(Map);
     if g_Game_IsServer then
     begin
-      nws := findDiskWad(MapsDir+NewWAD);
+//     nws := e_FindWad(MapDirs, NewWAD);
+      nws := NewWAD;
       if (length(nws) = 0) then
       begin
         ResName := '';
@@ -4844,7 +4850,9 @@ begin
     ResName := Map;
 
   //writeln('********: gsw=', gGameSettings.WAD, '; rn=', ResName);
-  Result := (ResName <> '') and g_Map_Load(MapsDir + gGameSettings.WAD + ':\' + ResName);
+  result := false;
+  if ResName <> '' then
+    result := g_Map_Load(gGameSettings.WAD + ':\' + ResName);
   if Result then
     begin
       g_Player_ResetAll(Force or gLastMap, gGameSettings.GameType = GT_SINGLE);
@@ -4942,11 +4950,11 @@ begin
   g_Game_ExecuteEvent('onmapstart');
 end;
 
-procedure SetFirstLevel();
+procedure SetFirstLevel;
 begin
   gNextMap := '';
 
-  MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
+  MapList := g_Map_GetMapsList(gGameSettings.WAD);
   if MapList = nil then
     Exit;
 
@@ -4974,7 +4982,7 @@ begin
     if gGameSettings.GameMode = GM_COOP then
       g_Player_RememberAll;
 
-    if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
+    if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
     begin
       gLastMap := True;
       if gGameSettings.GameMode = GM_COOP then
@@ -4983,7 +4991,7 @@ begin
       gStatsPressed := True;
       gNextMap := 'MAP01';
 
-      if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + gNextMap) then
+      if not g_Map_Exist(gGameSettings.WAD + ':\' + gNextMap) then
         g_Game_NextLevel;
 
       if g_Game_IsNet then
@@ -5011,7 +5019,7 @@ end;
 
 procedure g_Game_ClientWAD(NewWAD: String; const WHash: TMD5Digest);
 var
-  gWAD, xwad: String;
+  gWAD{, xwad}: String;
 begin
   if not g_Game_IsClient then Exit;
   //e_LogWritefln('*** g_Game_ClientWAD: `%s`', [NewWAD]);
@@ -5024,10 +5032,15 @@ begin
     Exit;
   end;
 
+(*
   xwad := ExtractRelativePath(MapsDir, gWAD);
   e_LogWritefln('using downloaded client map wad [%s] for [%s]`', [xwad, NewWAD], TMsgType.Notify);
   NewWAD := xwad;
   g_Game_LoadWAD(NewWAD);
+*)
+
+  e_LogWritefln('using downloaded client map wad [%s]`', [NewWAD], TMsgType.Notify);
+  g_Game_LoadWAD(NewWAD);
 
   {
   if LowerCase(NewWAD) = LowerCase(gGameSettings.WAD) then Exit;
@@ -5156,7 +5169,7 @@ var
 begin
   Result := '';
 
-  MapList := g_Map_GetMapsList(MapsDir + gGameSettings.WAD);
+  MapList := g_Map_GetMapsList(gGameSettings.WAD);
   if MapList = nil then
     Exit;
 
@@ -5178,7 +5191,7 @@ begin
     else
       Result := MapList[MapIndex + 1];
 
-    if not g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + Result) then Result := Map;
+    if not g_Map_Exist(gGameSettings.WAD + ':\' + Result) then Result := Map;
   end;
 
   MapList := nil;
@@ -5439,27 +5452,34 @@ begin
   begin
     if (Length(P) > 1) then
       NetInterpLevel := StrToIntDef(P[1], NetInterpLevel);
-
     g_Console_Add('net_interp = ' + IntToStr(NetInterpLevel));
-    config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
-    config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
-    config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
-    config.Free();
+    s := e_GetDir(ConfigDirs);
+    if s <> '' then
+    begin
+      config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME);
+      config.WriteInt('Client', 'InterpolationSteps', NetInterpLevel);
+      config.SaveFile(s + '/' + CONFIG_FILENAME);
+      config.Free
+    end
   end
   else if cmd = 'net_forceplayerupdate' then
   begin
-    if (Length(P) > 1) and
-       ((P[1] = '1') or (P[1] = '0')) then
+    if (Length(P) > 1) and ((P[1] = '1') or (P[1] = '0')) then
       NetForcePlayerUpdate := (P[1][1] = '1');
 
     if NetForcePlayerUpdate then
       g_Console_Add('net_forceplayerupdate = 1')
     else
       g_Console_Add('net_forceplayerupdate = 0');
-    config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
-    config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
-    config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
-    config.Free();
+
+    s := e_GetDir(ConfigDirs);
+    if s <> '' then
+    begin
+      config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME);
+      config.WriteBool('Client', 'ForcePlayerUpdate', NetForcePlayerUpdate);
+      config.SaveFile(s + '/' + CONFIG_FILENAME);
+      config.Free
+    end
   end
   else if cmd = 'net_predictself' then
   begin
@@ -5471,10 +5491,15 @@ begin
       g_Console_Add('net_predictself = 1')
     else
       g_Console_Add('net_predictself = 0');
-    config := TConfig.CreateFile(GameDir+'/'+CONFIG_FILENAME);
-    config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
-    config.SaveFile(GameDir+'/'+CONFIG_FILENAME);
-    config.Free();
+
+    s := e_GetDir(ConfigDirs);
+    if s <> '' then
+    begin
+      config := TConfig.CreateFile(s + '/' + CONFIG_FILENAME);
+      config.WriteBool('Client', 'PredictSelf', NetPredictSelf);
+      config.SaveFile(s + '/' + CONFIG_FILENAME);
+      config.Free
+    end
   end
   else if cmd = 'sv_name' then
   begin
@@ -6134,6 +6159,7 @@ var
   prt: Word;
   nm: Boolean;
   listen: LongWord;
+  found: Boolean;
 begin
 // Îáùèå êîìàíäû:
   cmd := LowerCase(P[0]);
@@ -6541,22 +6567,26 @@ begin
       g_Console_Add(cmd + ' <WAD> [MAP] [# players]');
       Exit;
     end;
-    // Èãðà åù¸ íå çàïóùåíà, ñíà÷àëà íàì íàäî çàãðóçèòü êàêîé-òî WAD
-    P[1] := addWadExtension(P[1]);
-    if FileExists(MapsDir + P[1]) then
+    // game not started yet, load fist map from some wad
+    found := false;
+    s := addWadExtension(P[1]);
+    found := e_FindResource(AllMapDirs, s);
+    P[1] := s;
+    if found then
     begin
-      // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
+      P[1] := ExpandFileName(P[1]);
+      // if map not choosed then set first map
       if Length(P) < 3 then
       begin
         SetLength(P, 3);
-        P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
+        P[2] := g_Game_GetFirstMap(P[1]);
       end;
 
       s := P[1] + ':\' + UpperCase(P[2]);
 
-      if g_Map_Exist(MapsDir + s) then
+      if g_Map_Exist(s) then
       begin
-        // Çàïóñêàåì ñâîþ èãðó
+        // start game
         g_Game_Free();
         with gGameSettings do
         begin
@@ -6596,43 +6626,46 @@ begin
       Exit;
     prt := StrToIntDef(P[2], 25666);
 
-    P[3] := addWadExtension(P[3]);
-    if FileExists(MapsDir + P[3]) then
+    s := addWadExtension(P[3]);
+    found := e_FindResource(AllMapDirs, s);
+    P[3] := s;
+    if found then
     begin
-      // Åñëè êàðòà íå óêàçàíà, áåð¸ì ïåðâóþ êàðòó â ôàéëå
+      // get first map in wad, if not specified
       if Length(P) < 5 then
       begin
         SetLength(P, 5);
-        P[4] := g_Game_GetFirstMap(MapsDir + P[1]);
+        P[4] := g_Game_GetFirstMap(P[1]);
       end;
-
       s := P[3] + ':\' + UpperCase(P[4]);
-
-      if g_Map_Exist(MapsDir + s) then
+      if g_Map_Exist(s) then
       begin
-        // Çàïóñêàåì ñâîþ èãðó
+        // start game
         g_Game_Free();
         with gGameSettings do
         begin
           GameMode := g_Game_TextToMode(gcGameMode);
-          if gSwitchGameMode <> GM_NONE then
-            GameMode := gSwitchGameMode;
+          if gSwitchGameMode <> GM_NONE then GameMode := gSwitchGameMode;
           if GameMode = GM_NONE then GameMode := GM_DM;
           if GameMode = GM_SINGLE then GameMode := GM_COOP;
           b := 0;
           if Length(P) >= 6 then
             b := StrToIntDef(P[5], 0);
-          g_Game_StartServer(s, GameMode, TimeLimit,
-                             GoalLimit, MaxLives, Options, b, listen, prt);
-        end;
+          g_Game_StartServer(s, GameMode, TimeLimit, GoalLimit, MaxLives, Options, b, listen, prt)
+        end
       end
       else
+      begin
         if P[4] = '' then
           g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[3]]))
         else
-          g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]));
-    end else
-      g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]));
+          g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [UpperCase(P[4]), P[3]]))
+      end
+    end
+    else
+    begin
+      g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[3]]))
+    end
   end
   else if cmd = 'map' then
   begin
@@ -6641,92 +6674,125 @@ begin
       if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
       begin
         g_Console_Add(cmd + ' <MAP>');
-        g_Console_Add(cmd + ' <WAD> [MAP]');
-      end else
-        g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
-    end else
+        g_Console_Add(cmd + ' <WAD> [MAP]')
+      end
+      else
+      begin
+        g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
+      end
+    end
+    else
+    begin
       if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
       begin
-        // Èä¸ò ñâîÿ èãðà èëè ñåðâåð
         if Length(P) < 3 then
         begin
-          // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
+          // first param is map or wad
           s := UpperCase(P[1]);
-          if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
-          begin // Êàðòà íàøëàñü
+          if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
+          begin
             gExitByTrigger := False;
             if gGameOn then
-            begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
+            begin
+              // already in game, finish current map
               gNextMap := s;
               gExit := EXIT_ENDLEVELCUSTOM;
             end
-            else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
-              g_Game_ChangeMap(s);
-          end else
+            else
+            begin
+              // intermission, so change map immediately
+              g_Game_ChangeMap(s)
+            end
+          end
+          else
           begin
-            // Òàêîé êàðòû íåò, èùåì WAD ôàéë
-            pw := findDiskWad(MapsDir + P[1]);
+            s := P[1];
+            found := e_FindResource(AllMapDirs, s);
+            P[1] := s;
             g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, 'WAD ' + P[1]]));
-            if FileExists(pw) then
+            if found then
             begin
-              // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
+              // no such map, found wad
               SetLength(P, 3);
-              P[1] := ExtractRelativePath(MapsDir, pw);
-              P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
-
+              P[1] := ExpandFileName(pw);
+              P[2] := g_Game_GetFirstMap(P[1]);
               s := P[1] + ':\' + P[2];
-
-              if g_Map_Exist(MapsDir + s) then
+              if g_Map_Exist(s) then
               begin
                 gExitByTrigger := False;
                 if gGameOn then
-                begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
+                begin
+                  // already in game, finish current map
                   gNextMap := s;
-                  gExit := EXIT_ENDLEVELCUSTOM;
+                  gExit := EXIT_ENDLEVELCUSTOM
                 end
-                else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
-                  g_Game_ChangeMap(s);
-              end else
+                else
+                begin
+                  // intermission, so change map immediately
+                  g_Game_ChangeMap(s)
+                end
+              end
+              else
+              begin
                 if P[2] = '' then
                   g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
                 else
-                  g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
-            end else
-              g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
+                  g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
+              end
+            end
+            else
+            begin
+              g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
+            end
           end;
-        end else
+        end
+        else
         begin
-          // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
-          P[1] := addWadExtension(P[1]);
-          if FileExists(MapsDir + P[1]) then
+          s := addWadExtension(P[1]);
+          found := e_FindResource(AllMapDirs, s);
+          P[1] := s;
+          if found then
           begin
-            // Íàøëè WAD ôàéë
             P[2] := UpperCase(P[2]);
             s := P[1] + ':\' + P[2];
-
-            if g_Map_Exist(MapsDir + s) then
-            begin // Íàøëè êàðòó
+            if g_Map_Exist(s) then
+            begin
               gExitByTrigger := False;
               if gGameOn then
-              begin // Èä¸ò èãðà - çàâåðøàåì óðîâåíü
+              begin
                 gNextMap := s;
-                gExit := EXIT_ENDLEVELCUSTOM;
+                gExit := EXIT_ENDLEVELCUSTOM
               end
-              else // Èíòåðìèññèÿ - ñðàçó çàãðóæàåì êàðòó
-                g_Game_ChangeMap(s);
-            end else
-              g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
-          end else
-            g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
-        end;
-      end else
-        g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
+              else
+              begin
+                g_Game_ChangeMap(s)
+              end
+            end
+            else
+            begin
+              g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
+            end
+          end
+          else
+          begin
+            g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
+          end
+        end
+      end
+      else
+      begin
+        g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
+      end
+    end
   end
   else if cmd = 'nextmap' then
   begin
     if not(gGameOn or (gState = STATE_INTERCUSTOM)) then
+    begin
       g_Console_Add(_lc[I_MSG_NOT_GAME])
-    else begin
+    end
+    else
+    begin
       nm := True;
       if Length(P) = 1 then
       begin
@@ -6734,113 +6800,148 @@ begin
         begin
           g_Console_Add(cmd + ' <MAP>');
           g_Console_Add(cmd + ' <WAD> [MAP]');
-        end else begin
+        end
+        else
+        begin
           nm := False;
           g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
         end;
-      end else
+      end
+      else
       begin
         nm := False;
         if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
         begin
           if Length(P) < 3 then
           begin
-            // Ïåðâûé ïàðàìåòð - ëèáî êàðòà, ëèáî èìÿ WAD ôàéëà
+            // first param is map or wad
             s := UpperCase(P[1]);
-            if g_Map_Exist(MapsDir + gGameSettings.WAD + ':\' + s) then
-            begin // Êàðòà íàøëàñü
+            if g_Map_Exist(gGameSettings.WAD + ':\' + s) then
+            begin
+              // map founded
               gExitByTrigger := False;
               gNextMap := s;
               nm := True;
-            end else
+            end
+            else
             begin
-              // Òàêîé êàðòû íåò, èùåì WAD ôàéë
-              P[1] := addWadExtension(P[1]);
+              // no such map, found wad
+              pw := addWadExtension(P[1]);
+              found := e_FindResource(MapDirs, pw);
+              if not found then
+                found := e_FindResource(WadDirs, pw);
+              P[1] := pw;
               g_Console_Add(Format(_lc[I_MSG_NO_MAP_FALLBACK], [s, P[1]]));
-              if FileExists(MapsDir + P[1]) then
+              if found then
               begin
-                // Ïàðàìåòðà êàðòû íåò, ïîýòîìó ñòàâèì ïåðâóþ èç ôàéëà
+                // map not specified, select first map
                 SetLength(P, 3);
-                P[2] := g_Game_GetFirstMap(MapsDir + P[1]);
-
+                P[2] := g_Game_GetFirstMap(P[1]);
                 s := P[1] + ':\' + P[2];
-
-                if g_Map_Exist(MapsDir + s) then
-                begin // Óñòàíàâëèâàåì êàðòó
+                if g_Map_Exist(s) then
+                begin
                   gExitByTrigger := False;
                   gNextMap := s;
-                  nm := True;
-                end else
+                  nm := True
+                end
+                else
+                begin
                   if P[2] = '' then
                     g_Console_Add(Format(_lc[I_MSG_NO_MAPS], [P[1]]))
                   else
-                    g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
-              end else
-                g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
-            end;
-          end else
+                    g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
+                end
+              end
+              else
+              begin
+                g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
+              end
+            end
+          end
+          else
           begin
-            // Óêàçàíî äâà ïàðàìåòðà, çíà÷èò ïåðâûé - WAD ôàéë, à âòîðîé - êàðòà
-            P[1] := addWadExtension(P[1]);
-            if FileExists(MapsDir + P[1]) then
+            // specified two params wad + map
+            pw := addWadExtension(P[1]);
+            found := e_FindResource(MapDirs, pw);
+            if not found then
+              found := e_FindResource(MapDirs, pw);
+            P[1] := pw;
+            if found then
             begin
-              // Íàøëè WAD ôàéë
               P[2] := UpperCase(P[2]);
               s := P[1] + ':\' + P[2];
-
-              if g_Map_Exist(MapsDir + s) then
-              begin // Íàøëè êàðòó
+              if g_Map_Exist(s) then
+              begin
                 gExitByTrigger := False;
                 gNextMap := s;
-                nm := True;
-              end else
-                g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]));
-            end else
-              g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]));
-          end;
-        end else
-          g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
+                nm := True
+              end
+              else
+              begin
+                g_Console_Add(Format(_lc[I_MSG_NO_MAP], [P[2]]))
+              end
+            end
+            else
+            begin
+              g_Console_Add(Format(_lc[I_MSG_NO_WAD], [P[1]]))
+            end
+          end
+        end
+        else
+        begin
+          g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
+        end
       end;
       if nm then
+      begin
         if gNextMap = '' then
           g_Console_Add(_lc[I_MSG_NEXTMAP_UNSET])
         else
-          g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]));
-    end;
+          g_Console_Add(Format(_lc[I_MSG_NEXTMAP_SET], [gNextMap]))
+      end
+    end
   end
   else if (cmd = 'endmap') or (cmd = 'goodbye') then
   begin
     if not gGameOn then
+    begin
       g_Console_Add(_lc[I_MSG_NOT_GAME])
+    end
     else
+    begin
       if g_Game_IsServer and (gGameSettings.GameType <> GT_SINGLE) then
       begin
         gExitByTrigger := False;
-        // Ñëåäóþùàÿ êàðòà íå çàäàíà, ïðîáóåì íàéòè òðèããåð Âûõîä
+        // next map not specified, try to find trigger EXIT
         if (gNextMap = '') and (gTriggers <> nil) then
+        begin
           for a := 0 to High(gTriggers) do
+          begin
             if gTriggers[a].TriggerType = TRIGGER_EXIT then
             begin
               gExitByTrigger := True;
               //gNextMap := gTriggers[a].Data.MapName;
               gNextMap := gTriggers[a].tgcMap;
-              Break;
-            end;
-        // Èùåì ñëåäóþùóþ êàðòó â WAD ôàéëå
+              Break
+            end
+          end
+        end;
         if gNextMap = '' then
           gNextMap := g_Game_GetNextMap();
-        // Ïðîâåðÿåì, íå çàäàí ëè WAD ôàéë ðåñóðñíîé ñòðîêîé
         if not isWadPath(gNextMap) then
           s := gGameSettings.WAD + ':\' + gNextMap
         else
           s := gNextMap;
-        // Åñëè êàðòà íàéäåíà, âûõîäèì ñ óðîâíÿ
-        if g_Map_Exist(MapsDir + s) then
+        if g_Map_Exist(s) then
           gExit := EXIT_ENDLEVELCUSTOM
         else
-          g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]));
-      end else
-        g_Console_Add(_lc[I_MSG_GM_UNAVAIL]);
+          g_Console_Add(Format(_lc[I_MSG_NO_MAP], [gNextMap]))
+      end
+      else
+      begin
+        g_Console_Add(_lc[I_MSG_GM_UNAVAIL])
+      end
+    end
   end
   else if (cmd = 'event') then
   begin
@@ -7095,47 +7196,20 @@ begin
   end;
 end;
 
-procedure g_TakeScreenShot();
-var
-  a: Word;
-  FileName: string;
-  ssdir, t: string;
-  st: TStream;
-  ok: Boolean;
+procedure g_TakeScreenShot;
+  var s: TStream; t: TDateTime; date, name: String;
 begin
   if e_NoGraphics then Exit;
-  ssdir := GameDir+'/screenshots';
-  if not findFileCI(ssdir, true) then
-  begin
-    // try to create dir
-    try
-      CreateDir(ssdir);
-    except
-    end;
-    if not findFileCI(ssdir, true) then exit; // alas
-  end;
+  t := Now;
+  DateTimeToString(date, 'yyyy-mm-dd-hh-nn-ss', t);
+  name := 'screenshot-' + date + '.png';
   try
-    for a := 1 to High(Word) do
-    begin
-      FileName := Format(ssdir+'screenshot%.3d.png', [a]);
-      t := FileName;
-      if findFileCI(t, true) then continue;
-      if not findFileCI(FileName) then
-      begin
-        ok := false;
-        st := createDiskFile(FileName);
-        try
-          e_MakeScreenshot(st, gScreenWidth, gScreenHeight);
-          ok := true;
-        finally
-          st.Free();
-        end;
-        if not ok then try DeleteFile(FileName); except end else g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [ExtractFileName(FileName)]));
-        break;
-      end;
-    end;
+    s := e_CreateResource(ScreenshotDirs, name);
+    e_MakeScreenshot(s, gScreenWidth, gScreenHeight);
+    g_Console_Add(Format(_lc[I_CONSOLE_SCREENSHOT], [name]))
   except
-  end;
+    g_Console_Add('oh shit, i can''t create screenshot!')
+  end
 end;
 
 procedure g_Game_InGameMenu(Show: Boolean);
@@ -7812,13 +7886,17 @@ begin
   // Override map to test:
     s := LowerCase(Find_Param_Value(pars, '-testmap'));
     if s <> '' then
-      gTestMap := MapsDir + s;
+    begin
+      if e_IsValidResourceName(s) then
+        e_FindResource(AllMapDirs, s);
+      gTestMap := ExpandFileName(s);
+    end;
 
   // Delete test map after play:
     s := Find_Param_Value(pars, '--testdelete');
     if (s <> '') then
     begin
-      gMapToDelete := MapsDir + map;
+      //gMapToDelete := MapsDir + map;
       e_WriteLog('"--testdelete" is deprecated, use --tempdelete.', TMsgType.Fatal);
       Halt(1);
     end;
@@ -7850,8 +7928,8 @@ begin
   s := Find_Param_Value(pars, '-exec');
   if s <> '' then
   begin
-    if not isWadPath(s) then
-      s := GameDir + '/' + s;
+//    if not isWadPath(s) then
+//      s := GameDir + '/' + s;
 
     {$I-}
     AssignFile(F, s);
index 67f3b8a6ebd8f5e4fbfb4e2893229b7db0e94afd..0c7f12659e2584350039ce93d1ec01da7f8556c5 100644 (file)
@@ -401,6 +401,7 @@ type
     procedure OnMessage(var Msg: TMessage); override;
     procedure Draw(); override;
     procedure AddItem(Item: String);
+    function ItemExists (item: String): Boolean;
     procedure SelectItem(Item: String);
     procedure Clear();
     function  GetWidth(): Integer; override;
@@ -420,22 +421,21 @@ type
 
   TGUIFileListBox = class(TGUIListBox)
   private
-    FBasePath: String;
-    FPath: String;
+    FSubPath: String;
     FFileMask: String;
     FDirs: Boolean;
+    FBaseList: SSArray; // highter index have highter priority
 
-    procedure OpenDir(path: String);
+    procedure ScanDirs;
 
   public
-    procedure OnMessage(var Msg: TMessage); override;
-    procedure SetBase(path: String);
+    procedure OnMessage (var Msg: TMessage); override;
+    procedure SetBase (dirs: SSArray; path: String = '');
     function  SelectedItem(): String;
-    procedure UpdateFileList();
+    procedure UpdateFileList;
 
     property Dirs: Boolean read FDirs write FDirs;
     property FileMask: String read FFileMask write FFileMask;
-    property Path: String read FPath;
   end;
 
   TGUIMemo = class(TGUIControl)
@@ -551,7 +551,7 @@ implementation
 
 uses
   {$INCLUDE ../nogl/noGLuses.inc}
-  g_textures, g_sound, SysUtils,
+  g_textures, g_sound, SysUtils, e_res,
   g_game, Math, StrUtils, g_player, g_options,
   g_map, g_weapons, xdynrec, wadreader;
 
@@ -2983,7 +2983,15 @@ begin
   if FSort then g_Basic.Sort(FItems);
 end;
 
-procedure TGUIListBox.Clear();
+function TGUIListBox.ItemExists (item: String): Boolean;
+  var i: Integer;
+begin
+  i := 0;
+  while (i <= High(FItems)) and (FItems[i] <> item) do Inc(i);
+  result := i <= High(FItems)
+end;
+
+procedure TGUIListBox.Clear;
 begin
   FItems := nil;
 
@@ -3173,7 +3181,7 @@ end;
 
 procedure TGUIFileListBox.OnMessage(var Msg: TMessage);
 var
-  a, b: Integer;
+  a, b: Integer; s: AnsiString;
 begin
   if not FEnabled then
     Exit;
@@ -3256,7 +3264,18 @@ begin
                 begin
                   if FItems[FIndex][1] = #29 then // Ïàïêà
                   begin
-                    OpenDir(FPath+Copy(FItems[FIndex], 2, 255));
+                    if FItems[FIndex] = #29 + '..' then
+                    begin
+                      e_LogWritefln('TGUIFileListBox: Upper dir "%s" -> "%s"', [FSubPath, e_UpperDir(FSubPath)]);
+                      FSubPath := e_UpperDir(FSubPath)
+                    end
+                    else
+                    begin
+                      s := Copy(AnsiString(FItems[FIndex]), 2);
+                      e_LogWritefln('TGUIFileListBox: Enter dir "%s" -> "%s"', [FSubPath, e_CatPath(FSubPath, s)]);
+                      FSubPath := e_CatPath(FSubPath, s);
+                    end;
+                    ScanDirs;
                     FIndex := 0;
                     Exit;
                   end;
@@ -3289,70 +3308,78 @@ begin
     end;
 end;
 
-procedure TGUIFileListBox.OpenDir(path: String);
-var
-  SR: TSearchRec;
-  i: Integer;
-  sm, sc: string;
+procedure TGUIFileListBox.ScanDirs;
+  var i, j: Integer; path: AnsiString; SR: TSearchRec; sm, sc: String;
 begin
-  Clear();
-
-  path := IncludeTrailingPathDelimiter(path);
-  path := ExpandFileName(path);
+  Clear;
 
-  // Êàòàëîãè:
-  if FDirs then
+  i := High(FBaseList);
+  while i >= 0 do
   begin
-    if FindFirst(path+'*', faDirectory, SR) = 0 then
-    repeat
-      if not LongBool(SR.Attr and faDirectory) then
-        Continue;
-      if (SR.Name = '.') or
-         ((SR.Name = '..') and (path = ExpandFileName(FBasePath))) then
-        Continue;
-
-      AddItem(#1 + SR.Name);
-    until FindNext(SR) <> 0;
-
-    FindClose(SR);
+    path := e_CatPath(FBaseList[i], FSubPath);
+    if FDirs then
+    begin
+      if FindFirst(path + '/' + '*', faDirectory, SR) = 0 then
+      begin
+        repeat
+          if LongBool(SR.Attr and faDirectory) then
+            if (SR.Name <> '.') and ((FSubPath <> '') or (SR.Name <> '..')) then
+              if Self.ItemExists(#1 + SR.Name) = false then
+                Self.AddItem(#1 + SR.Name)
+        until FindNext(SR) <> 0
+      end;
+      FindClose(SR)
+    end;
+    Dec(i)
   end;
 
-  // Ôàéëû:
-  sm := FFileMask;
-  while sm <> '' do
+  i := High(FBaseList);
+  while i >= 0 do
   begin
-    i := Pos('|', sm);
-    if i = 0 then i := length(sm)+1;
-    sc := Copy(sm, 1, i-1);
-    Delete(sm, 1, i);
-    if FindFirst(path+sc, faAnyFile, SR) = 0 then repeat AddItem(SR.Name); until FindNext(SR) <> 0;
-    FindClose(SR);
+    path := e_CatPath(FBaseList[i], FSubPath);
+    sm := FFileMask;
+    while sm <> '' do
+    begin
+      j := Pos('|', sm);
+      if j = 0 then
+        j := length(sm) + 1;
+      sc := Copy(sm, 1, j - 1);
+      Delete(sm, 1, j);
+      if FindFirst(path + '/' + sc, faAnyFile, SR) = 0 then
+      begin
+        repeat
+          if Self.ItemExists(SR.Name) = false then
+            AddItem(SR.Name)
+        until FindNext(SR) <> 0
+      end;
+      FindClose(SR)
+    end;
+    Dec(i)
   end;
 
   for i := 0 to High(FItems) do
     if FItems[i][1] = #1 then
       FItems[i][1] := #29;
-
-  FPath := path;
 end;
 
-procedure TGUIFileListBox.SetBase(path: String);
+procedure TGUIFileListBox.SetBase (dirs: SSArray; path: String = '');
 begin
-  FBasePath := path;
-  OpenDir(FBasePath);
+  FBaseList := dirs;
+  FSubPath := path;
+  ScanDirs
 end;
 
-function TGUIFileListBox.SelectedItem(): String;
+function TGUIFileListBox.SelectedItem (): String;
+  var s: AnsiString;
 begin
-  Result := '';
-
-  if (FIndex = -1) or (FItems = nil) or
-     (FIndex > High(FItems)) or
-     (FItems[FIndex][1] = '/') or
-     (FItems[FIndex][1] = '\') then
-    Exit;
-
-  Result := FPath + FItems[FIndex];
+  result := '';
+  if (FIndex >= 0) and (FIndex <= High(FItems)) and (FItems[FIndex][1] <> '/') and (FItems[FIndex][1] <> '\') then
+  begin
+    s := e_CatPath(FSubPath, FItems[FIndex]);
+    if e_FindResource(FBaseList, s) = true then
+      result := ExpandFileName(s)
+  end;
+  e_LogWritefln('TGUIFileListBox.SelectedItem -> "%s"', [result]);
 end;
 
 procedure TGUIFileListBox.UpdateFileList();
@@ -3367,7 +3394,8 @@ begin
   else
     fn := FItems[FIndex];
 
-  OpenDir(FPath);
+//  OpenDir(FPath);
+  ScanDirs;
 
   if fn <> '' then
     SelectItem(fn);
index 4590560f2a3a1d5b632bc683b625de4f933ed80a..a2a55ad87bb0664ec651338a1b396a0994b7b77d 100644 (file)
@@ -48,7 +48,7 @@ implementation
 
 uses
   {$INCLUDE ../nogl/noGLuses.inc}
-  {rttiobj,} typinfo, e_texture,
+  {rttiobj,} typinfo, e_texture, e_res,
   SysUtils, Classes, SDL2,
   MAPDEF, g_main, g_options,
   utils, hashtable, xparser;
@@ -1740,7 +1740,7 @@ begin
 
     // load bindings from file
     try
-      st := openDiskFileRO(GameDir+'holmes.rc');
+      st := e_OpenResourceRO(ConfigDirs, 'holmes.rc');
       pr := TFileTextParser.Create(st);
       conwriteln('parsing "holmes.rc"...');
       while (pr.tokType <> pr.TTEOF) do
index 45432615c04762ff01a513553de4f9b7822043af..24dac52e2e76556c340db9ddd3595c1ec67d1370 100644 (file)
@@ -17,6 +17,8 @@ unit g_main;
 
 interface
 
+  uses Utils;
+
 procedure Main ();
 procedure Init ();
 procedure Release ();
@@ -26,12 +28,28 @@ procedure KeyPress (K: Word);
 procedure CharPress (C: AnsiChar);
 
 var
-  GameDir: string;
-  DataDir: string;
-  MapsDir: string;
-  ModelsDir: string;
+  {--- TO REMOVE ---}
+  GameDir: string; 
+  {-----------------}
+
+  {--- Read-only dirs ---}
   GameWAD: string;
+  DataDirs: SSArray;
+  ModelDirs: SSArray;
+  MegawadDirs: SSArray;
+  MapDirs: SSArray;
+  WadDirs: SSArray;
+  AllMapDirs: SSArray; // Maps + Megawads
+
+  {--- Read-Write dirs ---}
   LogFileName: string;
+  LogDirs: SSArray;
+  SaveDirs: SSArray;
+  CacheDirs: SSArray;
+  ConfigDirs: SSArray;
+  ScreenshotDirs: SSArray;
+  MapDownloadDirs: SSArray;
+  WadDownloadDirs: SSArray;
 
 implementation
 
@@ -43,29 +61,121 @@ uses
   wadreader, e_log, g_window,
   e_graphics, e_input, g_game, g_console, g_gui,
   e_sound, g_options, g_sound, g_player, g_basic,
-  g_weapons, SysUtils, g_triggers, MAPDEF, g_map,
+  g_weapons, SysUtils, g_triggers, MAPDEF, g_map, e_res,
   g_menu, g_language, g_net, g_touch, g_system, g_res_downloader,
-  utils, conbuf, envvars,
+  conbuf, envvars,
   xparser;
 
 
 var
   charbuff: packed array [0..15] of AnsiChar;
 
+procedure InitPath;
+  var i: Integer; rwdir, rodir: AnsiString;
+
+  procedure AddPath (var arr: SSArray; str: AnsiString);
+  begin
+    SetLength(arr, Length(arr) + 1);
+    arr[High(arr)] := ExpandFileName(str)
+  end;
+
+  procedure AddDef (var arr: SSArray; str: AnsiString);
+  begin
+    if arr = nil then
+      AddPath(arr, str)
+  end;
+
+begin
+  GetDir(0, GameDir);
+
+  i := 1;
+  while i < ParamCount do
+  begin
+    case ParamStr(i) of
+    '--rw-dir':
+      begin
+        Inc(i);
+        rwdir := ParamStr(i);
+        (* RW *)
+        AddPath(LogDirs, e_CatPath(rwdir, ''));
+        AddPath(SaveDirs, e_CatPath(rwdir, 'data'));
+        AddPath(CacheDirs, e_CatPath(rwdir, 'data/cache'));
+        AddPath(ConfigDirs, e_CatPath(rwdir, ''));
+        AddPath(MapDownloadDirs, e_CatPath(rwdir, 'maps/downloads'));
+        AddPath(WadDownloadDirs, e_CatPath(rwdir, 'wads/downloads'));
+        AddPath(ScreenshotDirs, e_CatPath(rwdir, 'screenshots'));
+        (* RO *)
+        AddPath(DataDirs, e_CatPath(rwdir, 'data'));
+        AddPath(ModelDirs, e_CatPath(rwdir, 'data/models'));
+        AddPath(MegawadDirs, e_CatPath(rwdir, 'maps/megawads'));
+        AddPath(MapDirs, e_CatPath(rwdir, 'maps'));
+        AddPath(WadDirs, e_CatPath(rwdir, 'wads'));
+      end;
+    '--ro-dir':
+      begin
+        Inc(i);
+        rodir := ParamStr(i);
+        (* RO *)
+        AddPath(DataDirs, e_CatPath(rodir, 'data'));
+        AddPath(ModelDirs, e_CatPath(rodir, 'data/models'));
+        AddPath(MegawadDirs, e_CatPath(rodir, 'maps/megawads'));
+        AddPath(MapDirs, e_CatPath(rodir, 'maps'));
+        AddPath(WadDirs, e_CatPath(rodir, 'wads'));
+      end;
+    end;
+    Inc(i)
+  end;
+
+  (* RO *)
+  AddDef(DataDirs, 'data');
+  AddDef(ModelDirs, 'data/models');
+  AddDef(MegawadDirs, 'maps/megawads');
+  AddDef(MapDirs, 'maps');
+  AddDef(WadDirs, 'wads');
+  (* RW *)
+  AddDef(LogDirs, '.');
+  AddDef(SaveDirs, 'data');
+  AddDef(CacheDirs, 'data/cache');
+  AddDef(ConfigDirs, '.');
+  AddDef(MapDownloadDirs, 'maps/downloads');
+  AddDef(WadDownloadDirs, 'wad/downloads');
+  AddDef(ScreenshotDirs, 'screenshots');
+
+  for i := 0 to High(MapDirs) do
+    AddPath(AllMapDirs, MapDirs[i]);
+  for i := 0 to High(MegawadDirs) do
+    AddPath(AllMapDirs, MegawadDirs[i]);
+
+  if LogFileName = '' then
+  begin
+    rwdir := e_GetDir(LogDirs);
+    if rwdir <> '' then
+    begin
+      {$IFDEF HEADLESS}
+        LogFileName := e_CatPath(rwdir, 'Doom2DF_H.log');
+      {$ELSE}
+        LogFileName := e_Catpath(rwdir, 'Doom2DF.log');
+      {$ENDIF}      
+    end
+  end
+end;
+
 procedure Main();
 {$IFDEF ENABLE_HOLMES}
   var flexloaded: Boolean;
 {$ENDIF}
+  var s: AnsiString;
 begin
+  InitPath;
+  if LogFileName <> '' then
+    e_InitLog(LogFileName, TWriteMode.WM_NEWFILE);
   e_InitWritelnDriver();
 
-  GetDir(0, GameDir);
-  MapsDir := GameDir + '/maps/';
-  DataDir := GameDir + '/data/';
-  ModelsDir := DataDir + 'models/';
-  GameWAD := DataDir + 'Game.wad';
+  GameWAD := e_FindWad(DataDirs, 'GAME');
+  assert(GameWad <> '', 'GAME.WAD not installed?');
 
-  e_InitLog(GameDir + '/' + LogFileName, TWriteMode.WM_NEWFILE);
+
+//  e_InitLog(GameDir + '/' + LogFileName, TWriteMode.WM_NEWFILE);
 
   e_WriteLog(
     'Doom 2D: Forever version ' + GAME_VERSION +
@@ -85,7 +195,17 @@ begin
   e_InitInput;
 
   sys_Init;
-  g_Options_Read(GameDir + '/' + CONFIG_FILENAME);
+
+  s := CONFIG_FILENAME;
+  if e_FindResource(ConfigDirs, s) = true then
+  begin
+    g_Options_Read(s)
+  end
+  else
+  begin
+    g_Options_SetDefault;
+    g_Options_SetDefaultVideo
+  end;
   if sys_SetDisplayMode(gScreenWidth, gScreenHeight, gBPP, gFullScreen) = False then
     raise Exception.Create('Failed to set videomode on startup.');
 
@@ -372,9 +492,9 @@ begin
   s2 := Copy(charbuff, 15, 2);
   if CheckCheat(I_GAME_CHEAT_CHANGEMAP, 2) and (s2[1] >= '0') and (s2[1] <= '9') and (s2[2] >= '0') and (s2[2] <= '9') then
   begin
-    if g_Map_Exist(MapsDir+gGameSettings.WAD+':\MAP'+s2) then
+    if g_Map_Exist(gGameSettings.WAD + ':\MAP' + s2) then
     begin
-      c := 'MAP'+s2;
+      c := 'MAP' + s2;
       g_Game_ExitLevel(c);
     end;
     goto Cheated;
@@ -569,5 +689,4 @@ begin
   end;
 end;
 
-
 end.
index 4b65cbec5ddbd539a20ff3e06b1e8a6be28a787c..95bb1a02fd813f82536e759ee6a2b0cfa1f86f8c 100644 (file)
@@ -253,7 +253,7 @@ implementation
 
 uses
   {$INCLUDE ../nogl/noGLuses.inc}
-  e_input, g_main, e_log, e_texture, g_items, g_gfx, g_console,
+  e_input, g_main, e_log, e_texture, e_res, g_items, g_gfx, g_console,
   g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, g_triggers, g_player,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
@@ -345,8 +345,8 @@ begin
 
   try
     e_LogWritefln('parsing "mapdef.txt"...', []);
-    st := openDiskFileRO(DataDir+'mapdef.txt');
-    e_LogWritefln('found local "%smapdef.txt"', [DataDir]);
+    st := e_OpenResourceRO(DataDirs, 'mapdef.txt');
+    e_LogWritefln('found local "mapdef.txt"', []);
   except
     st := nil;
   end;
@@ -902,15 +902,14 @@ end;
 
 function GetReplacementWad (WadName: AnsiString): AnsiString;
 begin
-  if (length(WadName) = 0) then
-  begin
-    result := '';
-  end
-  else
+  result := '';
+  if WadName <> '' then
   begin
     result := WadName;
-    if g_Game_IsClient then result := g_Res_FindReplacementWad(WadName);
-    if (result = WadName) then result := GameDir+'/wads/'+result;
+    if g_Game_IsClient then
+      result := g_Res_FindReplacementWad(WadName);
+    if (result = WadName) then
+      result := e_FindWad(WadDirs, result)
   end;
 end;
 
@@ -2218,21 +2217,13 @@ begin
     begin
       e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, TMsgType.Notify);
       g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
-      FileName := g_ExtractWadName(gMapInfo.SkyName);
-
-      if (FileName <> '') then FileName := GameDir+'/wads/'+FileName else FileName := g_ExtractWadName(Res);
-
       if gTextureFilter then TEXTUREFILTER := GL_LINEAR else TEXTUREFILTER := GL_NEAREST;
       try
-        s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
+        s := e_GetResourcePath(WadDirs, gMapInfo.SkyName, g_ExtractWadName(Res));
         if g_Texture_CreateWAD(BackID, s) then
-        begin
-          g_Game_SetupScreenSize();
-        end
+          g_Game_SetupScreenSize
         else
-        begin
-          g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
-        end;
+          g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]))
       finally
         TEXTUREFILTER := GL_NEAREST;
       end;
@@ -2244,16 +2235,8 @@ begin
     begin
       e_WriteLog('  Loading music: ' + gMapInfo.MusicName, TMsgType.Notify);
       g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
-      FileName := g_ExtractWadName(gMapInfo.MusicName);
-
-      if FileName <> '' then
-        FileName := GameDir+'/wads/'+FileName
-      else
-        begin
-          FileName := g_ExtractWadName(Res);
-        end;
 
-      s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
+      s := e_GetResourcePath(WadDirs, gMapInfo.MusicName, g_ExtractWadName(Res));
       if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
         ok := True
       else
index ccc0a556af4ec8cb8c1ec0c6cb46605777fe850f..53a1cbc6bae3cca965e73a2c6dfdd3ea144ccca3 100644 (file)
@@ -47,7 +47,7 @@ uses
   g_basic, g_console, g_sound, g_gfx, g_player, g_options, g_weapons,
   e_log, SysUtils, CONFIG, g_playermodel, DateUtils,
   MAPDEF, Math, g_saveload,
-  e_texture, g_language,
+  e_texture, g_language, e_res,
   g_net, g_netmsg, g_netmaster, g_items, e_input, g_touch,
   utils, wadreader, g_system;
 
@@ -108,6 +108,7 @@ var
   menu: TGUIMenu;
   i: Integer;
   ovs: Boolean;
+  s: AnsiString;
 begin
   menu := TGUIMenu(g_GUI_GetWindow('OptionsVideoMenu').GetControl('mOptionsVideoMenu'));
 
@@ -374,8 +375,10 @@ begin
 
   if g_Game_IsClient then MC_SEND_PlayerSettings;
 
-  g_Options_Write(GameDir+'/'+CONFIG_FILENAME);
-  g_Console_WriteGameConfig();
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write(s + '/' + CONFIG_FILENAME);
+  g_Console_WriteGameConfig;
 end;
 
 procedure ReadOptions();
@@ -647,6 +650,7 @@ var
   Map: String;
   GameMode: Byte;
   Options: LongWord;
+  s: AnsiString;
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mCustomGameMenu')) do
   begin
@@ -695,7 +699,9 @@ begin
     gcMap := Map;
   end;
 
-  g_Options_Write_Gameplay_Custom(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Gameplay_Custom(s + '/' + CONFIG_FILENAME);
 
   g_Game_StartCustom(Map, GameMode, gcTimeLimit, gcGoalLimit,
                      gcMaxLives, Options, gcPlayers);
@@ -707,6 +713,7 @@ var
   Map: String;
   GameMode: Byte;
   Options: LongWord;
+  s: AnsiString;
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mNetServerMenu')) do
   begin
@@ -761,8 +768,12 @@ begin
     NetUseMaster := TGUISwitch(GetControl('swUseMaster')).ItemIndex = 0;
   end;
 
-  g_Options_Write_Net_Server(GameDir+'/'+CONFIG_FILENAME);
-  g_Options_Write_Gameplay_Net(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+  begin
+    g_Options_Write_Net_Server(s + '/' + CONFIG_FILENAME);
+    g_Options_Write_Gameplay_Net(s + '/' + CONFIG_FILENAME)
+  end;
 
   g_Game_StartServer(Map, GameMode, gnTimeLimit, gnGoalLimit, gnMaxLives,
                      Options, gnPlayers, 0, NetPort);
@@ -771,6 +782,7 @@ end;
 procedure ProcConnectNetGame();
 var
   PW: String;
+  s: AnsiString;
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mNetClientMenu')) do
   begin
@@ -779,13 +791,16 @@ begin
     PW := TGUIEdit(GetControl('edPW')).Text;
   end;
 
-  g_Options_Write_Net_Client(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Net_Client(s + '/' + CONFIG_FILENAME);
   g_Game_StartClient(NetClientIP, NetClientPort, PW);
 end;
 
 procedure ProcEnterPassword();
 var
   PW: string;
+  s: AnsiString;
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mClientPasswordMenu')) do
   begin
@@ -794,7 +809,9 @@ begin
     PW := TGUIEdit(GetControl('edPW')).Text;
   end;
 
-  g_Options_Write_Net_Client(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Net_Client(s + '/' + CONFIG_FILENAME);
   g_Game_StartClient(NetClientIP, NetClientPort, PW);
 end;
 
@@ -840,7 +857,7 @@ var
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mCampaignMenu')) do
   begin
-    WAD := ExtractRelativePath(MapsDir, TGUIFileListBox(GetControl('lsWAD')).SelectedItem());
+    WAD := TGUIFileListBox(GetControl('lsWAD')).SelectedItem();
     TwoPlayers := TGUISwitch(GetControl('swPlayers')).ItemIndex = 1;
   end;
 
@@ -1260,14 +1277,26 @@ begin
   end;
 end;
 
-procedure ProcSingle1Player();
+procedure ProcSinglePlayer (n: Integer);
+  var wad, map: AnsiString;
+begin
+  wad := g_ExtractWadName(gDefaultMegawadStart);
+  map := g_ExtractFilePathName(gDefaultMegawadStart);
+  if e_FindResource(AllMapDirs, wad) then
+  begin
+    wad := ExpandFileName(wad);
+    g_Game_StartSingle(wad + ':\' + map, False, n);
+  end;
+end;
+
+procedure ProcSingle1Player;
 begin
-  g_Game_StartSingle(gDefaultMegawadStart, False, 1);
+  ProcSinglePlayer(1)
 end;
 
-procedure ProcSingle2Players();
+procedure ProcSingle2Players;
 begin
-  g_Game_StartSingle(gDefaultMegawadStart, True, 2);
+  ProcSinglePlayer(2)
 end;
 
 procedure ProcSelectMapMenu();
@@ -1321,7 +1350,7 @@ var
 begin
   with TGUIMenu(g_ActiveWindow.GetControl('mSelectMapMenu')) do
   begin
-    wad := ExtractRelativePath(MapsDir, TGUIFileListBox(GetControl('lsMapWAD')).SelectedItem());
+    wad := TGUIFileListBox(GetControl('lsMapWAD')).SelectedItem();
     map := TGUIListBox(GetControl('lsMapRes')).SelectedItem();
   end;
 
@@ -1623,7 +1652,8 @@ begin
   if yes then gExit := EXIT_SIMPLE else g_GUI_HideWindow;
 end;
 
-procedure ProcSetRussianLanguage();
+procedure ProcSetRussianLanguage;
+  var s: AnsiString;
 begin
   if gLanguage <> LANGUAGE_RUSSIAN then
   begin
@@ -1631,14 +1661,17 @@ begin
     gLanguageChange := True;
     gAskLanguage := False;
 
-    g_Options_Write_Language(GameDir+'/'+CONFIG_FILENAME);
+    s := e_GetDir(ConfigDirs);
+    if s <> '' then
+      g_Options_Write_Language(s + '/' + CONFIG_FILENAME);
 
   // Ñîõðàíÿåì èçìåíåíèÿ âñåõ íàñòðîåê:
     ProcApplyOptions();
   end;
 end;
 
-procedure ProcSetEnglishLanguage();
+procedure ProcSetEnglishLanguage;
+  var s: AnsiString;
 begin
   if gLanguage <> LANGUAGE_ENGLISH then
   begin
@@ -1646,7 +1679,9 @@ begin
     gLanguageChange := True;
     gAskLanguage := False;
 
-    g_Options_Write_Language(GameDir+'/'+CONFIG_FILENAME);
+    s := e_GetDir(ConfigDirs);
+    if s <> '' then
+      g_Options_Write_Language(s + '/' + CONFIG_FILENAME);
 
   // Ñîõðàíÿåì èçìåíåíèÿ âñåõ íàñòðîåê:
     ProcApplyOptions();
@@ -1883,22 +1918,28 @@ begin
   ProcApplyOptions();
 end;
 
-procedure ProcSetFirstRussianLanguage();
+procedure ProcSetFirstRussianLanguage;
+  var s: AnsiString;
 begin
   gLanguage := LANGUAGE_RUSSIAN;
   gLanguageChange := True;
   gAskLanguage := False;
 
-  g_Options_Write_Language(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Language(s + '/' + CONFIG_FILENAME)
 end;
 
-procedure ProcSetFirstEnglishLanguage();
+procedure ProcSetFirstEnglishLanguage;
+  var s: AnsiString;
 begin
   gLanguage := LANGUAGE_ENGLISH;
   gLanguageChange := True;
   gAskLanguage := False;
 
-  g_Options_Write_Language(GameDir+'/'+CONFIG_FILENAME);
+  s := e_GetDir(ConfigDirs);
+  if s <> '' then
+    g_Options_Write_Language(s + '/' + CONFIG_FILENAME)
 end;
 
 procedure ProcRecallAddress();
@@ -2437,7 +2478,7 @@ begin
       Sort := True;
       Dirs := True;
       FileMask := '*.wad|*.pk3|*.zip|*.dfz';
-      SetBase(MapsDir+'megawads/');
+      SetBase(MegawadDirs);
     end;
 
     with AddLabel(_lc[I_MENU_MAP_NAME]) do
@@ -2492,7 +2533,7 @@ begin
       Sort := True;
       Dirs := True;
       FileMask := '*.wad|*.pk3|*.zip|*.dfz';
-      SetBase(MapsDir);
+      SetBase(MapDirs);
     end;
     with AddList(_lc[I_MENU_MAP_RESOURCE], 12, 4) do
     begin
index 261df8cad6e0aa6e08b42ccd41c3a312f8601410..6b3c8f7d174a33ce9e5eff10830391917c2a4f0d 100644 (file)
@@ -258,7 +258,8 @@ implementation
 
 uses
   SysUtils,
-  e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
+  e_input, e_res,
+  g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
   g_main, g_game, g_language, g_weapons, utils, ctypes, g_system,
   g_map;
 
@@ -531,7 +532,7 @@ begin
           killClientByFT(nc^);
           exit;
         end;
-        if (ridx < 0) then fname := MapsDir+gGameSettings.WAD else fname := {GameDir+'/wads/'+}gExternalResources[ridx].diskName;
+        if (ridx < 0) then fname := gGameSettings.WAD else fname := gExternalResources[ridx].diskName;
         if (length(fname) = 0) then
         begin
           e_WriteLog('Invalid filename: '+fname, TMsgType.Warning);
@@ -661,7 +662,7 @@ begin
       begin
         e_LogWritefln('client #%d requested map info', [nc.ID]);
         trans_omsg.Clear();
-        dfn := findDiskWad(MapsDir+gGameSettings.WAD);
+        dfn := findDiskWad(gGameSettings.WAD);
         if (dfn = '') then dfn := '!wad_not_found!.wad'; //FIXME
         //md5 := MD5File(dfn);
         md5 := gWADHash;
@@ -1329,6 +1330,7 @@ var
   F: TextFile;
   IPstr: string;
   IP: LongWord;
+  path: AnsiString;
 begin
   NetIn.Clear();
   NetOut.Clear();
@@ -1343,9 +1345,10 @@ begin
   NetPlrUID2 := -1;
   NetAddr.port := 25666;
   SetLength(NetBannedHosts, 0);
-  if FileExists(DataDir + BANLIST_FILENAME) then
+  path := BANLIST_FILENAME;
+  if e_FindResource(DataDirs, path) = true then
   begin
-    Assign(F, DataDir + BANLIST_FILENAME);
+    Assign(F, path);
     Reset(F);
     while not EOF(F) do
     begin
@@ -2184,22 +2187,28 @@ procedure g_Net_SaveBanList();
 var
   F: TextFile;
   I: Integer;
+  path: AnsiString;
 begin
-  Assign(F, DataDir + BANLIST_FILENAME);
-  Rewrite(F);
-  if NetBannedHosts <> nil then
-    for I := 0 to High(NetBannedHosts) do
-      if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
-        Writeln(F, IpToStr(NetBannedHosts[I].IP));
-  CloseFile(F);
+  path := e_GetDir(DataDirs);
+  if path <> '' then
+  begin
+    path := e_CatPath(path, BANLIST_FILENAME);
+    Assign(F, path);
+    Rewrite(F);
+    if NetBannedHosts <> nil then
+      for I := 0 to High(NetBannedHosts) do
+        if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
+          Writeln(F, IpToStr(NetBannedHosts[I].IP));
+    CloseFile(F)
+  end
 end;
 
 procedure g_Net_DumpStart();
 begin
   if NetMode = NET_SERVER then
-    NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_server')
+    NetDumpFile := e_CreateResource(LogDirs, NETDUMP_FILENAME + '_server')
   else
-    NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_client');
+    NetDumpFile := e_CreateResource(LogDirs, NETDUMP_FILENAME + '_client');
 end;
 
 procedure g_Net_DumpSendBuffer();
index 1c23bb876ceb5de32288f89577107f1b412531e7..f15e7c7718e8568906c3afea23ba9ed108daf5d6 100644 (file)
@@ -22,7 +22,8 @@ uses
 
 function GenPlayerName (n: Integer): String;
 
-procedure g_Options_SetDefault();
+procedure g_Options_SetDefault;
+procedure g_Options_SetDefaultVideo;
 procedure g_Options_Read(FileName: String);
 procedure g_Options_Write(FileName: String);
 procedure g_Options_Write_Language(FileName: String);
index cc6afb07be47661fa320584d0d6ecb2f80f5df1e..877975529c9521e5b80641646e34ada56abf67fa 100644 (file)
@@ -613,7 +613,7 @@ uses
   g_holmes,
 {$ENDIF}
   e_log, g_map, g_items, g_console, g_gfx, Math,
-  g_options, g_triggers, g_menu, g_game, g_grid,
+  g_options, g_triggers, g_menu, g_game, g_grid, e_res,
   wadreader, g_main, g_monsters, CONFIG, g_language,
   g_net, g_netmsg, g_window,
   utils, xstreams;
@@ -1254,14 +1254,16 @@ var
   a, b: Integer;
   config: TConfig;
   sa: SSArray;
+  path: AnsiString;
 begin
   BotNames := nil;
 
-  if not FileExists(DataDir + BOTNAMES_FILENAME) then
+  path := BOTNAMES_FILENAME;
+  if e_FindResource(DataDirs, path) = false then
     Exit;
 
 // ×èòàåì âîçìîæíûå èìåíà áîòîâ èç ôàéëà:
-  AssignFile(F, DataDir + BOTNAMES_FILENAME);
+  AssignFile(F, path);
   Reset(F);
 
   while not EOF(F) do
@@ -1282,7 +1284,7 @@ begin
   g_Bot_MixNames();
 
 // ×èòàåì ôàéë ñ ïàðàìåòðàìè áîòîâ:
-  config := TConfig.CreateFile(DataDir + BOTLIST_FILENAME);
+  config := TConfig.CreateFile(path);
   BotList := nil;
   a := 0;
 
index a7477581a189541fd1de6a9ea8f0530f44d99ef8..4e5697465bfc8b8c42ede7cc174ebc3e2dc07742 100644 (file)
@@ -41,7 +41,7 @@ uses
   g_game, g_items, g_map, g_monsters, g_triggers,
   g_basic, g_main, Math, wadreader,
   g_weapons, g_player, g_console,
-  e_log, g_language;
+  e_log, e_res, g_language;
 
 const
   SAVE_SIGNATURE = $56534644; // 'DFSV'
@@ -101,9 +101,7 @@ end;
 
 function buildSaveName (n: Integer): AnsiString;
 begin
-  result := '';
-  if (n < 0) or (n > 65535) then exit;
-  result := formatstrf('%sSAVGAME%s.DAT', [DataDir, n]);
+  result := 'SAVGAME' + IntToStr(n) + '.DAT'
 end;
 
 
@@ -120,7 +118,7 @@ begin
   try
     // Îòêðûâàåì ôàéë ñîõðàíåíèé
     filename := buildSaveName(n);
-    st := openDiskFileRO(filename);
+    st := e_OpenResourceRO(SaveDirs, filename);
     try
       if not utils.checkSign(st, 'DFSV') then
       begin
@@ -172,7 +170,7 @@ var
 begin
   result := false;
   try
-    st := createDiskFile(filename);
+    st := e_CreateResource(SaveDirs, filename);
     try
       utils.writeSign(st, 'DFSV');
       utils.writeInt(st, Byte(SAVE_VERSION));
@@ -280,6 +278,7 @@ begin
         e_WriteLog('SaveState Error: '+e.message, TMsgType.Warning);
         if deleteOnError then DeleteFile(filename);
         {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
+e_WriteStackTrace(e.message);
         result := false;
       end;
   end;
@@ -311,7 +310,7 @@ begin
   result := false;
 
   try
-    st := openDiskFileRO(filename);
+    st := e_OpenResourceRO(SaveDirs, filename);
     try
       if not utils.checkSign(st, 'DFSV') then raise XStreamError.Create('invalid save game signature');
       if (utils.readByte(st) <> SAVE_VERSION) then raise XStreamError.Create('invalid save game version');
@@ -528,16 +527,12 @@ end;
 
 function g_SaveGame (n: Integer; const aname: AnsiString): Boolean;
 begin
-  result := false;
-  if (n < 0) or (n > 65535) then exit;
   result := g_SaveGameTo(buildSaveName(n), aname, true);
 end;
 
 
 function g_LoadGame (n: Integer): Boolean;
 begin
-  result := false;
-  if (n < 0) or (n > 65535) then exit;
   result := g_LoadGameFrom(buildSaveName(n));
 end;
 
index 15575146d572935606c53f43d4d6367fe05866b1..3cfca16904004997968584ee954eefc307e92c79 100644 (file)
@@ -105,7 +105,7 @@ uses
   Math,
   g_player, g_map, g_panel, g_gfx, g_game, g_textures,
   g_console, g_monsters, g_items, g_phys, g_weapons,
-  wadreader, g_main, e_log, g_language,
+  wadreader, g_main, e_log, g_language, e_res,
   g_options, g_net, g_netmsg, utils, xparser, xstreams;
 
 const
@@ -2344,7 +2344,7 @@ end;
 function g_Triggers_Create (aTrigger: TTrigger; trec: TDynRecord; forceInternalIndex: Integer=-1): DWORD;
 var
   find_id: DWORD;
-  fn, mapw: AnsiString;
+  fn: AnsiString;
   f, olen: Integer;
   ptg: PTrigger;
 begin
@@ -2518,17 +2518,7 @@ begin
     // Åùå íåò òàêîãî çâóêà
     if not g_Sound_Exists(ptg.tgcSoundName) then
     begin
-      fn := g_ExtractWadName(ptg.tgcSoundName);
-      if (fn = '') then
-      begin // Çâóê â ôàéëå ñ êàðòîé
-        mapw := g_ExtractWadName(gMapInfo.Map);
-        fn := mapw+':'+g_ExtractFilePathName(ptg.tgcSoundName);
-      end
-      else // Çâóê â îòäåëüíîì ôàéëå
-      begin
-        fn := GameDir + '/wads/' + ptg.tgcSoundName;
-      end;
-
+      fn := e_GetResourcePath(WadDirs, ptg.tgcSoundName, g_ExtractWadName(gMapInfo.Map));
       //e_LogWritefln('loading trigger sound ''%s''', [fn]);
       if not g_Sound_CreateWADEx(ptg.tgcSoundName, fn) then
       begin
@@ -2554,18 +2544,7 @@ begin
     // Åùå íåò òàêîé ìóçûêè
     if not g_Sound_Exists(ptg.tgcMusicName) then
     begin
-      fn := g_ExtractWadName(ptg.tgcMusicName);
-
-      if fn = '' then
-      begin // Ìóçûêà â ôàéëå ñ êàðòîé
-        mapw := g_ExtractWadName(gMapInfo.Map);
-        fn := mapw+':'+g_ExtractFilePathName(ptg.tgcMusicName);
-      end
-      else // Ìóçûêà â ôàéëå ñ êàðòîé
-      begin
-        fn := GameDir+'/wads/'+ptg.tgcMusicName;
-      end;
-
+      fn := e_GetResourcePath(WadDirs, ptg.tgcMusicName, g_ExtractWadName(gMapInfo.Map));
       if not g_Sound_CreateWADEx(ptg.tgcMusicName, fn, True) then
       begin
         g_FatalError(Format(_lc[I_GAME_ERROR_TR_SOUND], [fn, ptg.tgcMusicName]));
index 20706c661dae397f8a0b4914ab00e9caa765a252..0cedb13e86a64643fb396cfd646d5d589fb30ae9 100644 (file)
@@ -99,12 +99,13 @@ function utf8Valid (const s: AnsiString): Boolean;
 
 function utf8to1251 (s: AnsiString): AnsiString;
 
-// `pathname` will be modified if path is valid
-// `lastIsDir` should be `true` if we are searching for directory
-// nobody cares about shitdoze, so i'll use the same code path for it
+// findFileCI eats case-insensitive path, traverses it and rewrites it to a
+// case-sensetive. result value means success.
+// if file/dir not founded than pathname is in undefined state!
 function findFileCI (var pathname: AnsiString; lastIsDir: Boolean=false): Boolean;
 
-// return fixed AnsiString or empty AnsiString
+// findDiskWad tries to find wad file and rewrites extension if needed
+// result is new filename or empty string
 function findDiskWad (fname: AnsiString): AnsiString;
 // slashes must be normalized!
 function isWadNamesEqu (wna, wnb: AnsiString): Boolean;
@@ -1123,9 +1124,9 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-// `pathname` will be modified if path is valid
-// `lastIsDir` should be `true` if we are searching for directory
-// nobody cares about shitdoze, so i'll use the same code path for it
+// findFileCI eats case-insensitive path, traverses it and rewrites it to a
+// case-sensetive. result value means success.
+// if file/dir not founded than pathname is in undefined state!
 function findFileCI (var pathname: AnsiString; lastIsDir: Boolean=false): Boolean;
 var
   sr: TSearchRec;