DEADSOFTWARE

added Vampyre Imaging Library; now textures can be in various formats, including...
[d2df-sdl.git] / src / game / g_map.pas
index 90cb224e401f03ba139123567a9177b77e6c16c8..8445e7825e307e89348d740d87c6779970f3362c 100644 (file)
@@ -1,10 +1,11 @@
+{$MODE DELPHI}
 unit g_map;
 
 interface
 
 uses
   e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
-  g_phys, WADEDITOR, BinEditor, g_panel, md5;
+  g_phys, wadreader, BinEditor, g_panel, md5;
 
 type
   TMapInfo = record
@@ -116,7 +117,8 @@ uses
   g_main, e_log, SysUtils, g_items, g_gfx, g_console,
   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;
+  Math, g_monsters, g_saveload, g_language, g_netmsg,
+  utils, sfs;
 
 const
   FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
@@ -345,9 +347,10 @@ begin
   Result := len;
 end;
 
-procedure CreateNullTexture(RecName: String);
+function CreateNullTexture(RecName: String): Integer;
 begin
   SetLength(Textures, Length(Textures)+1);
+  result := High(Textures);
 
   with Textures[High(Textures)] do
   begin
@@ -359,22 +362,22 @@ begin
   end;
 end;
 
-function CreateTexture(RecName: String; Map: string; log: Boolean): Boolean;
+function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   TextureData: Pointer;
   WADName: String;
   SectionName: String;
   TextureName: String;
   a, ResLength: Integer;
 begin
-  Result := False;
+  Result := -1;
 
   if Textures <> nil then
     for a := 0 to High(Textures) do
       if Textures[a].TextureName = RecName then
       begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
-        Result := True;
+        Result := a;
         Exit;
       end;
 
@@ -401,14 +404,14 @@ begin
       Anim := False;
     end;
 
-    Result := True;
+    result := High(Textures);
     Exit;
   end;
 
 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
   g_ProcessResourceStr(RecName, WADName, SectionName, TextureName);
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
 
   if WADName <> '' then
     WADName := GameDir+'/wads/'+WADName
@@ -420,7 +423,7 @@ begin
   if WAD.GetResource(SectionName, TextureName, TextureData, ResLength) then
     begin
       SetLength(Textures, Length(Textures)+1);
