DEADSOFTWARE

temporary disable sfs GC on game startup (this will speedup initial data loading)
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 15 Apr 2016 11:29:58 +0000 (14:29 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 15 Apr 2016 11:30:39 +0000 (14:30 +0300)
src/game/g_game.pas
src/game/g_main.pas
src/game/g_map.pas

index 40760c69eeb6c78d2ec0f81196bc8a1588c806d7..c5e144c7c0dfaad252644494be24bf65de0e092e 100644 (file)
@@ -295,7 +295,7 @@ uses
   g_triggers, MAPDEF, g_monsters, e_sound, CONFIG,
   BinEditor, g_language, g_net, SDL,
   ENet, e_fixedbuffer, g_netmsg, g_netmaster, GL, GLExt,
-  utils;
+  utils, sfs;
 
 type
   TEndCustomGameStat = record
@@ -1020,80 +1020,86 @@ begin
   gMapToDelete := '';
   gTempDelete := False;
 
-  g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
-  g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
-  g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
-  g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
-
-  LoadStdFont('STDTXT', 'STDFONT', gStdFont);
-  LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
-  LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
-
-  g_Game_ClearLoading();
-  g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
-  g_Game_SetLoadingText('', 0, False);
-
-  g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
-  g_Console_Init();
-
-  g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
-  g_PlayerModel_LoadData();
-
-  if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
-    repeat
-      if not g_PlayerModel_Load(ModelsDir+SR.Name) then
-        e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
-    until FindNext(SR) <> 0;
-  FindClose(SR);
-
-  if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
-    repeat
-      if not g_PlayerModel_Load(ModelsDir+SR.Name) then
-        e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
-    until FindNext(SR) <> 0;
-  FindClose(SR);
-
-  if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
-    repeat
-      if not g_PlayerModel_Load(ModelsDir+SR.Name) then
-        e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
-    until FindNext(SR) <> 0;
-  FindClose(SR);
-
-  gGameOn := False;
-  gPause := False;
-  gTime := 0;
-  LastScreenShot := 0;
-
-  {e_MouseInfo.Accel := 1.0;}
-
-  g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
-  g_Game_LoadData();
-
-  g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
-  g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
-  g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
-  g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
-  g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
-
-  g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
-  g_Menu_Init();
-
-  gMusic := TMusic.Create();
-  gMusic.SetByName('MUSIC_MENU');
-  gMusic.Play();
-
-  gGameSettings.WarmupTime := 30;
-
-  gState := STATE_MENU;
-
-  SetLength(gEvents, 6);
-  gEvents[0].Name := 'ongamestart';
-  gEvents[1].Name := 'ongameend';
-  gEvents[2].Name := 'onmapstart';
-  gEvents[3].Name := 'onmapend';
-  gEvents[4].Name := 'oninter';
-  gEvents[5].Name := 'onwadend';
+  sfsGCDisable(); // temporary disable removing of temporary volumes
+
+  try
+    g_Texture_CreateWADEx('MENU_BACKGROUND', GameWAD+':TEXTURES\TITLE');
+    g_Texture_CreateWADEx('INTER', GameWAD+':TEXTURES\INTER');
+    g_Texture_CreateWADEx('ENDGAME_EN', GameWAD+':TEXTURES\ENDGAME_EN');
+    g_Texture_CreateWADEx('ENDGAME_RU', GameWAD+':TEXTURES\ENDGAME_RU');
+
+    LoadStdFont('STDTXT', 'STDFONT', gStdFont);
+    LoadFont('MENUTXT', 'MENUFONT', gMenuFont);
+    LoadFont('SMALLTXT', 'SMALLFONT', gMenuSmallFont);
+
+    g_Game_ClearLoading();
+    g_Game_SetLoadingText(Format('Doom 2D: Forever %s', [GAME_VERSION]), 0, False);
+    g_Game_SetLoadingText('', 0, False);
+
+    g_Game_SetLoadingText(_lc[I_LOAD_CONSOLE], 0, False);
+    g_Console_Init();
+
+    g_Game_SetLoadingText(_lc[I_LOAD_MODELS], 0, False);
+    g_PlayerModel_LoadData();
+
+    if FindFirst(ModelsDir+'*.wad', faAnyFile, SR) = 0 then
+      repeat
+        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+          e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
+      until FindNext(SR) <> 0;
+    FindClose(SR);
+
+    if FindFirst(ModelsDir+'*.pk3', faAnyFile, SR) = 0 then
+      repeat
+        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+          e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
+      until FindNext(SR) <> 0;
+    FindClose(SR);
+
+    if FindFirst(ModelsDir+'*.zip', faAnyFile, SR) = 0 then
+      repeat
+        if not g_PlayerModel_Load(ModelsDir+SR.Name) then
+          e_WriteLog(Format('Error loading model %s', [SR.Name]), MSG_WARNING);
+      until FindNext(SR) <> 0;
+    FindClose(SR);
+
+    gGameOn := False;
+    gPause := False;
+    gTime := 0;
+    LastScreenShot := 0;
+
+    {e_MouseInfo.Accel := 1.0;}
+
+    g_Game_SetLoadingText(_lc[I_LOAD_GAME_DATA], 0, False);
+    g_Game_LoadData();
+
+    g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
+    g_Sound_CreateWADEx('MUSIC_INTERMUS', GameWAD+':MUSIC\INTERMUS', True);
+    g_Sound_CreateWADEx('MUSIC_MENU', GameWAD+':MUSIC\MENU', True);
+    g_Sound_CreateWADEx('MUSIC_ROUNDMUS', GameWAD+':MUSIC\ROUNDMUS', True);
+    g_Sound_CreateWADEx('MUSIC_STDENDMUS', GameWAD+':MUSIC\ENDMUS', True);
+
+    g_Game_SetLoadingText(_lc[I_LOAD_MENUS], 0, False);
+    g_Menu_Init();
+
+    gMusic := TMusic.Create();
+    gMusic.SetByName('MUSIC_MENU');
+    gMusic.Play();
+
+    gGameSettings.WarmupTime := 30;
+
+    gState := STATE_MENU;
+
+    SetLength(gEvents, 6);
+    gEvents[0].Name := 'ongamestart';
+    gEvents[1].Name := 'ongameend';
+    gEvents[2].Name := 'onmapstart';
+    gEvents[3].Name := 'onmapend';
+    gEvents[4].Name := 'oninter';
+    gEvents[5].Name := 'onwadend';
+  finally
+    sfsGCEnable(); // enable releasing unused volumes
+  end;
 end;
 
 procedure g_Game_Free();
index 5c6067ba154f4fde8561eaf18fad985151f5744a..c2307efbfaf090f99599e1c2a97aead8d32bb07e 100644 (file)
@@ -55,7 +55,7 @@ begin
   if SDL_Init(SDL_INIT_JOYSTICK or SDL_INIT_TIMER or SDL_INIT_VIDEO) < 0 then
 {$ENDIF}
     raise Exception.Create('SDL: Init failed: ' + SDL_GetError());
-    
+
   SDL_StartTextInput();
 
   e_WriteLog('Entering SDLMain', MSG_NOTIFY);
@@ -65,7 +65,7 @@ begin
   {$WARNINGS ON}
 
   SDL_StopTextInput();
-  
+
   e_WriteLog('Releasing SDL', MSG_NOTIFY);
   SDL_Quit();
 end;
index 84ebb9555b11ddee98d22ace33c0c02a025a5097..584a88f2b637679b28d719d0def9c9d38cfe6e42 100644 (file)
@@ -117,7 +117,7 @@ uses
   GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, MAPREADER, g_triggers, g_player, MAPDEF,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
-  utils;
+  utils, sfs;
 
 const
   FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
@@ -785,441 +785,446 @@ begin
   TriggersTable := nil;
   FillChar(texture, SizeOf(texture), 0);
 
-// Çàãðóçêà WAD:
-  g_ProcessResourceStr(Res, FileName, SectionName, ResName);
-  e_WriteLog('Loading map WAD: ' + FileName, MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
+  sfsGCDisable(); // temporary disable removing of temporary volumes
+  try
+  // Çàãðóçêà WAD:
+    g_ProcessResourceStr(Res, FileName, SectionName, ResName);
+    e_WriteLog('Loading map WAD: ' + FileName, MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
 
-  WAD := TWADEditor_1.Create();
-  if not WAD.ReadFile(FileName) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
-    WAD.Free();
-    Exit;
-  end;
-  if not WAD.GetResource('', ResName, Data, Len) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [ResName]));
+    WAD := TWADEditor_1.Create();
+    if not WAD.ReadFile(FileName) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
+      WAD.Free();
+      Exit;
+    end;
+    if not WAD.GetResource('', ResName, Data, Len) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [ResName]));
+      WAD.Free();
+      Exit;
+    end;
     WAD.Free();