-      if not e_CreateTextureMem(TextureData, Textures[High(Textures)].TextureID) then
+      if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
         Exit;
       e_GetTextureSize(Textures[High(Textures)].TextureID,
                        @Textures[High(Textures)].Width,
@@ -429,21 +432,21 @@ begin
       Textures[High(Textures)].TextureName := RecName;
       Textures[High(Textures)].Anim := False;
 
-      Result := True;
+      result := High(Textures);
     end
   else // Íåò òàêîãî ðåóñðñà â WAD'å
     if log then
       begin
         e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
-        e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+        //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
       end;
 
   WAD.Free();
 end;
 
-function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Boolean;
+function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   TextureWAD: Pointer;
   TextData: Pointer;
   TextureData: Pointer;
@@ -456,12 +459,12 @@ var
   _width, _height, _framecount, _speed: Integer;
   _backanimation: Boolean;
 begin
-  Result := False;
+  Result := -1;
 
 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
   g_ProcessResourceStr(RecName, WADName, SectionName, TextureName);
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
 
   if WADName <> '' then
     WADName := GameDir+'/wads/'+WADName
@@ -475,7 +478,7 @@ begin
     if log then
     begin
       e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
-      e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+      //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
     end;
     WAD.Free();
     Exit;
@@ -534,7 +537,7 @@ begin
   with Textures[High(Textures)] do
   begin
   // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
-    if g_Frames_CreateMemory(@FramesID, '', TextureData,
+    if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength,
          _width, _height, _framecount, _backanimation) then
       begin
         TextureName := RecName;
@@ -544,7 +547,7 @@ begin
         FramesCount := _framecount;
         Speed := _speed;
 
-        Result := True;
+        result := High(Textures);
       end
     else
       if log then
@@ -753,10 +756,11 @@ const
   DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
   DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   MapReader: TMapReader_1;
   Header: TMapHeaderRec_1;
   _textures: TTexturesRec1Array;
+  _texnummap: array of Integer; // `_textures` -> `Textures`
   panels: TPanelsRec1Array;
   items: TItemsRec1Array;
   monsters: TMonsterRec1Array;
@@ -777,455 +781,480 @@ var
   Data: Pointer;
   Len: Integer;
   ok, isAnim, trigRef: Boolean;
-  CurTex: Integer;
+  CurTex, ntn: Integer;
 begin
   Result := False;
   gMapInfo.Map := Res;
   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 := TWADFile.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();
 
-  if not MapReader.LoadMap(Data) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
-    FreeMem(Data);
-    MapReader.Free();
-    Exit;
-  end;
+  // Çàãðóçêà êàðòû:
+    e_WriteLog('Loading map: ' + ResName, MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
+    MapReader := TMapReader_1.Create();
 
-  FreeMem(Data);
-  generateExternalResourcesList(MapReader);
-// Çàãðóçêà òåêñòóð:
-  g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
-  _textures := MapReader.GetTextures();
-
-// Äîáàâëåíèå òåêñòóð â Textures[]:
-  if _textures <> nil then
-  begin
-    e_WriteLog('  Loading textures:', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
+    if not MapReader.LoadMap(Data) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
+      FreeMem(Data);
+      MapReader.Free();
+      Exit;
+    end;
 
-    for a := 0 to High(_textures) do
+    FreeMem(Data);
+    generateExternalResourcesList(MapReader);
+  // Çàãðóçêà òåêñòóð:
+    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
+    _textures := MapReader.GetTextures();
+    _texnummap := nil;
+
+  // Äîáàâëåíèå òåêñòóð â 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);
+      SetLength(_texnummap, length(_textures));
+
+      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
-            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
-            CreateNullTexture(_textures[a].Resource);
+            SetLength(s, b-1);
+            Break;
+          end;
+        e_WriteLog(Format('    Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
+        //if g_Map_IsSpecialTexture(s) then e_WriteLog('      SPECIAL!', MSG_NOTIFY);
+      // Àíèìèðîâàííàÿ òåêñòóðà:
+        if ByteBool(_textures[a].Anim) then
+          begin
+            ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
+            if ntn < 0 then
+            begin
+              g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
+              ntn := CreateNullTexture(_textures[a].Resource);
+            end;
+          end
+        else // Îáû÷íàÿ òåêñòóðà:
+          ntn := CreateTexture(_textures[a].Resource, FileName, True);
+          if ntn < 0 then
+          begin
+            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
+            ntn := 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();
+        _texnummap[a] := ntn; // fix texture number
+        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
+    // check texture numbers for panels
+    for a := 0 to High(panels) 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;
-
-      g_Game_StepLoading();
+      if panels[a].TextureNum > High(_textures) then
+      begin
+        e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
+        result := false;
+        exit;
+      end;
+      panels[a].TextureNum := _texnummap[panels[a].TextureNum];
     end;
-  end;
-
-// Ñîçäàåì ïàíåëè:
-  if panels <> nil then
-  begin
-    e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
 
-    for a := 0 to High(panels) do
+  // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
+    if triggers <> 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 table...', MSG_NOTIFY);
+      SetLength(TriggersTable, Length(triggers));
+      g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
 
-      if ok then
+      for a := 0 to High(TriggersTable) 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;
+      // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
+        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;
+
+        g_Game_StepLoading();
       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 panels <> nil then
+    begin
+      e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
 
-      // Ñïåö-òåêñòóðû çàïðåùåíû:
-        if g_Map_IsSpecialTexture(s) then
-          ok := False
+      for a := 0 to High(panels) do
+      begin
+        SetLength(AddTextures, 0);
+        trigRef := False;
+        CurTex := -1;
+        if _textures <> nil then
+          begin
+            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);
+                      ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                      if not ok then
+                      begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
+                        isAnim := False;
+                        ok := CreateTexture(TexName, FileName, False) >= 0;
+                      end;
+                    end
+                  else
+                    begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
+                      isAnim := False;
+                      ok := CreateTexture(TexName, FileName, False) >= 0;
+                      if not ok then
+                      begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
+                        isAnim := True;
+                        ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                      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();
+        //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(_textures), High(Textures), High(AddTextures)]), MSG_NOTIFY);
+
+      // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
+        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;
 
 function g_Map_GetMapInfo(Res: String): TMapInfo;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   MapReader: TMapReader_1;
   Header: TMapHeaderRec_1;
   FileName, SectionName, ResName: String;
@@ -1235,7 +1264,7 @@ begin
   FillChar(Result, SizeOf(Result), 0);
   g_ProcessResourceStr(Res, FileName, SectionName, ResName);
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
   if not WAD.ReadFile(FileName) then
   begin
     WAD.Free();
@@ -1277,7 +1306,7 @@ end;
 
 function g_Map_GetMapsList(WADName: string): SArray;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   a: Integer;
   ResList: SArray;
   Data: Pointer;
@@ -1286,7 +1315,7 @@ var
 begin
   Result := nil;
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
   if not WAD.ReadFile(WADName) then
   begin
     WAD.Free();
@@ -1316,7 +1345,7 @@ end;
 
 function g_Map_Exist(Res: string): Boolean;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   FileName, SectionName, ResName: string;
   ResList: SArray;
   a: Integer;
@@ -1325,9 +1354,9 @@ begin
 
   g_ProcessResourceStr(Res, FileName, SectionName, ResName);
 
-  if Pos('.wad', LowerCase(FileName)) = 0 then FileName := FileName+'.wad';
+  FileName := addWadExtension(FileName);
 
-  WAD := TWADEditor_1.Create;
+  WAD := TWADFile.Create;
   if not WAD.ReadFile(FileName) then
   begin
     WAD.Free();