-    Exit;
-  end;
-  WAD.Free();
 
-// Çàãðóçêà êàðòû:
-  e_WriteLog('Loading map: ' + ResName, MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
-  MapReader := TMapReader_1.Create();
+  // Çàãðóçêà êàðòû:
+    e_WriteLog('Loading map: ' + ResName, MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
+    MapReader := TMapReader_1.Create();
 
-  if not MapReader.LoadMap(Data) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
-    FreeMem(Data);
-    MapReader.Free();
-    Exit;
-  end;
-
-  FreeMem(Data);
-  generateExternalResourcesList(MapReader);
-// Çàãðóçêà òåêñòóð:
-  g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
-  _textures := MapReader.GetTextures();
+    if not MapReader.LoadMap(Data) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
+      FreeMem(Data);
+      MapReader.Free();
+      Exit;
+    end;
 
-// Äîáàâëåíèå òåêñòóð â Textures[]:
-  if _textures <> nil then
-  begin
-    e_WriteLog('  Loading textures:', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
+    FreeMem(Data);
+    generateExternalResourcesList(MapReader);
+  // Çàãðóçêà òåêñòóð:
+    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
+    _textures := MapReader.GetTextures();
 
-    for a := 0 to High(_textures) do
+  // Äîáàâëåíèå òåêñòóð â Textures[]:
+    if _textures <> nil then
     begin
-      SetLength(s, 64);
-      CopyMemory(@s[1], @_textures[a].Resource[0], 64);
-      for b := 1 to Length(s) do
-        if s[b] = #0 then
-        begin
-          SetLength(s, b-1);
-          Break;
-        end;
-      e_WriteLog('    Loading texture: ' + s, MSG_NOTIFY);
-    // Àíèìèðîâàííàÿ òåêñòóðà:
-      if ByteBool(_textures[a].Anim) then
-        begin
-          if not CreateAnimTexture(_textures[a].Resource, FileName, True) then
+      e_WriteLog('  Loading textures:', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
+
+      for a := 0 to High(_textures) do
+      begin
+        SetLength(s, 64);
+        CopyMemory(@s[1], @_textures[a].Resource[0], 64);
+        for b := 1 to Length(s) do
+          if s[b] = #0 then
+          begin
+            SetLength(s, b-1);
+            Break;
+          end;
+        e_WriteLog('    Loading texture: ' + s, MSG_NOTIFY);
+      // Àíèìèðîâàííàÿ òåêñòóðà:
+        if ByteBool(_textures[a].Anim) then
           begin
-            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
+            if not CreateAnimTexture(_textures[a].Resource, FileName, True) then
+            begin
+              g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
+              CreateNullTexture(_textures[a].Resource);
+            end;
+          end
+        else // Îáû÷íàÿ òåêñòóðà:
+          if not CreateTexture(_textures[a].Resource, FileName, True) then
+          begin
+            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
             CreateNullTexture(_textures[a].Resource);
           end;
-        end
-      else // Îáû÷íàÿ òåêñòóðà:
-        if not CreateTexture(_textures[a].Resource, FileName, True) then
-        begin
-          g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
-          CreateNullTexture(_textures[a].Resource);
-        end;
 
-      g_Game_StepLoading();
+        g_Game_StepLoading();
+      end;
     end;
-  end;
-
-// Çàãðóçêà òðèããåðîâ:
-  gTriggerClientID := 0;
-  e_WriteLog('  Loading triggers...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
-  triggers := MapReader.GetTriggers();
 
-// Çàãðóçêà ïàíåëåé:
-  e_WriteLog('  Loading panels...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
-  panels := MapReader.GetPanels();
+  // Çàãðóçêà òðèããåðîâ:
+    gTriggerClientID := 0;
+    e_WriteLog('  Loading triggers...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
+    triggers := MapReader.GetTriggers();
 
-// Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
-  if triggers <> nil then
-  begin
-    e_WriteLog('  Setting up trigger table...', MSG_NOTIFY);
-    SetLength(TriggersTable, Length(triggers));
-    g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
+  // Çàãðóçêà ïàíåëåé:
+    e_WriteLog('  Loading panels...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
+    panels := MapReader.GetPanels();
 
-    for a := 0 to High(TriggersTable) do
+  // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
+    if triggers <> nil then
     begin
-    // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
-      TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
-    // Ëèôòû:
-      if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
-        TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
-      else
-        TriggersTable[a].LiftPanel := -1;
-    // Äâåðè:
-      if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
-          TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
-          TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
-        TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
-      else
-        TriggersTable[a].DoorPanel := -1;
-    // Òóðåëü:
-      if triggers[a].TriggerType = TRIGGER_SHOT then
-        TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
-      else
-        TriggersTable[a].ShotPanel := -1;
+      e_WriteLog('  Setting up trigger table...', MSG_NOTIFY);
+      SetLength(TriggersTable, Length(triggers));
+      g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
 
-      g_Game_StepLoading();
-    end;
-  end;
+      for a := 0 to High(TriggersTable) do
+      begin
+      // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
+        TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
+      // Ëèôòû:
+        if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
+          TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
+        else
+          TriggersTable[a].LiftPanel := -1;
+      // Äâåðè:
+        if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
+            TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
+            TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
+          TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
+        else
+          TriggersTable[a].DoorPanel := -1;
+      // Òóðåëü:
+        if triggers[a].TriggerType = TRIGGER_SHOT then
+          TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
+        else
+          TriggersTable[a].ShotPanel := -1;
 
-// Ñîçäàåì ïàíåëè:
-  if panels <> nil then
-  begin
-    e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
+        g_Game_StepLoading();
+      end;
+    end;
 
-    for a := 0 to High(panels) do
+  // Ñîçäàåì ïàíåëè:
+    if panels <> nil then
     begin
-      SetLength(AddTextures, 0);
-      trigRef := False;
-      CurTex := -1;
-      if _textures <> nil then
-        begin
-          texture := _textures[panels[a].TextureNum];
-          ok := True;
-        end
-      else
-        ok := False;
+      e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
 
-      if ok then
+      for a := 0 to High(panels) do
       begin
-      // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
-      // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
-        ok := False;
-        if (TriggersTable <> nil) and (_textures <> nil) then
-          for b := 0 to High(TriggersTable) do
-            if (TriggersTable[b].TexturePanel = a)
-            or (TriggersTable[b].ShotPanel = a) then
-            begin
-              trigRef := True;
-              ok := True;
-              Break;
-            end;
-      end;
-
-      if ok then
-      begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
-        SetLength(s, 64);
-        CopyMemory(@s[1], @texture.Resource[0], 64);
-      // Èçìåðÿåì äëèíó:
-        Len := Length(s);
-        for c := Len downto 1 do
-          if s[c] <> #0 then
+        SetLength(AddTextures, 0);
+        trigRef := False;
+        CurTex := -1;
+        if _textures <> nil then
           begin
-            Len := c;
-            Break;
-          end;
-        SetLength(s, Len);
-
-      // Ñïåö-òåêñòóðû çàïðåùåíû:
-        if g_Map_IsSpecialTexture(s) then
-          ok := False
+            texture := _textures[panels[a].TextureNum];
+            ok := True;
+          end
         else
-      // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
-          ok := g_Texture_NumNameFindStart(s);
+          ok := False;
 
-      // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
-      // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
         if ok then
         begin
-          k := NNF_NAME_BEFORE;
-        // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
-          while ok or (k = NNF_NAME_BEFORE) or
-                (k = NNF_NAME_EQUALS) do
+        // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
+        // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
+          ok := False;
+          if (TriggersTable <> nil) and (_textures <> nil) then
+            for b := 0 to High(TriggersTable) do
+              if (TriggersTable[b].TexturePanel = a)
+              or (TriggersTable[b].ShotPanel = a) then
+              begin
+                trigRef := True;
+                ok := True;
+                Break;
+              end;
+        end;
+
+        if ok then
+        begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
+          SetLength(s, 64);
+          CopyMemory(@s[1], @texture.Resource[0], 64);
+        // Èçìåðÿåì äëèíó:
+          Len := Length(s);
+          for c := Len downto 1 do
+            if s[c] <> #0 then
+            begin
+              Len := c;
+              Break;
+            end;
+          SetLength(s, Len);
+
+        // Ñïåö-òåêñòóðû çàïðåùåíû:
+          if g_Map_IsSpecialTexture(s) then
+            ok := False
+          else
+        // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
+            ok := g_Texture_NumNameFindStart(s);
+
+        // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
+        // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
+          if ok then
           begin
-            k := g_Texture_NumNameFindNext(TexName);
+            k := NNF_NAME_BEFORE;
+          // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
+            while ok or (k = NNF_NAME_BEFORE) or
+                  (k = NNF_NAME_EQUALS) do
+            begin
+              k := g_Texture_NumNameFindNext(TexName);
 
-            if (k = NNF_NAME_BEFORE) or
-               (k = NNF_NAME_AFTER) then
-              begin
-              // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
-                if ByteBool(texture.Anim) then
-                  begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
-                    isAnim := True;
-                    ok := CreateAnimTexture(TexName, FileName, False);
-                    if not ok then
-                    begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
-                      isAnim := False;
-                      ok := CreateTexture(TexName, FileName, False);
-                    end;
-                  end
-                else
-                  begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
-                    isAnim := False;
-                    ok := CreateTexture(TexName, FileName, False);
-                    if not ok then
-                    begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
+              if (k = NNF_NAME_BEFORE) or
+                 (k = NNF_NAME_AFTER) then
+                begin
+                // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
+                  if ByteBool(texture.Anim) then
+                    begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
                       isAnim := True;
                       ok := CreateAnimTexture(TexName, FileName, False);
+                      if not ok then
+                      begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
+                        isAnim := False;
+                        ok := CreateTexture(TexName, FileName, False);
+                      end;
+                    end
+                  else
+                    begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
+                      isAnim := False;
+                      ok := CreateTexture(TexName, FileName, False);
+                      if not ok then
+                      begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
+                        isAnim := True;
+                        ok := CreateAnimTexture(TexName, FileName, False);
+                      end;
                     end;
-                  end;
 
-              // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
-                if ok then
-                begin
-                  for c := 0 to High(Textures) do
-                    if Textures[c].TextureName = TexName then
-                    begin
-                      SetLength(AddTextures, Length(AddTextures)+1);
-                      AddTextures[High(AddTextures)].Texture := c;
-                      AddTextures[High(AddTextures)].Anim := isAnim;
-                      Break;
-                    end;
-                end;
-              end
-            else
-              if k = NNF_NAME_EQUALS then
-                begin
-                // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
-                  SetLength(AddTextures, Length(AddTextures)+1);
-                  AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
-                  AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
-                  CurTex := High(AddTextures);
-                  ok := True;
+                // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
+                  if ok then
+                  begin
+                    for c := 0 to High(Textures) do
+                      if Textures[c].TextureName = TexName then
+                      begin
+                        SetLength(AddTextures, Length(AddTextures)+1);
+                        AddTextures[High(AddTextures)].Texture := c;
+                        AddTextures[High(AddTextures)].Anim := isAnim;
+                        Break;
+                      end;
+                  end;
                 end
-              else // NNF_NO_NAME
-                ok := False;
-          end; // while ok...
-
-          ok := True;
-        end; // if ok - åñòü ñìåæíûå òåêñòóðû
-      end; // if ok - ññûëàþòñÿ òðèããåðû
-
-      if not ok then
-      begin
-      // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
-        SetLength(AddTextures, 1);
-        AddTextures[0].Texture := panels[a].TextureNum;
-        AddTextures[0].Anim := ByteBool(texture.Anim);
-        CurTex := 0;
-      end;
+              else
+                if k = NNF_NAME_EQUALS then
+                  begin
+                  // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
+                    SetLength(AddTextures, Length(AddTextures)+1);
+                    AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
+                    AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
+                    CurTex := High(AddTextures);
+                    ok := True;
+                  end
+                else // NNF_NO_NAME
+                  ok := False;
+            end; // while ok...
 
-    // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
-      PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
+            ok := True;
+          end; // if ok - åñòü ñìåæíûå òåêñòóðû
+        end; // if ok - ññûëàþòñÿ òðèããåðû
 
-    // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
-      if TriggersTable <> nil then
-        for b := 0 to High(TriggersTable) do
+        if not ok then
         begin
-        // Òðèããåð äâåðè/ëèôòà:
-          if (TriggersTable[b].LiftPanel = a) or
-             (TriggersTable[b].DoorPanel = a) then
-            TTriggerData(triggers[b].DATA).PanelID := PanelID;
-        // Òðèããåð ñìåíû òåêñòóðû:
-          if TriggersTable[b].TexturePanel = a then
-            triggers[b].TexturePanel := PanelID;
-        // Òðèããåð "Òóðåëü":
-          if TriggersTable[b].ShotPanel = a then
-            TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
+        // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
+          SetLength(AddTextures, 1);
+          AddTextures[0].Texture := panels[a].TextureNum;
+          AddTextures[0].Anim := ByteBool(texture.Anim);
+          CurTex := 0;
         end;
 
-      g_Game_StepLoading();
+      // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
+        PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
+
+      // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
+        if TriggersTable <> nil then
+          for b := 0 to High(TriggersTable) do
+          begin
+          // Òðèããåð äâåðè/ëèôòà:
+            if (TriggersTable[b].LiftPanel = a) or
+               (TriggersTable[b].DoorPanel = a) then
+              TTriggerData(triggers[b].DATA).PanelID := PanelID;
+          // Òðèããåð ñìåíû òåêñòóðû:
+            if TriggersTable[b].TexturePanel = a then
+              triggers[b].TexturePanel := PanelID;
+          // Òðèããåð "Òóðåëü":
+            if TriggersTable[b].ShotPanel = a then
+              TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
+          end;
+
+        g_Game_StepLoading();
+      end;
     end;
-  end;
 
-// Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
-  if (triggers <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Creating triggers...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
-  // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
-    for a := 0 to High(triggers) do
+  // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
+    if (triggers <> nil) and not gLoadGameMode then
     begin
-      if triggers[a].TexturePanel <> -1 then
-        b := panels[TriggersTable[a].TexturePanel].PanelType
-      else
-        b := 0;
-      if (triggers[a].TriggerType = TRIGGER_SHOT) and
-         (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
-        c := panels[TriggersTable[a].ShotPanel].PanelType
-      else
-        c := 0;
-      CreateTrigger(triggers[a], b, c);
+      e_WriteLog('  Creating triggers...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
+    // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
+      for a := 0 to High(triggers) do
+      begin
+        if triggers[a].TexturePanel <> -1 then
+          b := panels[TriggersTable[a].TexturePanel].PanelType
+        else
+          b := 0;
+        if (triggers[a].TriggerType = TRIGGER_SHOT) and
+           (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
+          c := panels[TriggersTable[a].ShotPanel].PanelType
+        else
+          c := 0;
+        CreateTrigger(triggers[a], b, c);
+      end;
     end;
-  end;
 
-// Çàãðóçêà ïðåäìåòîâ:
-  e_WriteLog('  Loading triggers...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
-  items := MapReader.GetItems();
+  // Çàãðóçêà ïðåäìåòîâ:
+    e_WriteLog('  Loading triggers...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
+    items := MapReader.GetItems();
 
-// Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
-  if (items <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Spawning items...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
-    for a := 0 to High(items) do
-      CreateItem(Items[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
+    if (items <> nil) and not gLoadGameMode then
+    begin
+      e_WriteLog('  Spawning items...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
+      for a := 0 to High(items) do
+        CreateItem(Items[a]);
+    end;
 
-// Çàãðóçêà îáëàñòåé:
-  e_WriteLog('  Loading areas...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
-  areas := MapReader.GetAreas();
+  // Çàãðóçêà îáëàñòåé:
+    e_WriteLog('  Loading areas...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
+    areas := MapReader.GetAreas();
 
-// Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
-  if areas <> nil then
-  begin
-    e_WriteLog('  Creating areas...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
-    for a := 0 to High(areas) do
-      CreateArea(areas[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
+    if areas <> nil then
+    begin
+      e_WriteLog('  Creating areas...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
+      for a := 0 to High(areas) do
+        CreateArea(areas[a]);
+    end;
 
-// Çàãðóçêà ìîíñòðîâ:
-  e_WriteLog('  Loading monsters...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
-  monsters := MapReader.GetMonsters();
+  // Çàãðóçêà ìîíñòðîâ:
+    e_WriteLog('  Loading monsters...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
+    monsters := MapReader.GetMonsters();
 
-  gTotalMonsters := 0;
+    gTotalMonsters := 0;
 
-// Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
-  if (monsters <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Spawning monsters...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
-    for a := 0 to High(monsters) do
-      CreateMonster(monsters[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
+    if (monsters <> nil) and not gLoadGameMode then
+    begin
+      e_WriteLog('  Spawning monsters...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
+      for a := 0 to High(monsters) do
+        CreateMonster(monsters[a]);
+    end;
 
-// Çàãðóçêà îïèñàíèÿ êàðòû:
-  e_WriteLog('  Reading map info...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
-  Header := MapReader.GetMapHeader();
+  // Çàãðóçêà îïèñàíèÿ êàðòû:
+    e_WriteLog('  Reading map info...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
+    Header := MapReader.GetMapHeader();
 
-  MapReader.Free();
+    MapReader.Free();
 
-  with gMapInfo do
-  begin
-    Name := Header.MapName;
-    Description := Header.MapDescription;
-    Author := Header.MapAuthor;
-    MusicName := Header.MusicName;
-    SkyName := Header.SkyName;
-    Height := Header.Height;
-    Width := Header.Width;
-  end;
+    with gMapInfo do
+    begin
+      Name := Header.MapName;
+      Description := Header.MapDescription;
+      Author := Header.MapAuthor;
+      MusicName := Header.MusicName;
+      SkyName := Header.SkyName;
+      Height := Header.Height;
+      Width := Header.Width;
+    end;
 
-// Çàãðóçêà íåáà:
-  if gMapInfo.SkyName <> '' then
-  begin
-    e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
-    g_ProcessResourceStr(gMapInfo.SkyName, FileName, SectionName, ResName);
+  // Çàãðóçêà íåáà:
+    if gMapInfo.SkyName <> '' then
+    begin
+      e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
+      g_ProcessResourceStr(gMapInfo.SkyName, FileName, SectionName, ResName);
 
-    if FileName <> '' then
-      FileName := GameDir+'/wads/'+FileName
-    else
-      begin
-        g_ProcessResourceStr(Res, @FileName2, nil, nil);
-        FileName := FileName2;
-      end;
+      if FileName <> '' then
+        FileName := GameDir+'/wads/'+FileName
+      else
+        begin
+          g_ProcessResourceStr(Res, @FileName2, nil, nil);
+          FileName := FileName2;
+        end;
 
-    s := FileName+':'+SectionName+'/'+ResName;
-    if g_Texture_CreateWAD(BackID, s) then
-      begin
-        g_Game_SetupScreenSize();
-      end
-    else
-      g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
-  end;
+      s := FileName+':'+SectionName+'/'+ResName;
+      if g_Texture_CreateWAD(BackID, s) then
+        begin
+          g_Game_SetupScreenSize();
+        end
+      else
+        g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
+    end;
 
-// Çàãðóçêà ìóçûêè:
-  ok := False;
-  if gMapInfo.MusicName <> '' then
-  begin
-    e_WriteLog('  Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
-    g_ProcessResourceStr(gMapInfo.MusicName, FileName, SectionName, ResName);
+  // Çàãðóçêà ìóçûêè:
+    ok := False;
+    if gMapInfo.MusicName <> '' then
+    begin
+      e_WriteLog('  Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
+      g_ProcessResourceStr(gMapInfo.MusicName, FileName, SectionName, ResName);
 
-    if FileName <> '' then
-      FileName := GameDir+'/wads/'+FileName
-    else
-      begin
-        g_ProcessResourceStr(Res, @FileName2, nil, nil);
-        FileName := FileName2;
-      end;
+      if FileName <> '' then
+        FileName := GameDir+'/wads/'+FileName
+      else
+        begin
+          g_ProcessResourceStr(Res, @FileName2, nil, nil);
+          FileName := FileName2;
+        end;
+
+      s := FileName+':'+SectionName+'/'+ResName;
+      if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
+        ok := True
+      else
+        g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
+    end;
 
-    s := FileName+':'+SectionName+'/'+ResName;
-    if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
-      ok := True
+  // Îñòàëüíûå óñòàíâêè:
+    CreateDoorMap();
+    CreateLiftMap();
+
+    g_Items_Init();
+    g_Weapon_Init();
+    g_Monsters_Init();
+
+  // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
+    if not gLoadGameMode then
+      g_GFX_Init();
+
+  // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
+    _textures := nil;
+    panels := nil;
+    items := nil;
+    areas := nil;
+    triggers := nil;
+    TriggersTable := nil;
+    AddTextures := nil;
+
+  // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
+    if ok and (not gLoadGameMode) then
+      begin
+        gMusic.SetByName(gMapInfo.MusicName);
+        gMusic.Play();
+      end
     else
-      g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
+      gMusic.SetByName('');
+  finally
+    sfsGCEnable(); // enable releasing unused volumes
   end;
 
-// Îñòàëüíûå óñòàíâêè:
-  CreateDoorMap();
-  CreateLiftMap();
-
-  g_Items_Init();
-  g_Weapon_Init();
-  g_Monsters_Init();
-
-// Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
-  if not gLoadGameMode then
-    g_GFX_Init();
-
-// Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
-  _textures := nil;
-  panels := nil;
-  items := nil;
-  areas := nil;
-  triggers := nil;
-  TriggersTable := nil;
-  AddTextures := nil;
-
-// Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
-  if ok and (not gLoadGameMode) then
-    begin
-      gMusic.SetByName(gMapInfo.MusicName);
-      gMusic.Play();
-    end
-  else
-    gMusic.SetByName('');
-
   e_WriteLog('Done loading map.', MSG_NOTIFY);
   Result := True;
 end;