DEADSOFTWARE

bye-bye, bineditor, we won't miss you
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Tue, 12 Sep 2017 20:46:24 +0000 (23:46 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Tue, 12 Sep 2017 20:46:44 +0000 (23:46 +0300)
19 files changed:
src/engine/e_sound_fmod.inc
src/engine/e_texture.pas
src/game/Doom2DF.dpr
src/game/g_game.pas
src/game/g_gui.pas
src/game/g_items.pas
src/game/g_map.pas
src/game/g_menu.pas
src/game/g_monsters.pas
src/game/g_panel.pas
src/game/g_phys.pas
src/game/g_player.pas
src/game/g_saveload.pas
src/game/g_textures.pas
src/game/g_triggers.pas
src/game/g_weapons.pas
src/shared/BinEditor.pas [deleted file]
src/shared/CONFIG.pas
src/shared/utils.pas

index 6c9efa6e841e55ee44e9f3307335e2cae9911e64..5e1c811fe994d0b119aca6cf3cb8347507e9d797 100644 (file)
@@ -91,7 +91,7 @@ var
 implementation
 
 uses
-  g_window, g_options, BinEditor;
+  g_window, g_options, utils;
 
 const
   N_CHANNELS = 512;
index 62ace83b459098298fa725a7ba72a71c34070314..4db41dc091005e56b554d52f581c6fc822130758 100644 (file)
@@ -54,7 +54,7 @@ function LoadTextureImg (var img: TImageData; var Texture: GLTexture; var pWidth
 implementation
 
 uses
-  Classes, BinEditor, g_options, utils;
+  Classes, g_options, utils;
 
 
 function AlignP2 (n: Word): Word;
index a3e333a32062f604de7d9ca45026614c5230446a..980c54b8d003c77576f5f80ec05e6b308f2e9c32 100644 (file)
@@ -104,7 +104,6 @@ uses
   xparser in '../shared/xparser.pas',
   xdynrec in '../shared/xdynrec.pas',
   exoma in '../shared/exoma.pas',
-  BinEditor in '../shared/BinEditor.pas',
   envvars in '../shared/envvars.pas',
   g_panel in 'g_panel.pas',
   g_language in 'g_language.pas',
index 38fd9582e1182798a706a450a1c444c70caa1a52..daec002afce309a4120acfbfcb4c574e38689d30 100644 (file)
@@ -19,8 +19,10 @@ unit g_game;
 interface
 
 uses
-  g_basic, g_player, e_graphics, Classes, g_res_downloader,
-  SysUtils, g_sound, g_gui, MAPDEF, wadreader, md5, xprofiler;
+  SysUtils, Classes,
+  MAPDEF,
+  g_basic, g_player, e_graphics, g_res_downloader,
+  g_sound, g_gui, wadreader, md5, xprofiler;
 
 type
   TGameSettings = record
@@ -339,7 +341,7 @@ uses
   e_input, e_log, g_console, g_items, g_map, g_panel,
   g_playermodel, g_gfx, g_options, g_weapons, Math,
   g_triggers, g_monsters, e_sound, CONFIG,
-  BinEditor, g_language, g_net, SDL,
+  g_language, g_net, SDL,
   ENet, e_msg, g_netmsg, g_netmaster, GL, GLExt,
   utils, sfs, g_holmes;
 
index 2ead3bbbb06a9e0bf0bd7c8cbae0b5eb2d626919..04b8d76a1cc6b79e6c508c3236005e2d2e5a1758 100644 (file)
@@ -268,6 +268,7 @@ type
     FMiddleID: DWORD;
     FOnChangeEvent: TOnChangeEvent;
     FOnEnterEvent: TOnEnterEvent;
+    FInvalid: Boolean;
     procedure SetText(Text: string);
   public
     constructor Create(FontID: DWORD);
@@ -283,6 +284,7 @@ type
     property Text: string read FText write SetText;
     property Color: TRGB read FColor write FColor;
     property Font: TFont read FFont write FFont;
+    property Invalid: Boolean read FInvalid write FInvalid;
   end;
 
   TGUIKeyRead = class(TGUIControl)
@@ -2219,6 +2221,7 @@ begin
 
   FMaxLength := 0;
   FWidth := 0;
+  FInvalid := false;
 
   g_Texture_Get(EDIT_LEFT, FLeftID);
   g_Texture_Get(EDIT_RIGHT, FRightID);
@@ -2228,6 +2231,7 @@ end;
 procedure TGUIEdit.Draw;
 var
   c, w, h: Word;
+  r, g, b: Byte;
 begin
   inherited;
 
@@ -2237,9 +2241,13 @@ begin
   for c := 0 to FWidth-1 do
     e_Draw(FMiddleID, FX+8+c*16, FY, 0, True, False);
 
-  FFont.Draw(FX+8, FY, FText, FColor.R, FColor.G, FColor.B);
+  r := FColor.R;
+  g := FColor.G;
+  b := FColor.B;
+  if FInvalid then begin r := 128; g := 128; b := 128; end;
+  FFont.Draw(FX+8, FY, FText, r, g, b);
 
-  if FWindow.FActiveControl = Self then
+  if (FWindow.FActiveControl = self) then
   begin
     FFont.GetTextSize(Copy(FText, 1, FCaretPos), w, h);
     h := e_CharFont_GetMaxHeight(FFont.ID);
index ef2e925b659be7ab89c696789a67be165e09273d..fd04006f9b223d443b65a899e70515270c0b6272 100644 (file)
@@ -19,7 +19,8 @@ unit g_items;
 interface
 
 uses
-  g_textures, g_phys, g_saveload, BinEditor, MAPDEF;
+  SysUtils, Classes,
+  MAPDEF, g_textures, g_phys, g_saveload;
 
 Type
   PItem = ^TItem;
@@ -59,8 +60,8 @@ procedure g_Items_Draw();
 procedure g_Items_DrawDrop();
 procedure g_Items_Pick(ID: DWORD);
 procedure g_Items_Remove(ID: DWORD);
-procedure g_Items_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Items_LoadState(var Mem: TBinMemoryReader);
+procedure g_Items_SaveState (st: TStream);
+procedure g_Items_LoadState (st: TStream);
 
 procedure g_Items_RestartRound ();
 
@@ -88,10 +89,11 @@ var
 implementation
 
 uses
+  Math,
   g_basic, e_graphics, g_sound, g_main, g_gfx, g_map,
-  Math, g_game, g_triggers, g_console, SysUtils, g_player, g_net, g_netmsg,
+  g_game, g_triggers, g_console, g_player, g_net, g_netmsg,
   e_log,
-  g_grid, binheap, idpool;
+  g_grid, binheap, idpool, utils, xstreams;
 
 
 var
@@ -710,24 +712,17 @@ begin
 end;
 
 
-procedure g_Items_SaveState (var Mem: TBinMemoryWriter);
+procedure g_Items_SaveState (st: TStream);
 var
   count, i: Integer;
-  sig: DWORD;
   tt: Byte;
 begin
   // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ïðåäìåòîâ
   count := 0;
-  if (ggItems <> nil) then
-  begin
-    for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) then Inc(count);
-  end;
-
-  Mem := TBinMemoryWriter.Create((count+1) * 60);
+  for i := 0 to High(ggItems) do if (ggItems[i].ItemType <> ITEM_NONE) then Inc(count);
 
   // Êîëè÷åñòâî ïðåäìåòîâ
-  Mem.WriteInt(count);
-
+  utils.writeInt(st, LongInt(count));
   if (count = 0) then exit;
 
   for i := 0 to High(ggItems) do
@@ -735,72 +730,71 @@ begin
     if (ggItems[i].ItemType <> ITEM_NONE) then
     begin
       // Ñèãíàòóðà ïðåäìåòà
-      sig := ITEM_SIGNATURE; // 'ITEM'
-      Mem.WriteDWORD(sig);
+      utils.writeSign(st, 'ITEM');
+      utils.writeInt(st, Byte(0));
       // Òèï ïðåäìåòà
       tt := ggItems[i].ItemType;
       if ggItems[i].dropped then tt := tt or $80;
-      Mem.WriteByte(tt);
+      utils.writeInt(st, Byte(tt));
       // Åñòü ëè ðåñïàóí
-      Mem.WriteBoolean(ggItems[i].Respawnable);
+      utils.writeBool(st, ggItems[i].Respawnable);
       // Êîîðäèíàòû ðåñïóíà
-      Mem.WriteInt(ggItems[i].InitX);
-      Mem.WriteInt(ggItems[i].InitY);
+      utils.writeInt(st, LongInt(ggItems[i].InitX));
+      utils.writeInt(st, LongInt(ggItems[i].InitY));
       // Âðåìÿ äî ðåñïàóíà
-      Mem.WriteWord(ggItems[i].RespawnTime);
+      utils.writeInt(st, Word(ggItems[i].RespawnTime));
       // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
-      Mem.WriteBoolean(ggItems[i].alive);
+      utils.writeBool(st, ggItems[i].alive);
       // Ìîæåò ëè îí ïàäàòü
-      Mem.WriteBoolean(ggItems[i].Fall);
+      utils.writeBool(st, ggItems[i].Fall);
       // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
-      Mem.WriteInt(ggItems[i].SpawnTrigger);
+      utils.writeInt(st, LongInt(ggItems[i].SpawnTrigger));
       // Îáúåêò ïðåäìåòà
-      Obj_SaveState(@ggItems[i].Obj, Mem);
+      Obj_SaveState(st, @ggItems[i].Obj);
     end;
   end;
 end;
 
 
-procedure g_Items_LoadState (var Mem: TBinMemoryReader);
+procedure g_Items_LoadState (st: TStream);
 var
   count, i, a: Integer;
-  sig: DWORD;
   b: Byte;
 begin
-  if (Mem = nil) then exit;
+  assert(st <> nil);
 
   g_Items_Free();
 
   // Êîëè÷åñòâî ïðåäìåòîâ
-  Mem.ReadInt(count);
-
-  if (count = 0) then Exit;
+  count := utils.readLongInt(st);
+  if (count = 0) then exit;
+  if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid number of items');
 
   for a := 0 to count-1 do
   begin
     // Ñèãíàòóðà ïðåäìåòà
-    Mem.ReadDWORD(sig);
-    if (sig <> ITEM_SIGNATURE) then raise EBinSizeError.Create('g_Items_LoadState: Wrong Item Signature'); // 'ITEM'
+    if not utils.checkSign(st, 'ITEM') then raise XStreamError.Create('invalid item signature');
+    if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid item version');
     // Òèï ïðåäìåòà
-    Mem.ReadByte(b); // bit7=1: monster drop
+    b := utils.readByte(st); // bit7=1: monster drop
     // Ñîçäàåì ïðåäìåò
     i := g_Items_Create(0, 0, b and $7F, False, False);
     if ((b and $80) <> 0) then g_Items_SetDrop(i);
     // Åñòü ëè ðåñïàóí
-    Mem.ReadBoolean(ggItems[i].Respawnable);
+    ggItems[i].Respawnable := utils.readBool(st);
     // Êîîðäèíàòû ðåñïóíà
-    Mem.ReadInt(ggItems[i].InitX);
-    Mem.ReadInt(ggItems[i].InitY);
+    ggItems[i].InitX := utils.readLongInt(st);
+    ggItems[i].InitY := utils.readLongInt(st);
     // Âðåìÿ äî ðåñïàóíà
-    Mem.ReadWord(ggItems[i].RespawnTime);
+    ggItems[i].RespawnTime := utils.readWord(st);
     // Ñóùåñòâóåò ëè ýòîò ïðåäìåò
-    Mem.ReadBoolean(ggItems[i].alive);
+    ggItems[i].alive := utils.readBool(st);
     // Ìîæåò ëè îí ïàäàòü
-    Mem.ReadBoolean(ggItems[i].Fall);
+    ggItems[i].Fall := utils.readBool(st);
     // Èíäåêñ òðèããåðà, ñîçäàâøåãî ïðåäìåò
-    Mem.ReadInt(ggItems[i].SpawnTrigger);
+    ggItems[i].SpawnTrigger := utils.readLongInt(st);
     // Îáúåêò ïðåäìåòà
-    Obj_LoadState(@ggItems[i].Obj, Mem);
+    Obj_LoadState(@ggItems[i].Obj, st);
   end;
 end;
 
index ad9c68ddbb5265a3c5619febfb53e696bc53d607..5d4f8d87cb5b0ab9a7a9c615b429496d1a6f572f 100644 (file)
@@ -20,8 +20,9 @@ unit g_map;
 interface
 
 uses
-  e_graphics, g_basic, MAPDEF, g_textures, Classes,
-  g_phys, wadreader, BinEditor, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec;
+  SysUtils, Classes,
+  e_graphics, g_basic, MAPDEF, g_textures,
+  g_phys, wadreader, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec;
 
 type
   TMapInfo = record
@@ -82,7 +83,7 @@ procedure g_Map_EnableWall_XXX (ID: DWORD);
 procedure g_Map_DisableWall_XXX (ID: DWORD);
 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer);
 
-procedure g_Map_SwitchTextureGUID (PanelType: Word; pguid: Integer; AnimLoop: Byte = 0);
+procedure g_Map_SwitchTextureGUID (pguid: Integer; AnimLoop: Byte = 0);
 
 procedure g_Map_ReAdd_DieTriggers();
 function  g_Map_IsSpecialTexture(Texture: String): Boolean;
@@ -95,8 +96,8 @@ function  g_Map_HaveFlagPoints(): Boolean;
 procedure g_Map_ResetFlag(Flag: Byte);
 procedure g_Map_DrawFlags();
 
-procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
-procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
+procedure g_Map_SaveState (st: TStream);
+procedure g_Map_LoadState (st: TStream);
 
 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
 
@@ -242,7 +243,7 @@ var
 implementation
 
 uses
-  e_input, g_main, e_log, e_texture, SysUtils, g_items, g_gfx, g_console,
+  e_input, g_main, e_log, e_texture, g_items, g_gfx, g_console,
   GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, g_triggers, g_player,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
@@ -1312,7 +1313,7 @@ begin
   end;
 end;
 
-function CreateTrigger (amapIdx: Integer; Trigger: TDynRecord; atpanid, atrigpanid: Integer; fTexturePanel1Type, fTexturePanel2Type: Word): Integer;
+function CreateTrigger (amapIdx: Integer; Trigger: TDynRecord; atpanid, atrigpanid: Integer): Integer;
 var
   _trigger: TTrigger;
 begin
@@ -1328,16 +1329,11 @@ begin
     Width := Trigger.Width;
     Height := Trigger.Height;
     Enabled := Trigger.Enabled;
-    //TexturePanel := Trigger.TexturePanel;
     TexturePanelGUID := atpanid;
-    TexturePanelType := fTexturePanel1Type;
-    ShotPanelType := fTexturePanel2Type;
     TriggerType := Trigger.TriggerType;
     ActivateType := Trigger.ActivateType;
     Keys := Trigger.Keys;
     trigPanelGUID := atrigpanid;
-    //trigShotPanelId := ashotpanid;
-    //Data.Default := Trigger.DATA;
   end;
 
   result := Integer(g_Triggers_Create(_trigger, Trigger));
@@ -2028,13 +2024,10 @@ begin
       for rec in triggers do
       begin
         Inc(trignum);
-        if (TriggersTable[trignum].texPanel <> nil) then b := TriggersTable[trignum].texPanel.PanelType else b := 0;
-        if (TriggersTable[trignum].actPanel <> nil) then c := TriggersTable[trignum].actPanel.PanelType else c := 0;
-        // we can have only one of those
         tgpid := TriggersTable[trignum].actPanelIdx;
         //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
         TriggersTable[trignum].tnum := trignum;
-        TriggersTable[trignum].id := CreateTrigger(trignum, rec, TriggersTable[trignum].texPanelIdx, tgpid, Word(b), Word(c));
+        TriggersTable[trignum].id := CreateTrigger(trignum, rec, TriggersTable[trignum].texPanelIdx, tgpid);
       end;
     end;
 
@@ -2926,25 +2919,12 @@ begin
 end;
 
 
-procedure g_Map_SwitchTextureGUID (PanelType: Word; pguid: Integer; AnimLoop: Byte = 0);
+procedure g_Map_SwitchTextureGUID (pguid: Integer; AnimLoop: Byte = 0);
 var
   tp: TPanel;
 begin
   tp := g_Map_PanelByGUID(pguid);
   if (tp = nil) then exit;
-  {
-  case PanelType of
-    PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: tp := gWalls[ID];
-    PANEL_FORE: tp := gRenderForegrounds[ID];
-    PANEL_BACK: tp := gRenderBackgrounds[ID];
-    PANEL_WATER: tp := gWater[ID];
-    PANEL_ACID1: tp := gAcid1[ID];
-    PANEL_ACID2: tp := gAcid2[ID];
-    PANEL_STEP: tp := gSteps[ID];
-    else exit;
-  end;
-  }
-
   tp.NextTexture(AnimLoop);
   if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelTexture(pguid, AnimLoop);
 end;
@@ -3087,80 +3067,62 @@ begin
 end;
 
 
-procedure g_Map_SaveState (var Mem: TBinMemoryWriter);
+procedure g_Map_SaveState (st: TStream);
 var
-  dw: DWORD;
-  b: Byte;
   str: String;
-  boo: Boolean;
 
   procedure savePanels ();
   var
-    PAMem: TBinMemoryWriter;
     pan: TPanel;
   begin
-    // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé
-    PAMem := TBinMemoryWriter.Create((Length(panByGUID)+1) * 40);
-
     // Ñîõðàíÿåì ïàíåëè
-    //Mem.WriteInt(Length(panByGUID));
-    for pan in panByGUID do pan.SaveState(PAMem);
-
-    // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé
-    PAMem.SaveToMemory(Mem);
-    PAMem.Free();
+    utils.writeInt(st, LongInt(Length(panByGUID)));
+    for pan in panByGUID do pan.SaveState(st);
   end;
 
-  procedure SaveFlag (flag: PFlag);
+  procedure saveFlag (flag: PFlag);
+  var
+    b: Byte;
   begin
-    // Ñèãíàòóðà ôëàãà
-    dw := FLAG_SIGNATURE; // 'FLAG'
-    Mem.WriteDWORD(dw);
+    utils.writeSign(st, 'FLAG');
+    utils.writeInt(st, Byte(0)); // version
     // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
-    Mem.WriteByte(flag^.RespawnType);
+    utils.writeInt(st, Byte(flag^.RespawnType));
     // Ñîñòîÿíèå ôëàãà
-    Mem.WriteByte(flag^.State);
+    utils.writeInt(st, Byte(flag^.State));
     // Íàïðàâëåíèå ôëàãà
     if flag^.Direction = D_LEFT then b := 1 else b := 2; // D_RIGHT
-    Mem.WriteByte(b);
+    utils.writeInt(st, Byte(b));
     // Îáúåêò ôëàãà
-    Obj_SaveState(@flag^.Obj, Mem);
+    Obj_SaveState(st, @flag^.Obj);
   end;
 
 begin
-  Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
-
-  ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
   savePanels();
-  ///// /////
 
-  ///// Ñîõðàíÿåì ìóçûêó: /////
-  // Ñèãíàòóðà ìóçûêè:
-  dw := MUSIC_SIGNATURE; // 'MUSI'
-  Mem.WriteDWORD(dw);
-  // Íàçâàíèå ìóçûêè:
-  Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
+  // Ñîõðàíÿåì ìóçûêó
+  utils.writeSign(st, 'MUSI');
+  utils.writeInt(st, Byte(0));
+  // Íàçâàíèå ìóçûêè
+  assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
   if gMusic.NoMusic then str := '' else str := gMusic.Name;
-  Mem.WriteString(str, 64);
+  utils.writeStr(st, str);
   // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
-  dw := gMusic.GetPosition();
-  Mem.WriteDWORD(dw);
+  utils.writeInt(st, LongWord(gMusic.GetPosition()));
   // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
-  boo := gMusic.SpecPause;
-  Mem.WriteBoolean(boo);
-  ///// /////
+  utils.writeBool(st, gMusic.SpecPause);
 
   ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
-  Mem.WriteInt(gTotalMonsters);
+  utils.writeInt(st, LongInt(gTotalMonsters));
   ///// /////
 
   //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
-  if gGameSettings.GameMode = GM_CTF then
+  if (gGameSettings.GameMode = GM_CTF) then
   begin
     // Ôëàã Êðàñíîé êîìàíäû
-    SaveFlag(@gFlags[FLAG_RED]);
+    saveFlag(@gFlags[FLAG_RED]);
     // Ôëàã Ñèíåé êîìàíäû
-    SaveFlag(@gFlags[FLAG_BLUE]);
+    saveFlag(@gFlags[FLAG_BLUE]);
   end;
   ///// /////
 
@@ -3168,64 +3130,53 @@ begin
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
   begin
     // Î÷êè Êðàñíîé êîìàíäû
-    Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
+    utils.writeInt(st, SmallInt(gTeamStat[TEAM_RED].Goals));
     // Î÷êè Ñèíåé êîìàíäû
-    Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
+    utils.writeInt(st, SmallInt(gTeamStat[TEAM_BLUE].Goals));
   end;
   ///// /////
 end;
 
 
-procedure g_Map_LoadState (var Mem: TBinMemoryReader);
+procedure g_Map_LoadState (st: TStream);
 var
   dw: DWORD;
-  b: Byte;
   str: String;
   boo: Boolean;
 
   procedure loadPanels ();
   var
-    PAMem: TBinMemoryReader;
     pan: TPanel;
-    //count: LongInt;
   begin
-    // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé
-    PAMem := TBinMemoryReader.Create();
-    PAMem.LoadFromMemory(Mem);
-
     // Çàãðóæàåì ïàíåëè
-    //PAMem.ReadInt(count);
-    //if (count <> Length(panByGUID)) then raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: invalid number of panels');
-    //if (count <> Length(panByGUID)) then raise EBinSizeError.Create(Format('g_Map_LoadState: LoadPanelArray: invalid number of panels (%d : %d)', [count, Length(panByGUID)]));
+    if (Length(panByGUID) <> utils.readLongInt(st)) then raise XStreamError.Create('invalid number of saved panels');
     for pan in panByGUID do
     begin
-      pan.LoadState(PAMem);
+      pan.LoadState(st);
       if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
     end;
-
-    // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí
-    PAMem.Free();
   end;
 
-  procedure LoadFlag(flag: PFlag);
+  procedure loadFlag (flag: PFlag);
+  var
+    b: Byte;
   begin
     // Ñèãíàòóðà ôëàãà
-    Mem.ReadDWORD(dw);
-    // 'FLAG'
-    if dw <> FLAG_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
+    if not utils.checkSign(st, 'FLAG') then raise XStreamError.Create('invalid flag signature');
+    if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid flag version');
     // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
-    Mem.ReadByte(flag^.RespawnType);
+    flag^.RespawnType := utils.readByte(st);
     // Ñîñòîÿíèå ôëàãà
-    Mem.ReadByte(flag^.State);
+    flag^.State := utils.readByte(st);
     // Íàïðàâëåíèå ôëàãà
-    Mem.ReadByte(b);
-    if b = 1 then flag^.Direction := D_LEFT else flag^.Direction := D_RIGHT; // b = 2
+    b := utils.readByte(st);
+    if (b = 1) then flag^.Direction := D_LEFT else flag^.Direction := D_RIGHT; // b = 2
     // Îáúåêò ôëàãà
-    Obj_LoadState(@flag^.Obj, Mem);
+    Obj_LoadState(@flag^.Obj, st);
   end;
 
 begin
-  if Mem = nil then Exit;
+  if (st = nil) then exit;
 
   ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
   loadPanels();
@@ -3236,36 +3187,34 @@ begin
   //mapCreateGrid();
 
   ///// Çàãðóæàåì ìóçûêó: /////
-  // Ñèãíàòóðà ìóçûêè
-  Mem.ReadDWORD(dw);
-  // 'MUSI'
-  if dw <> MUSIC_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
+  if not utils.checkSign(st, 'MUSI') then raise XStreamError.Create('invalid music signature');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid music version');
   // Íàçâàíèå ìóçûêè
-  Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
-  Mem.ReadString(str);
+  assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
+  str := utils.readStr(st);
   // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
-  Mem.ReadDWORD(dw);
+  dw := utils.readLongWord(st);
   // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
-  Mem.ReadBoolean(boo);
+  boo := utils.readBool(st);
   // Çàïóñêàåì ýòó ìóçûêó
   gMusic.SetByName(str);
   gMusic.SpecPause := boo;
   gMusic.Play();
-  gMusic.Pause(True);
+  gMusic.Pause(true);
   gMusic.SetPosition(dw);
   ///// /////
 
   ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
-  Mem.ReadInt(gTotalMonsters);
+  gTotalMonsters := utils.readLongInt(st);
   ///// /////
 
   //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
-  if gGameSettings.GameMode = GM_CTF then
+  if (gGameSettings.GameMode = GM_CTF) then
   begin
     // Ôëàã Êðàñíîé êîìàíäû
-    LoadFlag(@gFlags[FLAG_RED]);
+    loadFlag(@gFlags[FLAG_RED]);
     // Ôëàã Ñèíåé êîìàíäû
-    LoadFlag(@gFlags[FLAG_BLUE]);
+    loadFlag(@gFlags[FLAG_BLUE]);
   end;
   ///// /////
 
@@ -3273,9 +3222,9 @@ begin
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
   begin
     // Î÷êè Êðàñíîé êîìàíäû
-    Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
+    gTeamStat[TEAM_RED].Goals := utils.readSmallInt(st);
     // Î÷êè Ñèíåé êîìàíäû
-    Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
+    gTeamStat[TEAM_BLUE].Goals := utils.readSmallInt(st);
   end;
   ///// /////
 end;
index aca1dbf59939c6b399c083399728e2396f1f7daf..1af1a57336430d661b07faba3e378fb16d34bc2c 100644 (file)
@@ -1100,19 +1100,26 @@ end;
 procedure ProcLoadMenu();
 var
   a: Integer;
+  valid: Boolean;
 begin
   for a := 1 to 8 do
-    TGUIEdit(TGUIMenu(g_GUI_GetWindow('LoadMenu').GetControl('mmLoadMenu')).GetControl('edSlot'+IntToStr(a))).Text :=
-    g_GetSaveName(a);
+  begin
+    TGUIEdit(TGUIMenu(g_GUI_GetWindow('LoadMenu').GetControl('mmLoadMenu')).GetControl('edSlot'+IntToStr(a))).Text := g_GetSaveName(a, valid);
+    TGUIEdit(TGUIMenu(g_GUI_GetWindow('LoadMenu').GetControl('mmLoadMenu')).GetControl('edSlot'+IntToStr(a))).Invalid := not valid;
+    TGUIMenu(g_GUI_GetWindow('LoadMenu').GetControl('mmLoadMenu')).GetControl('edSlot'+IntToStr(a)).Enabled := valid;
+  end;
 end;
 
 procedure ProcSaveMenu();
 var
   a: Integer;
+  valid: Boolean;
 begin
   for a := 1 to 8 do
-    TGUIEdit(TGUIMenu(g_GUI_GetWindow('SaveMenu').GetControl('mmSaveMenu')).GetControl('edSlot'+IntToStr(a))).Text :=
-    g_GetSaveName(a);
+  begin
+    TGUIEdit(TGUIMenu(g_GUI_GetWindow('SaveMenu').GetControl('mmSaveMenu')).GetControl('edSlot'+IntToStr(a))).Text := g_GetSaveName(a, valid);
+    TGUIEdit(TGUIMenu(g_GUI_GetWindow('SaveMenu').GetControl('mmSaveMenu')).GetControl('edSlot'+IntToStr(a))).Invalid := not valid;
+  end;
 end;
 
 procedure ProcSaveGame(Sender: TGUIControl);
index 1b39ee849607715154823fed3b6caf519667c925..2d6daf6c9de719bd9fed2b43f9c8025dfa9ec14c 100644 (file)
@@ -21,9 +21,10 @@ unit g_monsters;
 interface
 
 uses
+  SysUtils, Classes,
   mempool,
   g_basic, e_graphics, g_phys, g_textures, g_grid,
-  g_saveload, BinEditor, g_panel, xprofiler;
+  g_saveload, g_panel, xprofiler;
 
 const
   MONSTATE_SLEEP  = 0;
@@ -134,8 +135,8 @@ type
     procedure AddTrigger(t: Integer);
     procedure ClearTriggers();
     procedure Respawn();
-    procedure SaveState(var Mem: TBinMemoryWriter);
-    procedure LoadState(var Mem: TBinMemoryReader);
+    procedure SaveState (st: TStream);
+    procedure LoadState (st: TStream);
     procedure SetState(State: Byte; ForceAnim: Byte = 255);
     procedure MakeBloodVector(Count: Word; VelX, VelY: Integer);
     procedure MakeBloodSimple(Count: Word);
@@ -234,8 +235,8 @@ procedure g_Monsters_Draw ();
 procedure g_Monsters_DrawHealth ();
 function  g_Monsters_ByUID (UID: Word): TMonster;
 procedure g_Monsters_killedp ();
-procedure g_Monsters_SaveState (var Mem: TBinMemoryWriter);
-procedure g_Monsters_LoadState (var Mem: TBinMemoryReader);
+procedure g_Monsters_SaveState (st: TStream);
+procedure g_Monsters_LoadState (st: TStream);
 
 function g_Mons_SpawnAt (monType: Integer; x, y: Integer; dir: TDirection=D_LEFT): TMonster; overload;
 function g_Mons_SpawnAt (const typeName: AnsiString; x, y: Integer; dir: TDirection=D_LEFT): TMonster; overload;
@@ -309,8 +310,8 @@ implementation
 uses
   e_log, e_texture, g_main, g_sound, g_gfx, g_player, g_game,
   g_weapons, g_triggers, MAPDEF, g_items, g_options,
-  g_console, g_map, Math, SysUtils, g_menu, wadreader,
-  g_language, g_netmsg, idpool;
+  g_console, g_map, Math, g_menu, wadreader,
+  g_language, g_netmsg, idpool, utils, xstreams;
 
 
 
@@ -1458,86 +1459,74 @@ begin
   result := uidMap[UID];
 end;
 
-procedure g_Monsters_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Monsters_SaveState (st: TStream);
 var
   count, i: Integer;
-  b: Byte;
 begin
-// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ìîíñòðîâ:
+  // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ìîíñòðîâ
   count := 0;
-  if (gMonsters <> nil) then
+  for i := 0 to High(gMonsters) do
   begin
-    for i := 0 to High(gMonsters) do
-    begin
-      if (gMonsters[i] <> nil) then
-      begin
-        if (gMonsters[i].FMonsterType <> MONSTER_NONE) then count += 1;
-      end;
-    end;
+    if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then count += 1;
   end;
 
-  Mem := TBinMemoryWriter.Create((count+1) * 350);
+  // Ñîõðàíÿåì èíôîðìàöèþ öåëåóêàçàòåëÿ
+  utils.writeInt(st, LongInt(pt_x));
+  utils.writeInt(st, LongInt(pt_xs));
+  utils.writeInt(st, LongInt(pt_y));
+  utils.writeInt(st, LongInt(pt_ys));
 
-// Ñîõðàíÿåì èíôîðìàöèþ öåëåóêàçàòåëÿ:
-  Mem.WriteInt(pt_x);
-  Mem.WriteInt(pt_xs);
-  Mem.WriteInt(pt_y);
-  Mem.WriteInt(pt_ys);
+  // Êîëè÷åñòâî ìîíñòðîâ
+  utils.writeInt(st, LongInt(count));
 
-// Êîëè÷åñòâî ìîíñòðîâ:
-  Mem.WriteInt(count);
+  if (count = 0) then exit;
 
-  if count = 0 then
-    Exit;
-
-// Ñîõðàíÿåì ìîíñòðîâ:
+  // Ñîõðàíÿåì ìîíñòðîâ
   for i := 0 to High(gMonsters) do
   begin
-    if (gMonsters[i] <> nil) then
+    if (gMonsters[i] <> nil) and (gMonsters[i].FMonsterType <> MONSTER_NONE) then
     begin
-      if (gMonsters[i].FMonsterType <> MONSTER_NONE) then
-      begin
-        // Òèï ìîíñòðà:
-        b := gMonsters[i].MonsterType;
-        Mem.WriteByte(b);
-        // Ñîõðàíÿåì äàííûå ìîíñòðà:
-        gMonsters[i].SaveState(Mem);
-      end;
+      // Òèï ìîíñòðà
+      utils.writeInt(st, Byte(gMonsters[i].MonsterType));
+      // Ñîõðàíÿåì äàííûå ìîíñòðà:
+      gMonsters[i].SaveState(st);
     end;
   end;
 end;
 
-procedure g_Monsters_LoadState(var Mem: TBinMemoryReader);
+
+procedure g_Monsters_LoadState (st: TStream);
 var
   count, a: Integer;
   b: Byte;
   mon: TMonster;
 begin
-  if Mem = nil then exit;
+  assert(st <> nil);
 
   g_Monsters_Free(false);
 
   // Çàãðóæàåì èíôîðìàöèþ öåëåóêàçàòåëÿ
-  Mem.ReadInt(pt_x);
-  Mem.ReadInt(pt_xs);
-  Mem.ReadInt(pt_y);
-  Mem.ReadInt(pt_ys);
+  pt_x := utils.readLongInt(st);
+  pt_xs := utils.readLongInt(st);
+  pt_y := utils.readLongInt(st);
+  pt_ys := utils.readLongInt(st);
 
   // Êîëè÷åñòâî ìîíñòðîâ
-  Mem.ReadInt(count);
+  count := utils.readLongInt(st);
 
-  if count = 0 then exit;
+  if (count = 0) then exit;
+  if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid monster count');
 
   // Çàãðóæàåì ìîíñòðîâ
   for a := 0 to count-1 do
   begin
     // Òèï ìîíñòðà
-    Mem.ReadByte(b);
+    b := utils.readByte(st);
     // Ñîçäàåì ìîíñòðà
     mon := g_Monsters_Create(b, 0, 0, D_LEFT);
-    if mon = nil then raise EBinSizeError.Create('g_Monsters_LoadState: ID = -1 (Can''t create)');
+    if (mon = nil) then raise XStreamError.Create('g_Monsters_LoadState: ID = -1 (can''t create)');
     // Çàãðóæàåì äàííûå ìîíñòðà
-    mon.LoadState(Mem);
+    mon.LoadState(st);
   end;
 end;
 
@@ -4423,187 +4412,170 @@ begin
   WakeUpSound();
 end;
 
-procedure TMonster.SaveState(var Mem: TBinMemoryWriter);
+procedure TMonster.SaveState (st: TStream);
 var
   i: Integer;
-  sig: DWORD;
   b: Byte;
   anim: Boolean;
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà ìîíñòðà:
-  sig := MONSTER_SIGNATURE; // 'MONS'
-  Mem.WriteDWORD(sig);
-// UID ìîíñòðà:
-  Mem.WriteWord(FUID);
-// Íàïðàâëåíèå:
-  if FDirection = D_LEFT then
-    b := 1
-  else // D_RIGHT
-    b := 2;
-  Mem.WriteByte(b);
-// Íàäî ëè óäàëèòü åãî:
-  Mem.WriteBoolean(FRemoved);
-// Îñòàëîñü çäîðîâüÿ:
-  Mem.WriteInt(FHealth);
-// Ñîñòîÿíèå:
-  Mem.WriteByte(FState);
-// Òåêóùàÿ àíèìàöèÿ:
-  Mem.WriteByte(FCurAnim);
-// UID öåëè:
-  Mem.WriteWord(FTargetUID);
-// Âðåìÿ ïîñëå ïîòåðè öåëè:
-  Mem.WriteInt(FTargetTime);
-// Ïîâåäåíèå ìîíñòðà:
-  Mem.WriteByte(FBehaviour);
-// Ãîòîâíîñòü ê âûñòðåëó:
-  Mem.WriteInt(FAmmo);
-// Áîëü:
-  Mem.WriteInt(FPain);
-// Âðåìÿ îæèäàíèÿ:
-  Mem.WriteInt(FSleep);
-// Îçâó÷èâàòü ëè áîëü:
-  Mem.WriteBoolean(FPainSound);
-// Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè:
-  Mem.WriteBoolean(FWaitAttackAnim);
-// Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå:
-  Mem.WriteBoolean(FChainFire);
-// Ïîäëåæèò ëè ðåñïàâíó:
-  Mem.WriteBoolean(FNoRespawn);
-// Êîîðäèíàòû öåëè:
-  Mem.WriteInt(tx);
-  Mem.WriteInt(ty);
-// ID ìîíñòðà ïðè ñòàðòå êàðòû:
-  Mem.WriteInt(FStartID);
-// Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà:
-  Mem.WriteInt(FSpawnTrigger);
-// Îáúåêò ìîíñòðà:
-  Obj_SaveState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà:
-  anim := vilefire <> nil;
-  Mem.WriteBoolean(anim);
-// Åñëè åñòü - ñîõðàíÿåì:
-  if anim then
-    vilefire.SaveState(Mem);
-// Àíèìàöèè:
+  assert(st <> nil);
+
+  // Ñèãíàòóðà ìîíñòðà:
+  utils.writeSign(st, 'MONS');
+  utils.writeInt(st, Byte(0)); // version
+  // UID ìîíñòðà:
+  utils.writeInt(st, Word(FUID));
+  // Íàïðàâëåíèå
+  if FDirection = D_LEFT then b := 1 else b := 2; // D_RIGHT
+  utils.writeInt(st, Byte(b));
+  // Íàäî ëè óäàëèòü åãî
+  utils.writeBool(st, FRemoved);
+  // Îñòàëîñü çäîðîâüÿ
+  utils.writeInt(st, LongInt(FHealth));
+  // Ñîñòîÿíèå
+  utils.writeInt(st, Byte(FState));
+  // Òåêóùàÿ àíèìàöèÿ
+  utils.writeInt(st, Byte(FCurAnim));
+  // UID öåëè
+  utils.writeInt(st, Word(FTargetUID));
+  // Âðåìÿ ïîñëå ïîòåðè öåëè
+  utils.writeInt(st, LongInt(FTargetTime));
+  // Ïîâåäåíèå ìîíñòðà
+  utils.writeInt(st, Byte(FBehaviour));
+  // Ãîòîâíîñòü ê âûñòðåëó
+  utils.writeInt(st, LongInt(FAmmo));
+  // Áîëü
+  utils.writeInt(st, LongInt(FPain));
+  // Âðåìÿ îæèäàíèÿ
+  utils.writeInt(st, LongInt(FSleep));
+  // Îçâó÷èâàòü ëè áîëü
+  utils.writeBool(st, FPainSound);
+  // Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè
+  utils.writeBool(st, FWaitAttackAnim);
+  // Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå
+  utils.writeBool(st, FChainFire);
+  // Ïîäëåæèò ëè ðåñïàâíó
+  utils.writeBool(st, FNoRespawn);
+  // Êîîðäèíàòû öåëè
+  utils.writeInt(st, LongInt(tx));
+  utils.writeInt(st, LongInt(ty));
+  // ID ìîíñòðà ïðè ñòàðòå êàðòû
+  utils.writeInt(st, LongInt(FStartID));
+  // Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà
+  utils.writeInt(st, LongInt(FSpawnTrigger));
+  // Îáúåêò ìîíñòðà
+  Obj_SaveState(st, @FObj);
+  // Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà
+  anim := (vilefire <> nil);
+  utils.writeBool(st, anim);
+  // Åñëè åñòü - ñîõðàíÿåì:
+  if anim then vilefire.SaveState(st);
+  // Àíèìàöèè
   for i := ANIM_SLEEP to ANIM_PAIN do
   begin
-  // Åñòü ëè ëåâàÿ àíèìàöèÿ:
-    anim := FAnim[i, D_LEFT] <> nil;
-    Mem.WriteBoolean(anim);
-  // Åñëè åñòü - ñîõðàíÿåì:
-    if anim then
-      FAnim[i, D_LEFT].SaveState(Mem);
-  // Åñòü ëè ïðàâàÿ àíèìàöèÿ:
-    anim := FAnim[i, D_RIGHT] <> nil;
-    Mem.WriteBoolean(anim);
-  // Åñëè åñòü - ñîõðàíÿåì:
-    if anim then
-      FAnim[i, D_RIGHT].SaveState(Mem);
+    // Åñòü ëè ëåâàÿ àíèìàöèÿ
+    anim := (FAnim[i, D_LEFT] <> nil);
+    utils.writeBool(st, anim);
+    // Åñëè åñòü - ñîõðàíÿåì
+    if anim then FAnim[i, D_LEFT].SaveState(st);
+    // Åñòü ëè ïðàâàÿ àíèìàöèÿ
+    anim := (FAnim[i, D_RIGHT] <> nil);
+    utils.writeBool(st, anim);
+    // Åñëè åñòü - ñîõðàíÿåì
+    if anim then FAnim[i, D_RIGHT].SaveState(st);
   end;
 end;
 
 
-procedure TMonster.LoadState(var Mem: TBinMemoryReader);
+procedure TMonster.LoadState (st: TStream);
 var
   i: Integer;
-  sig: DWORD;
   b: Byte;
   anim: Boolean;
 begin
-  if Mem = nil then
-    Exit;
+  assert(st <> nil);
 
-// Ñèãíàòóðà ìîíñòðà:
-  Mem.ReadDWORD(sig);
-  if sig <> MONSTER_SIGNATURE then // 'MONS'
-  begin
-    raise EBinSizeError.Create('TMonster.LoadState: Wrong Monster Signature');
-  end;
+  // Ñèãíàòóðà ìîíñòðà:
+  if not utils.checkSign(st, 'MONS') then raise XStreamError.Create('invalid monster signature');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid monster version');
   if (uidMap[FUID] <> nil) and (uidMap[FUID] <> self) then raise Exception.Create('internal error in monster loader (0)');
   uidMap[FUID] := nil;
-// UID ìîíñòðà:
-  Mem.ReadWord(FUID);
+  // UID ìîíñòðà:
+  FUID := utils.readWord(st);
   //if (arrIdx = -1) then raise Exception.Create('internal error in monster loader');
   if (uidMap[FUID] <> nil) then raise Exception.Create('internal error in monster loader (1)');
   uidMap[FUID] := self;
-// Íàïðàâëåíèå:
-  Mem.ReadByte(b);
-  if b = 1 then
-    FDirection := D_LEFT
-  else // b = 2
-    FDirection := D_RIGHT;
-// Íàäî ëè óäàëèòü åãî:
-  Mem.ReadBoolean(FRemoved);
-// Îñòàëîñü çäîðîâüÿ:
-  Mem.ReadInt(FHealth);
-// Ñîñòîÿíèå:
-  Mem.ReadByte(FState);
-// Òåêóùàÿ àíèìàöèÿ:
-  Mem.ReadByte(FCurAnim);
-// UID öåëè:
-  Mem.ReadWord(FTargetUID);
-// Âðåìÿ ïîñëå ïîòåðè öåëè:
-  Mem.ReadInt(FTargetTime);
-// Ïîâåäåíèå ìîíñòðà:
-  Mem.ReadByte(FBehaviour);
-// Ãîòîâíîñòü ê âûñòðåëó:
-  Mem.ReadInt(FAmmo);
-// Áîëü:
-  Mem.ReadInt(FPain);
-// Âðåìÿ îæèäàíèÿ:
-  Mem.ReadInt(FSleep);
-// Îçâó÷èâàòü ëè áîëü:
-  Mem.ReadBoolean(FPainSound);
-// Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè:
-  Mem.ReadBoolean(FWaitAttackAnim);
-// Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå:
-  Mem.ReadBoolean(FChainFire);
-// Ïîäëåæèò ëè ðåñïàâíó
-  Mem.ReadBoolean(FNoRespawn);
-// Êîîðäèíàòû öåëè:
-  Mem.ReadInt(tx);
-  Mem.ReadInt(ty);
-// ID ìîíñòðà ïðè ñòàðòå êàðòû:
-  Mem.ReadInt(FStartID);
-// Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà:
-  Mem.ReadInt(FSpawnTrigger);
-// Îáúåêò ìîíñòðà:
-  Obj_LoadState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà:
-  Mem.ReadBoolean(anim);
-// Åñëè åñòü - çàãðóæàåì:
+  // Íàïðàâëåíèå
+  b := utils.readByte(st);
+  if b = 1 then FDirection := D_LEFT else FDirection := D_RIGHT; // b = 2
+  // Íàäî ëè óäàëèòü åãî
+  FRemoved := utils.readBool(st);
+  // Îñòàëîñü çäîðîâüÿ
+  FHealth := utils.readLongInt(st);
+  // Ñîñòîÿíèå
+  FState := utils.readByte(st);
+  // Òåêóùàÿ àíèìàöèÿ
+  FCurAnim := utils.readByte(st);
+  // UID öåëè
+  FTargetUID := utils.readWord(st);
+  // Âðåìÿ ïîñëå ïîòåðè öåëè
+  FTargetTime := utils.readLongInt(st);
+  // Ïîâåäåíèå ìîíñòðà
+  FBehaviour := utils.readByte(st);
+  // Ãîòîâíîñòü ê âûñòðåëó
+  FAmmo := utils.readLongInt(st);
+  // Áîëü
+  FPain := utils.readLongInt(st);
+  // Âðåìÿ îæèäàíèÿ
+  FSleep := utils.readLongInt(st);
+  // Îçâó÷èâàòü ëè áîëü
+  FPainSound := utils.readBool(st);
+  // Áûëà ëè àòàêà âî âðåìÿ àíèìàöèè àòàêè
+  FWaitAttackAnim := utils.readBool(st);
+  // Íàäî ëè ñòðåëÿòü íà ñëåäóþùåì øàãå
+  FChainFire := utils.readBool(st);
+  // Ïîäëåæèò ëè ðåñïàâíó
+  FNoRespawn := utils.readBool(st);
+  // Êîîðäèíàòû öåëè
+  tx := utils.readLongInt(st);
+  ty := utils.readLongInt(st);
+  // ID ìîíñòðà ïðè ñòàðòå êàðòû
+  FStartID := utils.readLongInt(st);
+  // Èíäåêñ òðèããåðà, ñîçäàâøåãî ìîíñòðà
+  FSpawnTrigger := utils.readLongInt(st);
+  // Îáúåêò ìîíñòðà
+  Obj_LoadState(@FObj, st);
+  // Åñòü ëè àíèìàöèÿ îãíÿ êîëäóíà
+  anim := utils.readBool(st);
+  // Åñëè åñòü - çàãðóæàåì:
   if anim then
   begin
     Assert(vilefire <> nil, 'TMonster.LoadState: no vilefire anim');
-    vilefire.LoadState(Mem);
+    vilefire.LoadState(st);
   end;
-// Àíèìàöèè:
+  // Àíèìàöèè
   for i := ANIM_SLEEP to ANIM_PAIN do
   begin
-  // Åñòü ëè ëåâàÿ àíèìàöèÿ:
-    Mem.ReadBoolean(anim);
-  // Åñëè åñòü - çàãðóæàåì:
+    // Åñòü ëè ëåâàÿ àíèìàöèÿ
+    anim := utils.readBool(st);
+    // Åñëè åñòü - çàãðóæàåì
     if anim then
     begin
-      Assert(FAnim[i, D_LEFT] <> nil,
-        'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
-      FAnim[i, D_LEFT].LoadState(Mem);
+      Assert(FAnim[i, D_LEFT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_left anim');
+      FAnim[i, D_LEFT].LoadState(st);
     end;
-  // Åñòü ëè ïðàâàÿ àíèìàöèÿ:
-     Mem.ReadBoolean(anim);
-  // Åñëè åñòü - çàãðóæàåì:
+    // Åñòü ëè ïðàâàÿ àíèìàöèÿ
+     anim := utils.readBool(st);
+    // Åñëè åñòü - çàãðóæàåì
     if anim then
     begin
-      Assert(FAnim[i, D_RIGHT] <> nil,
-        'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
-      FAnim[i, D_RIGHT].LoadState(Mem);
+      Assert(FAnim[i, D_RIGHT] <> nil, 'TMonster.LoadState: no '+IntToStr(i)+'_right anim');
+      FAnim[i, D_RIGHT].LoadState(st);
     end;
   end;
 end;
 
+
 procedure TMonster.ActivateTriggers();
 var
   a: Integer;
index 16f43c2cbb2eedd0c7016b1fb1d9f4d6bfae35ce..c5eec299a67f3b75dbffbdae292e14c19ca3870a 100644 (file)
@@ -20,7 +20,8 @@ unit g_panel;
 interface
 
 uses
-  MAPDEF, BinEditor, g_textures, xdynrec;
+  SysUtils, Classes,
+  MAPDEF, g_textures, xdynrec;
 
 type
   TAddTextureArray = Array of
@@ -124,8 +125,8 @@ type
     function    GetTextureID(): Cardinal;
     function    GetTextureCount(): Integer;
 
-    procedure   SaveState(var Mem: TBinMemoryWriter);
-    procedure   LoadState(var Mem: TBinMemoryReader);
+    procedure   SaveState (st: TStream);
+    procedure   LoadState (st: TStream);
 
     procedure positionChanged (); inline;
 
@@ -157,10 +158,10 @@ type
     property width: Word read FWidth write FWidth;
     property height: Word read FHeight write FHeight;
     property panelType: Word read FPanelType write FPanelType;
-    property enabled: Boolean read FEnabled write FEnabled; // Ñîõðàíÿòü ïðè SaveState?
-    property door: Boolean read FDoor write FDoor; // Ñîõðàíÿòü ïðè SaveState?
-    property liftType: Byte read FLiftType write FLiftType; // Ñîõðàíÿòü ïðè SaveState?
-    property lastAnimLoop: Byte read FLastAnimLoop write FLastAnimLoop; // Ñîõðàíÿòü ïðè SaveState?
+    property enabled: Boolean read FEnabled write FEnabled;
+    property door: Boolean read FDoor write FDoor;
+    property liftType: Byte read FLiftType write FLiftType;
+    property lastAnimLoop: Byte read FLastAnimLoop write FLastAnimLoop;
 
     property movingSpeedX: Integer read getMovingSpeedX write setMovingSpeedX;
     property movingSpeedY: Integer read getMovingSpeedY write setMovingSpeedY;
@@ -208,8 +209,8 @@ var
 implementation
 
 uses
-  SysUtils, e_texture, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons, g_triggers,
-  g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, utils;
+  e_texture, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons, g_triggers,
+  g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, utils, xstreams;
 
 const
   PANEL_SIGNATURE = $4C4E4150; // 'PANL'
@@ -1013,123 +1014,112 @@ end;
 const
   PAN_SAVE_VERSION = 1;
 
-procedure TPanel.SaveState (var Mem: TBinMemoryWriter);
+procedure TPanel.SaveState (st: TStream);
 var
-  sig: DWORD;
   anim: Boolean;
-  ver: Byte;
 begin
-  if (Mem = nil) then exit;
+  if (st = nil) then exit;
 
   // Ñèãíàòóðà ïàíåëè
-  sig := PANEL_SIGNATURE; // 'PANL'
-  Mem.WriteDWORD(sig);
-  ver := PAN_SAVE_VERSION;
-  Mem.WriteByte(ver);
+  utils.writeSign(st, 'PANL');
+  utils.writeInt(st, Byte(PAN_SAVE_VERSION));
   // Îòêðûòà/çàêðûòà, åñëè äâåðü
-  Mem.WriteBoolean(FEnabled);
+  utils.writeBool(st, FEnabled);
   // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
-  Mem.WriteByte(FLiftType);
+  utils.writeInt(st, Byte(FLiftType));
   // Íîìåð òåêóùåé òåêñòóðû
-  Mem.WriteInt(FCurTexture);
-  // Êîîðäû
-  Mem.WriteInt(FX);
-  Mem.WriteInt(FY);
-  Mem.WriteWord(FWidth);
-  Mem.WriteWord(FHeight);
-  // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà
+  utils.writeInt(st, Integer(FCurTexture));
+  // Êîîðäèíàòû è ðàçìåð
+  utils.writeInt(st, Integer(FX));
+  utils.writeInt(st, Integer(FY));
+  utils.writeInt(st, Word(FWidth));
+  utils.writeInt(st, Word(FHeight));
+  // Àíèìèðîâàíà ëè òåêóùàÿ òåêñòóðà
   if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) then
   begin
     assert(FTextureIDs[FCurTexture].AnTex <> nil, 'TPanel.SaveState: No animation object');
-    anim := True;
+    anim := true;
   end
   else
   begin
-    anim := False;
+    anim := false;
   end;
-  Mem.WriteBoolean(anim);
+  utils.writeBool(st, anim);
   // Åñëè äà - ñîõðàíÿåì àíèìàöèþ
-  if anim then FTextureIDs[FCurTexture].AnTex.SaveState(Mem);
+  if anim then FTextureIDs[FCurTexture].AnTex.SaveState(st);
 
   // moving platform state
-  Mem.WriteInt(mMovingSpeed.X);
-  Mem.WriteInt(mMovingSpeed.Y);
-  Mem.WriteInt(mMovingStart.X);
-  Mem.WriteInt(mMovingStart.Y);
-  Mem.WriteInt(mMovingEnd.X);
-  Mem.WriteInt(mMovingEnd.Y);
-
-  Mem.WriteInt(mSizeSpeed.w);
-  Mem.WriteInt(mSizeSpeed.h);
-  Mem.WriteInt(mSizeEnd.w);
-  Mem.WriteInt(mSizeEnd.h);
-
-  Mem.WriteBoolean(mMovingActive);
-  Mem.WriteBoolean(mMoveOnce);
-
-  Mem.WriteInt(mEndPosTrig);
-  Mem.WriteInt(mEndSizeTrig);
+  utils.writeInt(st, Integer(mMovingSpeed.X));
+  utils.writeInt(st, Integer(mMovingSpeed.Y));
+  utils.writeInt(st, Integer(mMovingStart.X));
+  utils.writeInt(st, Integer(mMovingStart.Y));
+  utils.writeInt(st, Integer(mMovingEnd.X));
+  utils.writeInt(st, Integer(mMovingEnd.Y));
+
+  utils.writeInt(st, Integer(mSizeSpeed.w));
+  utils.writeInt(st, Integer(mSizeSpeed.h));
+  utils.writeInt(st, Integer(mSizeEnd.w));
+  utils.writeInt(st, Integer(mSizeEnd.h));
+
+  utils.writeBool(st, mMovingActive);
+  utils.writeBool(st, mMoveOnce);
+
+  utils.writeInt(st, Integer(mEndPosTrig));
+  utils.writeInt(st, Integer(mEndSizeTrig));
 end;
 
 
-procedure TPanel.LoadState (var Mem: TBinMemoryReader);
-var
-  sig: DWORD;
-  anim: Boolean;
-  ver: Byte;
+procedure TPanel.LoadState (st: TStream);
 begin
-  if (Mem = nil) then exit;
+  if (st = nil) then exit;
 
   // Ñèãíàòóðà ïàíåëè
-  Mem.ReadDWORD(sig);
-  if (sig <> PANEL_SIGNATURE) then raise EBinSizeError.Create('TPanel.LoadState: wrong panel signature'); // 'PANL'
-  Mem.ReadByte(ver);
-  if (ver <> PAN_SAVE_VERSION) then raise EBinSizeError.Create('TPanel.LoadState: invalid panel version');
+  if not utils.checkSign(st, 'PANL') then raise XStreamError.create('wrong panel signature');
+  if (utils.readByte(st) <> PAN_SAVE_VERSION) then raise XStreamError.create('wrong panel version');
   // Îòêðûòà/çàêðûòà, åñëè äâåðü
-  Mem.ReadBoolean(FEnabled);
+  FEnabled := utils.readBool(st);
   // Íàïðàâëåíèå ëèôòà, åñëè ëèôò
-  Mem.ReadByte(FLiftType);
+  FLiftType := utils.readByte(st);
   // Íîìåð òåêóùåé òåêñòóðû
-  Mem.ReadInt(FCurTexture);
-  // Êîîðäû
-  Mem.ReadInt(FX);
-  Mem.ReadInt(FY);
-  Mem.ReadWord(FWidth);
-  Mem.ReadWord(FHeight);
-  //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]);
+  FCurTexture := utils.readLongInt(st);
+  // Êîîðäèíàòû è ðàçìåð
+  FX := utils.readLongInt(st);
+  FY := utils.readLongInt(st);
+  FWidth := utils.readWord(st);
+  FHeight := utils.readWord(st);
   // Àíèìèðîâàííàÿ ëè òåêóùàÿ òåêñòóðà
-  Mem.ReadBoolean(anim);
-  // Åñëè äà - çàãðóæàåì àíèìàöèþ
-  if anim then
+  if utils.readBool(st) then
   begin
+    // Åñëè äà - çàãðóæàåì àíèìàöèþ
     Assert((FCurTexture >= 0) and
            (FTextureIDs[FCurTexture].Anim) and
            (FTextureIDs[FCurTexture].AnTex <> nil),
            'TPanel.LoadState: No animation object');
-    FTextureIDs[FCurTexture].AnTex.LoadState(Mem);
+    FTextureIDs[FCurTexture].AnTex.LoadState(st);
   end;
 
   // moving platform state
-  Mem.ReadInt(mMovingSpeed.X);
-  Mem.ReadInt(mMovingSpeed.Y);
-  Mem.ReadInt(mMovingStart.X);
-  Mem.ReadInt(mMovingStart.Y);
-  Mem.ReadInt(mMovingEnd.X);
-  Mem.ReadInt(mMovingEnd.Y);
+  mMovingSpeed.X := utils.readLongInt(st);
+  mMovingSpeed.Y := utils.readLongInt(st);
+  mMovingStart.X := utils.readLongInt(st);
+  mMovingStart.Y := utils.readLongInt(st);
+  mMovingEnd.X := utils.readLongInt(st);
+  mMovingEnd.Y := utils.readLongInt(st);
 
-  Mem.ReadInt(mSizeSpeed.w);
-  Mem.ReadInt(mSizeSpeed.h);
-  Mem.ReadInt(mSizeEnd.w);
-  Mem.ReadInt(mSizeEnd.h);
+  mSizeSpeed.w := utils.readLongInt(st);
+  mSizeSpeed.h := utils.readLongInt(st);
+  mSizeEnd.w := utils.readLongInt(st);
+  mSizeEnd.h := utils.readLongInt(st);
 
-  Mem.ReadBoolean(mMovingActive);
-  Mem.ReadBoolean(mMoveOnce);
+  mMovingActive := utils.readBool(st);
+  mMoveOnce := utils.readBool(st);
 
-  Mem.ReadInt(mEndPosTrig);
-  Mem.ReadInt(mEndSizeTrig);
+  mEndPosTrig := utils.readLongInt(st);
+  mEndSizeTrig := utils.readLongInt(st);
 
   positionChanged();
   //mapGrid.proxyEnabled[proxyId] := FEnabled; // done in g_map.pas
 end;
 
+
 end.
index 41fdfddc5d2517d45849265c1da176e17bd23920..e92e49b1a6a9ba171d187a2acc2aa4e167df4fe8 100644 (file)
@@ -73,7 +73,7 @@ implementation
 
 uses
   g_map, g_basic, Math, g_player, g_console, SysUtils,
-  g_sound, g_gfx, MAPDEF, g_monsters, g_game, BinEditor, utils;
+  g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
 
 
 const
index 932e081d6da0b56ef9dabac4c6e190b9ca2ceaa2..e6e0d360227097ccb7a83698395ab0cf4777ca3f 100644 (file)
@@ -20,10 +20,11 @@ unit g_player;
 interface
 
 uses
+  SysUtils, Classes,
   mempool,
   e_graphics, g_playermodel, g_basic, g_textures,
   g_weapons, g_phys, g_sound, g_saveload, MAPDEF,
-  BinEditor, g_panel;
+  g_panel;
 
 const
   KEY_LEFT       = 1;
@@ -310,8 +311,8 @@ type
     procedure   Update(); virtual;
     procedure   RememberState();
     procedure   RecallState();
-    procedure   SaveState(var Mem: TBinMemoryWriter); virtual;
-    procedure   LoadState(var Mem: TBinMemoryReader); virtual;
+    procedure   SaveState (st: TStream); virtual;
+    procedure   LoadState (st: TStream); virtual;
     procedure   PauseSounds(Enable: Boolean);
     procedure   NetFire(Wpn: Byte; X, Y, AX, AY: Integer; WID: Integer = -1);
     procedure   DoLerp(Level: Integer = 2);
@@ -400,15 +401,20 @@ type
   end;
 
   TDifficult = record
+  public
     DiagFire: Byte;
     InvisFire: Byte;
     DiagPrecision: Byte;
     FlyPrecision: Byte;
     Cover: Byte;
     CloseJump: Byte;
-    WeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
-    CloseWeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
+    WeaponPrior: packed array [WP_FIRST..WP_LAST] of Byte;
+    CloseWeaponPrior: packed array [WP_FIRST..WP_LAST] of Byte;
     //SafeWeaponPrior: Array [WP_FIRST..WP_LAST] of Byte;
+
+  public
+    procedure save (st: TStream);
+    procedure load (st: TStream);
   end;
 
   TAIFlag = record
@@ -449,8 +455,8 @@ type
     function    PickItem(ItemType: Byte; force: Boolean; var remove: Boolean): Boolean; override;
     function    Heal(value: Word; Soft: Boolean): Boolean; override;
     procedure   Update(); override;
-    procedure   SaveState(var Mem: TBinMemoryWriter); override;
-    procedure   LoadState(var Mem: TBinMemoryReader); override;
+    procedure   SaveState (st: TStream); override;
+    procedure   LoadState (st: TStream); override;
   end;
 
   PGib = ^TGib;
@@ -502,8 +508,8 @@ type
     procedure   Damage(Value: Word; vx, vy: Integer);
     procedure   Update();
     procedure   Draw();
-    procedure   SaveState(var Mem: TBinMemoryWriter);
-    procedure   LoadState(var Mem: TBinMemoryReader);
+    procedure   SaveState (st: TStream);
+    procedure   LoadState (st: TStream);
 
     procedure getMapBox (out x, y, w, h: Integer); inline;
     procedure moveBy (dx, dy: Integer); inline;
@@ -550,7 +556,7 @@ function  g_Shells_GetMax(): Word;
 procedure g_Player_Init();
 procedure g_Player_Free();
 function  g_Player_Create(ModelName: String; Color: TRGB; Team: Byte; Bot: Boolean): Word;
-function  g_Player_CreateFromState(var Mem: TBinMemoryReader): Word;
+function  g_Player_CreateFromState (st: TStream): Word;
 procedure g_Player_Remove(UID: Word);
 procedure g_Player_ResetTeams();
 procedure g_Player_UpdateAll();
@@ -570,8 +576,8 @@ procedure g_Player_UpdatePhysicalObjects();
 procedure g_Player_DrawCorpses();
 procedure g_Player_DrawShells();
 procedure g_Player_RemoveAllCorpses();
-procedure g_Player_Corpses_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Player_Corpses_LoadState(var Mem: TBinMemoryReader);
+procedure g_Player_Corpses_SaveState (st: TStream);
+procedure g_Player_Corpses_LoadState (st: TStream);
 procedure g_Bot_Add(Team, Difficult: Byte);
 procedure g_Bot_AddList(Team: Byte; lname: ShortString; num: Integer = -1);
 procedure g_Bot_MixNames();
@@ -580,10 +586,13 @@ procedure g_Bot_RemoveAll();
 implementation
 
 uses
-  e_log, g_map, g_items, g_console, SysUtils, g_gfx, Math,
+  e_log, g_map, g_items, g_console, g_gfx, Math,
   g_options, g_triggers, g_menu, g_game, g_grid,
   wadreader, g_main, g_monsters, CONFIG, g_language,
-  g_net, g_netmsg, g_window, GL, g_holmes;
+  g_net, g_netmsg, g_window, GL, g_holmes,
+  utils, xstreams;
+
+const PLR_SAVE_VERSION = 0;
 
 type
   TBotProfile = record
@@ -802,47 +811,36 @@ begin
   Result := gPlayers[a].FUID;
 end;
 
-function g_Player_CreateFromState(var Mem: TBinMemoryReader): Word;
+function g_Player_CreateFromState (st: TStream): Word;
 var
   a, i: Integer;
   ok, Bot: Boolean;
-  sig: DWORD;
   b: Byte;
 begin
-  Result := 0;
-  if Mem = nil then
-    Exit;
+  result := 0;
+  if (st = nil) then exit; //???
 
-// Ñèãíàòóðà èãðîêà:
-  Mem.ReadDWORD(sig);
-  if sig <> PLAYER_SIGNATURE then // 'PLYR'
-  begin
-    raise EBinSizeError.Create('g_Player_CreateFromState: Wrong Player Signature');
-  end;
+  // Ñèãíàòóðà èãðîêà
+  if not utils.checkSign(st, 'PLYR') then raise XStreamError.Create('invalid player signature');
+  if (utils.readByte(st) <> PLR_SAVE_VERSION) then raise XStreamError.Create('invalid player version');
 
-// Áîò èëè ÷åëîâåê:
-  Mem.ReadBoolean(Bot);
+  // Áîò èëè ÷åëîâåê:
+  Bot := utils.readBool(st);
 
-  ok := False;
+  ok := false;
   a := 0;
 
-// Åñòü ëè ìåñòî â gPlayers:
-  if gPlayers <> nil then
-    for a := 0 to High(gPlayers) do
-      if gPlayers[a] = nil then
-      begin
-        ok := True;
-        Break;
-      end;
+  // Åñòü ëè ìåñòî â gPlayers:
+  for a := 0 to High(gPlayers) do if (gPlayers[a] = nil) then begin ok := true; break; end;
 
-// Íåò ìåñòà - ðàñøèðÿåì gPlayers:
+  // Íåò ìåñòà - ðàñøèðÿåì gPlayers
   if not ok then
   begin
     SetLength(gPlayers, Length(gPlayers)+1);
     a := High(gPlayers);
   end;
 
-// Ñîçäàåì îáúåêò èãðîêà:
+  // Ñîçäàåì îáúåêò èãðîêà
   if Bot then
     gPlayers[a] := TBot.Create()
   else
@@ -850,133 +848,115 @@ begin
   gPlayers[a].FIamBot := Bot;
   gPlayers[a].FPhysics := True;
 
-// UID èãðîêà:
-  Mem.ReadWord(gPlayers[a].FUID);
-// Èìÿ èãðîêà:
-  Mem.ReadString(gPlayers[a].FName);
-// Êîìàíäà:
-  Mem.ReadByte(gPlayers[a].FTeam);
+  // UID èãðîêà
+  gPlayers[a].FUID := utils.readWord(st);
+  // Èìÿ èãðîêà
+  gPlayers[a].FName := utils.readStr(st);
+  // Êîìàíäà
+  gPlayers[a].FTeam := utils.readByte(st);
   gPlayers[a].FPreferredTeam := gPlayers[a].FTeam;
-// Æèâ ëè:
-  Mem.ReadBoolean(gPlayers[a].FAlive);
-// Èçðàñõîäîâàë ëè âñå æèçíè:
-  Mem.ReadBoolean(gPlayers[a].FNoRespawn);
-// Íàïðàâëåíèå:
-  Mem.ReadByte(b);
-  if b = 1 then
-    gPlayers[a].FDirection := D_LEFT
-  else // b = 2
-    gPlayers[a].FDirection := D_RIGHT;
-// Çäîðîâüå:
-  Mem.ReadInt(gPlayers[a].FHealth);
-// Æèçíè:
-  Mem.ReadByte(gPlayers[a].FLives);
-// Áðîíÿ:
-  Mem.ReadInt(gPlayers[a].FArmor);
-// Çàïàñ âîçäóõà:
-  Mem.ReadInt(gPlayers[a].FAir);
-// Çàïàñ ãîðþ÷åãî:
-  Mem.ReadInt(gPlayers[a].FJetFuel);
-// Áîëü:
-  Mem.ReadInt(gPlayers[a].FPain);
-// Óáèë:
-  Mem.ReadInt(gPlayers[a].FKills);
-// Óáèë ìîíñòðîâ:
-  Mem.ReadInt(gPlayers[a].FMonsterKills);
-// Ôðàãîâ:
-  Mem.ReadInt(gPlayers[a].FFrags);
-// Ôðàãîâ ïîäðÿä:
-  Mem.ReadByte(gPlayers[a].FFragCombo);
-// Âðåìÿ ïîñëåäíåãî ôðàãà:
-  Mem.ReadDWORD(gPlayers[a].FLastFrag);
-// Ñìåðòåé:
-  Mem.ReadInt(gPlayers[a].FDeath);
-// Êàêîé ôëàã íåñåò:
-  Mem.ReadByte(gPlayers[a].FFlag);
-// Íàøåë ñåêðåòîâ:
-  Mem.ReadInt(gPlayers[a].FSecrets);
-// Òåêóùåå îðóæèå:
-  Mem.ReadByte(gPlayers[a].FCurrWeap);
-// Ñëåäóþùåå æåëàåìîå îðóæèå:
-  Mem.ReadWord(gPlayers[a].FNextWeap);
-// ...è ïàóçà:
-  Mem.ReadByte(gPlayers[a].FNextWeapDelay);
-// Âðåìÿ çàðÿäêè BFG:
-  Mem.ReadSmallInt(gPlayers[a].FBFGFireCounter);
-// Áóôåð óðîíà:
-  Mem.ReadInt(gPlayers[a].FDamageBuffer);
-// Ïîñëåäíèé óäàðèâøèé:
-  Mem.ReadWord(gPlayers[a].FLastSpawnerUID);
-// Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
-  Mem.ReadByte(gPlayers[a].FLastHit);
-// Îáúåêò èãðîêà:
-  Obj_LoadState(@gPlayers[a].FObj, Mem);
-// Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.ReadWord(gPlayers[a].FAmmo[i]);
-// Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.ReadWord(gPlayers[a].FMaxAmmo[i]);
-// Íàëè÷èå îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.ReadBoolean(gPlayers[a].FWeapon[i]);
-// Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.ReadWord(gPlayers[a].FReloading[i]);
-// Íàëè÷èå ðþêçàêà:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(gPlayers[a].FRulez, R_ITEM_BACKPACK);
-// Íàëè÷èå êðàñíîãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(gPlayers[a].FRulez, R_KEY_RED);
-// Íàëè÷èå çåëåíîãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(gPlayers[a].FRulez, R_KEY_GREEN);
-// Íàëè÷èå ñèíåãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(gPlayers[a].FRulez, R_KEY_BLUE);
-// Íàëè÷èå áåðñåðêà:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(gPlayers[a].FRulez, R_BERSERK);
-// Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
-  for i := MR_SUIT to MR_MAX do
-    Mem.ReadDWORD(gPlayers[a].FMegaRulez[i]);
-// Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
-  for i := T_RESPAWN to T_FLAGCAP do
-    Mem.ReadDWORD(gPlayers[a].FTime[i]);
-
-// Íàçâàíèå ìîäåëè:
-  Mem.ReadString(gPlayers[a].FActualModelName);
-// Öâåò ìîäåëè:
-  Mem.ReadByte(gPlayers[a].FColor.R);
-  Mem.ReadByte(gPlayers[a].FColor.G);
-  Mem.ReadByte(gPlayers[a].FColor.B);
-// Îáíîâëÿåì ìîäåëü èãðîêà:
+  // Æèâ ëè
+  gPlayers[a].FAlive := utils.readBool(st);
+  // Èçðàñõîäîâàë ëè âñå æèçíè
+  gPlayers[a].FNoRespawn := utils.readBool(st);
+  // Íàïðàâëåíèå
+  b := utils.readByte(st);
+  if b = 1 then gPlayers[a].FDirection := D_LEFT else gPlayers[a].FDirection := D_RIGHT; // b = 2
+  // Çäîðîâüå
+  gPlayers[a].FHealth := utils.readLongInt(st);
+  // Æèçíè
+  gPlayers[a].FLives := utils.readByte(st);
+  // Áðîíÿ
+  gPlayers[a].FArmor := utils.readLongInt(st);
+  // Çàïàñ âîçäóõà
+  gPlayers[a].FAir := utils.readLongInt(st);
+  // Çàïàñ ãîðþ÷åãî
+  gPlayers[a].FJetFuel := utils.readLongInt(st);
+  // Áîëü
+  gPlayers[a].FPain := utils.readLongInt(st);
+  // Óáèë
+  gPlayers[a].FKills := utils.readLongInt(st);
+  // Óáèë ìîíñòðîâ
+  gPlayers[a].FMonsterKills := utils.readLongInt(st);
+  // Ôðàãîâ
+  gPlayers[a].FFrags := utils.readLongInt(st);
+  // Ôðàãîâ ïîäðÿä
+  gPlayers[a].FFragCombo := utils.readByte(st);
+  // Âðåìÿ ïîñëåäíåãî ôðàãà
+  gPlayers[a].FLastFrag := utils.readLongWord(st);
+  // Ñìåðòåé
+  gPlayers[a].FDeath := utils.readLongInt(st);
+  // Êàêîé ôëàã íåñåò
+  gPlayers[a].FFlag := utils.readByte(st);
+  // Íàøåë ñåêðåòîâ
+  gPlayers[a].FSecrets := utils.readLongInt(st);
+  // Òåêóùåå îðóæèå
+  gPlayers[a].FCurrWeap := utils.readByte(st);
+  // Ñëåäóþùåå æåëàåìîå îðóæèå
+  gPlayers[a].FNextWeap := utils.readWord(st);
+  // ...è ïàóçà
+  gPlayers[a].FNextWeapDelay := utils.readByte(st);
+  // Âðåìÿ çàðÿäêè BFG
+  gPlayers[a].FBFGFireCounter := utils.readSmallInt(st);
+  // Áóôåð óðîíà
+  gPlayers[a].FDamageBuffer := utils.readLongInt(st);
+  // Ïîñëåäíèé óäàðèâøèé
+  gPlayers[a].FLastSpawnerUID := utils.readWord(st);
+  // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà
+  gPlayers[a].FLastHit := utils.readByte(st);
+  // Îáúåêò èãðîêà:
+  Obj_LoadState(@gPlayers[a].FObj, st);
+  // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do gPlayers[a].FAmmo[i] := utils.readWord(st);
+  // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do gPlayers[a].FMaxAmmo[i] := utils.readWord(st);
+  // Íàëè÷èå îðóæèÿ
+  for i := WP_FIRST to WP_LAST do gPlayers[a].FWeapon[i] := utils.readBool(st);
+  // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ
+  for i := WP_FIRST to WP_LAST do gPlayers[a].FReloading[i] := utils.readWord(st);
+  // Íàëè÷èå ðþêçàêà
+  if utils.readBool(st) then Include(gPlayers[a].FRulez, R_ITEM_BACKPACK);
+  // Íàëè÷èå êðàñíîãî êëþ÷à
+  if utils.readBool(st) then Include(gPlayers[a].FRulez, R_KEY_RED);
+  // Íàëè÷èå çåëåíîãî êëþ÷à
+  if utils.readBool(st) then Include(gPlayers[a].FRulez, R_KEY_GREEN);
+  // Íàëè÷èå ñèíåãî êëþ÷à
+  if utils.readBool(st) then Include(gPlayers[a].FRulez, R_KEY_BLUE);
+  // Íàëè÷èå áåðñåðêà
+  if utils.readBool(st) then Include(gPlayers[a].FRulez, R_BERSERK);
+  // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ
+  for i := MR_SUIT to MR_MAX do gPlayers[a].FMegaRulez[i] := utils.readLongWord(st);
+  // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà
+  for i := T_RESPAWN to T_FLAGCAP do gPlayers[a].FTime[i] := utils.readLongWord(st);
+
+  // Íàçâàíèå ìîäåëè:
+  gPlayers[a].FActualModelName := utils.readStr(st);
+  // Öâåò ìîäåëè
+  gPlayers[a].FColor.R := utils.readByte(st);
+  gPlayers[a].FColor.G := utils.readByte(st);
+  gPlayers[a].FColor.B := utils.readByte(st);
+  // Îáíîâëÿåì ìîäåëü èãðîêà
   gPlayers[a].SetModel(gPlayers[a].FActualModelName);
 
-// Íåò ìîäåëè - ñîçäàíèå íå âîçìîæíî:
-  if gPlayers[a].FModel = nil then
+  // Íåò ìîäåëè - ñîçäàíèå íåâîçìîæíî
+  if (gPlayers[a].FModel = nil) then
   begin
     gPlayers[a].Free();
     gPlayers[a] := nil;
     g_FatalError(Format(_lc[I_GAME_ERROR_MODEL], [gPlayers[a].FActualModelName]));
-    Exit;
+    exit;
   end;
 
-// Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû:
+  // Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
     gPlayers[a].FModel.Color := TEAMCOLOR[gPlayers[a].FTeam]
   else
     gPlayers[a].FModel.Color := gPlayers[a].FColor;
 
-  Result := gPlayers[a].FUID;
+  result := gPlayers[a].FUID;
 end;
 
+
 procedure g_Player_ResetTeams();
 var
   a: Integer;
@@ -1874,76 +1854,66 @@ begin
   SetLength(gCorpses, MaxCorpses);
 end;
 
-procedure g_Player_Corpses_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Player_Corpses_SaveState (st: TStream);
 var
   count, i: Integer;
-  b: Boolean;
 begin
-// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðóïîâ:
+  // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðóïîâ
   count := 0;
-  if gCorpses <> nil then
-    for i := 0 to High(gCorpses) do
-      if gCorpses[i] <> nil then
-        count := count + 1;
-
-  Mem := TBinMemoryWriter.Create((count+1) * 128);
+  for i := 0 to High(gCorpses) do if (gCorpses[i] <> nil) then Inc(count);
 
-// Êîëè÷åñòâî òðóïîâ:
-  Mem.WriteInt(count);
+  // Êîëè÷åñòâî òðóïîâ
+  utils.writeInt(st, LongInt(count));
 
-  if count = 0 then
-    Exit;
+  if (count = 0) then exit;
 
-// Ñîõðàíÿåì òðóïû:
+  // Ñîõðàíÿåì òðóïû
   for i := 0 to High(gCorpses) do
+  begin
     if gCorpses[i] <> nil then
     begin
-    // Íàçâàíèå ìîäåëè:
-      Mem.WriteString(gCorpses[i].FModelName);
-    // Òèï ñìåðòè:
-      b := gCorpses[i].Mess;
-      Mem.WriteBoolean(b);
-    // Ñîõðàíÿåì äàííûå òðóïà:
-      gCorpses[i].SaveState(Mem);
+      // Íàçâàíèå ìîäåëè
+      utils.writeStr(st, gCorpses[i].FModelName);
+      // Òèï ñìåðòè
+      utils.writeBool(st, gCorpses[i].Mess);
+      // Ñîõðàíÿåì äàííûå òðóïà:
+      gCorpses[i].SaveState(st);
     end;
+  end;
 end;
 
-procedure g_Player_Corpses_LoadState(var Mem: TBinMemoryReader);
+
+procedure g_Player_Corpses_LoadState (st: TStream);
 var
   count, i: Integer;
   str: String;
   b: Boolean;
 begin
-  if Mem = nil then
-    Exit;
+  assert(st <> nil);
 
   g_Player_RemoveAllCorpses();
 
-// Êîëè÷åñòâî òðóïîâ:
-  Mem.ReadInt(count);
+  // Êîëè÷åñòâî òðóïîâ:
+  count := utils.readLongInt(st);
+  if (count < 0) or (count > Length(gCorpses)) then raise XStreamError.Create('invalid number of corpses');
 
-  if count > Length(gCorpses) then
-  begin
-    raise EBinSizeError.Create('g_Player_Corpses_LoadState: Too Many Corpses');
-  end;
+  if (count = 0) then exit;
 
-  if count = 0 then
-    Exit;
-
-// Çàãðóæàåì òðóïû:
+  // Çàãðóæàåì òðóïû
   for i := 0 to count-1 do
   begin
-  // Íàçâàíèå ìîäåëè:
-    Mem.ReadString(str);
-  // Òèï ñìåðòè:
-    Mem.ReadBoolean(b);
-  // Ñîçäàåì òðóï:
+    // Íàçâàíèå ìîäåëè:
+    str := utils.readStr(st);
+    // Òèï ñìåðòè
+    b := utils.readBool(st);
+    // Ñîçäàåì òðóï
     gCorpses[i] := TCorpse.Create(0, 0, str, b);
-  // Çàãðóæàåì äàííûå òðóïà:
-    gCorpses[i].LoadState(Mem);
+    // Çàãðóæàåì äàííûå òðóïà
+    gCorpses[i].LoadState(st);
   end;
 end;
 
+
 { T P l a y e r : }
 
 function TPlayer.isValidViewPort (): Boolean; inline; begin result := (viewPortW > 0) and (viewPortH > 0); end;
@@ -5673,280 +5643,214 @@ begin
     MH_SEND_PlayerStats(FUID);
 end;
 
-procedure TPlayer.SaveState(var Mem: TBinMemoryWriter);
+procedure TPlayer.SaveState (st: TStream);
 var
   i: Integer;
-  sig: DWORD;
-  str: String;
   b: Byte;
 begin
-  if FIamBot then
-    i := 512
-  else
-    i := 256;
-
-  Mem := TBinMemoryWriter.Create(i);
-
-// Ñèãíàòóðà èãðîêà:
-  sig := PLAYER_SIGNATURE; // 'PLYR'
-  Mem.WriteDWORD(sig);
-// Áîò èëè ÷åëîâåê:
-  Mem.WriteBoolean(FIamBot);
-// UID èãðîêà:
-  Mem.WriteWord(FUID);
-// Èìÿ èãðîêà:
-  Mem.WriteString(FName, 32);
-// Êîìàíäà:
-  Mem.WriteByte(FTeam);
-// Æèâ ëè:
-  Mem.WriteBoolean(FAlive);
-// Èçðàñõîäîâàë ëè âñå æèçíè:
-  Mem.WriteBoolean(FNoRespawn);
-// Íàïðàâëåíèå:
-  if FDirection = D_LEFT then
-    b := 1
-  else // D_RIGHT
-    b := 2;
-  Mem.WriteByte(b);
-// Çäîðîâüå:
-  Mem.WriteInt(FHealth);
-// Æèçíè:
-  Mem.WriteByte(FLives);
-// Áðîíÿ:
-  Mem.WriteInt(FArmor);
-// Çàïàñ âîçäóõà:
-  Mem.WriteInt(FAir);
-// Çàïàñ ãîðþ÷åãî:
-  Mem.WriteInt(FJetFuel);
-// Áîëü:
-  Mem.WriteInt(FPain);
-// Óáèë:
-  Mem.WriteInt(FKills);
-// Óáèë ìîíñòðîâ:
-  Mem.WriteInt(FMonsterKills);
-// Ôðàãîâ:
-  Mem.WriteInt(FFrags);
-// Ôðàãîâ ïîäðÿä:
-  Mem.WriteByte(FFragCombo);
-// Âðåìÿ ïîñëåäíåãî ôðàãà:
-  Mem.WriteDWORD(FLastFrag);
-// Ñìåðòåé:
-  Mem.WriteInt(FDeath);
-// Êàêîé ôëàã íåñåò:
-  Mem.WriteByte(FFlag);
-// Íàøåë ñåêðåòîâ:
-  Mem.WriteInt(FSecrets);
-// Òåêóùåå îðóæèå:
-  Mem.WriteByte(FCurrWeap);
-// Æåëàåìîå îðóæèå:
-  Mem.WriteWord(FNextWeap);
-// ...è ïàóçà
-  Mem.WriteByte(FNextWeapDelay);
-// Âðåìÿ çàðÿäêè BFG:
-  Mem.WriteSmallInt(FBFGFireCounter);
-// Áóôåð óðîíà:
-  Mem.WriteInt(FDamageBuffer);
-// Ïîñëåäíèé óäàðèâøèé:
-  Mem.WriteWord(FLastSpawnerUID);
-// Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
-  Mem.WriteByte(FLastHit);
-// Îáúåêò èãðîêà:
-  Obj_SaveState(@FObj, Mem);
-// Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.WriteWord(FAmmo[i]);
-// Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.WriteWord(FMaxAmmo[i]);
-// Íàëè÷èå îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.WriteBoolean(FWeapon[i]);
-// Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.WriteWord(FReloading[i]);
-// Íàëè÷èå ðþêçàêà:
-  if R_ITEM_BACKPACK in FRulez then
-    b := 1
-  else
-    b := 0;
-  Mem.WriteByte(b);
-// Íàëè÷èå êðàñíîãî êëþ÷à:
-  if R_KEY_RED in FRulez then
-    b := 1
-  else
-    b := 0;
-  Mem.WriteByte(b);
-// Íàëè÷èå çåëåíîãî êëþ÷à:
-  if R_KEY_GREEN in FRulez then
-    b := 1
-  else
-    b := 0;
-  Mem.WriteByte(b);
-// Íàëè÷èå ñèíåãî êëþ÷à:
-  if R_KEY_BLUE in FRulez then
-    b := 1
-  else
-    b := 0;
-  Mem.WriteByte(b);
-// Íàëè÷èå áåðñåðêà:
-  if R_BERSERK in FRulez then
-    b := 1
-  else
-    b := 0;
-  Mem.WriteByte(b);
-// Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
-  for i := MR_SUIT to MR_MAX do
-    Mem.WriteDWORD(FMegaRulez[i]);
-// Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
-  for i := T_RESPAWN to T_FLAGCAP do
-    Mem.WriteDWORD(FTime[i]);
-// Íàçâàíèå ìîäåëè:
-  str := FModel.Name;
-  Mem.WriteString(str);
-// Öâåò ìîäåëè:
-  b := FColor.R;
-  Mem.WriteByte(b);
-  b := FColor.G;
-  Mem.WriteByte(b);
-  b := FColor.B;
-  Mem.WriteByte(b);
-end;
-
-procedure TPlayer.LoadState(var Mem: TBinMemoryReader);
+  // Ñèãíàòóðà èãðîêà
+  utils.writeSign(st, 'PLYR');
+  utils.writeInt(st, Byte(PLR_SAVE_VERSION)); // version
+  // Áîò èëè ÷åëîâåê
+  utils.writeBool(st, FIamBot);
+  // UID èãðîêà
+  utils.writeInt(st, Word(FUID));
+  // Èìÿ èãðîêà
+  utils.writeStr(st, FName);
+  // Êîìàíäà
+  utils.writeInt(st, Byte(FTeam));
+  // Æèâ ëè
+  utils.writeBool(st, FAlive);
+  // Èçðàñõîäîâàë ëè âñå æèçíè
+  utils.writeBool(st, FNoRespawn);
+  // Íàïðàâëåíèå
+  if FDirection = D_LEFT then b := 1 else b := 2; // D_RIGHT
+  utils.writeInt(st, Byte(b));
+  // Çäîðîâüå
+  utils.writeInt(st, LongInt(FHealth));
+  // Æèçíè
+  utils.writeInt(st, Byte(FLives));
+  // Áðîíÿ
+  utils.writeInt(st, LongInt(FArmor));
+  // Çàïàñ âîçäóõà
+  utils.writeInt(st, LongInt(FAir));
+  // Çàïàñ ãîðþ÷åãî
+  utils.writeInt(st, LongInt(FJetFuel));
+  // Áîëü
+  utils.writeInt(st, LongInt(FPain));
+  // Óáèë
+  utils.writeInt(st, LongInt(FKills));
+  // Óáèë ìîíñòðîâ
+  utils.writeInt(st, LongInt(FMonsterKills));
+  // Ôðàãîâ
+  utils.writeInt(st, LongInt(FFrags));
+  // Ôðàãîâ ïîäðÿä
+  utils.writeInt(st, Byte(FFragCombo));
+  // Âðåìÿ ïîñëåäíåãî ôðàãà
+  utils.writeInt(st, LongWord(FLastFrag));
+  // Ñìåðòåé
+  utils.writeInt(st, LongInt(FDeath));
+  // Êàêîé ôëàã íåñåò
+  utils.writeInt(st, Byte(FFlag));
+  // Íàøåë ñåêðåòîâ
+  utils.writeInt(st, LongInt(FSecrets));
+  // Òåêóùåå îðóæèå
+  utils.writeInt(st, Byte(FCurrWeap));
+  // Æåëàåìîå îðóæèå
+  utils.writeInt(st, Word(FNextWeap));
+  // ...è ïàóçà
+  utils.writeInt(st, Byte(FNextWeapDelay));
+  // Âðåìÿ çàðÿäêè BFG
+  utils.writeInt(st, SmallInt(FBFGFireCounter));
+  // Áóôåð óðîíà
+  utils.writeInt(st, LongInt(FDamageBuffer));
+  // Ïîñëåäíèé óäàðèâøèé
+  utils.writeInt(st, Word(FLastSpawnerUID));
+  // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà
+  utils.writeInt(st, Byte(FLastHit));
+  // Îáúåêò èãðîêà
+  Obj_SaveState(st, @FObj);
+  // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FAmmo[i]));
+  // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FMaxAmmo[i]));
+  // Íàëè÷èå îðóæèÿ
+  for i := WP_FIRST to WP_LAST do utils.writeBool(st, FWeapon[i]);
+  // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ
+  for i := WP_FIRST to WP_LAST do utils.writeInt(st, Word(FReloading[i]));
+  // Íàëè÷èå ðþêçàêà
+  utils.writeBool(st, (R_ITEM_BACKPACK in FRulez));
+  // Íàëè÷èå êðàñíîãî êëþ÷à
+  utils.writeBool(st, (R_KEY_RED in FRulez));
+  // Íàëè÷èå çåëåíîãî êëþ÷à
+  utils.writeBool(st, (R_KEY_GREEN in FRulez));
+  // Íàëè÷èå ñèíåãî êëþ÷à
+  utils.writeBool(st, (R_KEY_BLUE in FRulez));
+  // Íàëè÷èå áåðñåðêà
+  utils.writeBool(st, (R_BERSERK in FRulez));
+  // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ
+  for i := MR_SUIT to MR_MAX do utils.writeInt(st, LongWord(FMegaRulez[i]));
+  // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà
+  for i := T_RESPAWN to T_FLAGCAP do utils.writeInt(st, LongWord(FTime[i]));
+  // Íàçâàíèå ìîäåëè
+  utils.writeStr(st, FModel.Name);
+  // Öâåò ìîäåëè
+  utils.writeInt(st, Byte(FColor.R));
+  utils.writeInt(st, Byte(FColor.G));
+  utils.writeInt(st, Byte(FColor.B));
+end;
+
+
+procedure TPlayer.LoadState (st: TStream);
 var
   i: Integer;
-  sig: DWORD;
   str: String;
   b: Byte;
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà èãðîêà:
-  Mem.ReadDWORD(sig);
-  if sig <> PLAYER_SIGNATURE then // 'PLYR'
-  begin
-    raise EBinSizeError.Create('TPlayer.LoadState: Wrong Player Signature');
-  end;
-// Áîò èëè ÷åëîâåê:
-  Mem.ReadBoolean(FIamBot);
-// UID èãðîêà:
-  Mem.ReadWord(FUID);
-// Èìÿ èãðîêà:
-  Mem.ReadString(str);
-  if (Self <> gPlayer1) and (Self <> gPlayer2) then
-    FName := str;
-// Êîìàíäà:
-  Mem.ReadByte(FTeam);
-// Æèâ ëè:
-  Mem.ReadBoolean(FAlive);
-// Èçðàñõîäîâàë ëè âñå æèçíè:
-  Mem.ReadBoolean(FNoRespawn);
-// Íàïðàâëåíèå:
-  Mem.ReadByte(b);
-  if b = 1 then
-    FDirection := D_LEFT
-  else // b = 2
-    FDirection := D_RIGHT;
-// Çäîðîâüå:
-  Mem.ReadInt(FHealth);
-// Æèçíè:
-  Mem.ReadByte(FLives);
-// Áðîíÿ:
-  Mem.ReadInt(FArmor);
-// Çàïàñ âîçäóõà:
-  Mem.ReadInt(FAir);
-// Çàïàñ ãîðþ÷åãî:
-  Mem.ReadInt(FJetFuel);
-// Áîëü:
-  Mem.ReadInt(FPain);
-// Óáèë:
-  Mem.ReadInt(FKills);
-// Óáèë ìîíñòðîâ:
-  Mem.ReadInt(FMonsterKills);
-// Ôðàãîâ:
-  Mem.ReadInt(FFrags);
-// Ôðàãîâ ïîäðÿä:
-  Mem.ReadByte(FFragCombo);
-// Âðåìÿ ïîñëåäíåãî ôðàãà:
-  Mem.ReadDWORD(FLastFrag);
-// Ñìåðòåé:
-  Mem.ReadInt(FDeath);
-// Êàêîé ôëàã íåñåò:
-  Mem.ReadByte(FFlag);
-// Íàøåë ñåêðåòîâ:
-  Mem.ReadInt(FSecrets);
-// Òåêóùåå îðóæèå:
-  Mem.ReadByte(FCurrWeap);
-// Æåëàåìîå îðóæèå:
-  Mem.ReadWord(FNextWeap);
-// ...è ïàóçà
-  Mem.ReadByte(FNextWeapDelay);
-// Âðåìÿ çàðÿäêè BFG:
-  Mem.ReadSmallInt(FBFGFireCounter);
-// Áóôåð óðîíà:
-  Mem.ReadInt(FDamageBuffer);
-// Ïîñëåäíèé óäàðèâøèé:
-  Mem.ReadWord(FLastSpawnerUID);
-// Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà:
-  Mem.ReadByte(FLastHit);
-// Îáúåêò èãðîêà:
-  Obj_LoadState(@FObj, Mem);
-// Òåêóùåå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.ReadWord(FAmmo[i]);
-// Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ:
-  for i := A_BULLETS to A_HIGH do
-    Mem.ReadWord(FMaxAmmo[i]);
-// Íàëè÷èå îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.ReadBoolean(FWeapon[i]);
-// Âðåìÿ ïåðåçàðÿäêè îðóæèÿ:
-  for i := WP_FIRST to WP_LAST do
-    Mem.ReadWord(FReloading[i]);
-// Íàëè÷èå ðþêçàêà:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(FRulez, R_ITEM_BACKPACK);
-// Íàëè÷èå êðàñíîãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(FRulez, R_KEY_RED);
-// Íàëè÷èå çåëåíîãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(FRulez, R_KEY_GREEN);
-// Íàëè÷èå ñèíåãî êëþ÷à:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(FRulez, R_KEY_BLUE);
-// Íàëè÷èå áåðñåðêà:
-  Mem.ReadByte(b);
-  if b = 1 then
-    Include(FRulez, R_BERSERK);
-// Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ:
-  for i := MR_SUIT to MR_MAX do
-    Mem.ReadDWORD(FMegaRulez[i]);
-// Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà:
-  for i := T_RESPAWN to T_FLAGCAP do
-    Mem.ReadDWORD(FTime[i]);
-// Íàçâàíèå ìîäåëè:
-  Mem.ReadString(str);
-// Öâåò ìîäåëè:
-  Mem.ReadByte(FColor.R);
-  Mem.ReadByte(FColor.G);
-  Mem.ReadByte(FColor.B);
-  if Self = gPlayer1 then
+  assert(st <> nil);
+
+  // Ñèãíàòóðà èãðîêà
+  if not utils.checkSign(st, 'PLYR') then raise XStreamError.Create('invalid player signature');
+  if (utils.readByte(st) <> PLR_SAVE_VERSION) then raise XStreamError.Create('invalid player version');
+  // Áîò èëè ÷åëîâåê:
+  FIamBot := utils.readBool(st);
+  // UID èãðîêà
+  FUID := utils.readWord(st);
+  // Èìÿ èãðîêà
+  str := utils.readStr(st);
+  if (self <> gPlayer1) and (self <> gPlayer2) then FName := str;
+  // Êîìàíäà
+  FTeam := utils.readByte(st);
+  // Æèâ ëè
+  FAlive := utils.readBool(st);
+  // Èçðàñõîäîâàë ëè âñå æèçíè
+  FNoRespawn := utils.readBool(st);
+  // Íàïðàâëåíèå
+  b := utils.readByte(st);
+  if b = 1 then FDirection := D_LEFT else FDirection := D_RIGHT; // b = 2
+  // Çäîðîâüå
+  FHealth := utils.readLongInt(st);
+  // Æèçíè
+  FLives := utils.readByte(st);
+  // Áðîíÿ
+  FArmor := utils.readLongInt(st);
+  // Çàïàñ âîçäóõà
+  FAir := utils.readLongInt(st);
+  // Çàïàñ ãîðþ÷åãî
+  FJetFuel := utils.readLongInt(st);
+  // Áîëü
+  FPain := utils.readLongInt(st);
+  // Óáèë
+  FKills := utils.readLongInt(st);
+  // Óáèë ìîíñòðîâ
+  FMonsterKills := utils.readLongInt(st);
+  // Ôðàãîâ
+  FFrags := utils.readLongInt(st);
+  // Ôðàãîâ ïîäðÿä
+  FFragCombo := utils.readByte(st);
+  // Âðåìÿ ïîñëåäíåãî ôðàãà
+  FLastFrag := utils.readLongWord(st);
+  // Ñìåðòåé
+  FDeath := utils.readLongInt(st);
+  // Êàêîé ôëàã íåñåò
+  FFlag := utils.readByte(st);
+  // Íàøåë ñåêðåòîâ
+  FSecrets := utils.readLongInt(st);
+  // Òåêóùåå îðóæèå
+  FCurrWeap := utils.readByte(st);
+  // Æåëàåìîå îðóæèå
+  FNextWeap := utils.readWord(st);
+  // ...è ïàóçà
+  FNextWeapDelay := utils.readByte(st);
+  // Âðåìÿ çàðÿäêè BFG
+  FBFGFireCounter := utils.readSmallInt(st);
+  // Áóôåð óðîíà
+  FDamageBuffer := utils.readLongInt(st);
+  // Ïîñëåäíèé óäàðèâøèé
+  FLastSpawnerUID := utils.readWord(st);
+  // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà
+  FLastHit := utils.readByte(st);
+  // Îáúåêò èãðîêà
+  Obj_LoadState(@FObj, st);
+  // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do FAmmo[i] := utils.readWord(st);
+  // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ
+  for i := A_BULLETS to A_HIGH do FMaxAmmo[i] := utils.readWord(st);
+  // Íàëè÷èå îðóæèÿ
+  for i := WP_FIRST to WP_LAST do FWeapon[i] := utils.readBool(st);
+  // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ
+  for i := WP_FIRST to WP_LAST do FReloading[i] := utils.readWord(st);
+  // Íàëè÷èå ðþêçàêà
+  if utils.readBool(st) then Include(FRulez, R_ITEM_BACKPACK);
+  // Íàëè÷èå êðàñíîãî êëþ÷à
+  if utils.readBool(st) then Include(FRulez, R_KEY_RED);
+  // Íàëè÷èå çåëåíîãî êëþ÷à
+  if utils.readBool(st) then Include(FRulez, R_KEY_GREEN);
+  // Íàëè÷èå ñèíåãî êëþ÷à
+  if utils.readBool(st) then Include(FRulez, R_KEY_BLUE);
+  // Íàëè÷èå áåðñåðêà
+  if utils.readBool(st) then Include(FRulez, R_BERSERK);
+  // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ
+  for i := MR_SUIT to MR_MAX do FMegaRulez[i] := utils.readLongWord(st);
+  // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà
+  for i := T_RESPAWN to T_FLAGCAP do FTime[i] := utils.readLongWord(st);
+  // Íàçâàíèå ìîäåëè
+  str := utils.readStr(st);
+  // Öâåò ìîäåëè
+  FColor.R := utils.readByte(st);
+  FColor.G := utils.readByte(st);
+  FColor.B := utils.readByte(st);
+  if (self = gPlayer1) then
   begin
     str := gPlayer1Settings.Model;
     FColor := gPlayer1Settings.Color;
-  end;
-  if Self = gPlayer2 then
+  end
+  else if (self = gPlayer2) then
   begin
     str := gPlayer2Settings.Model;
     FColor := gPlayer2Settings.Color;
   end;
-// Îáíîâëÿåì ìîäåëü èãðîêà:
+  // Îáíîâëÿåì ìîäåëü èãðîêà
   SetModel(str);
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
     FModel.Color := TEAMCOLOR[FTeam]
@@ -5954,6 +5858,7 @@ begin
     FModel.Color := FColor;
 end;
 
+
 procedure TPlayer.AllRulez(Health: Boolean);
 var
   a: Integer;
@@ -6332,80 +6237,73 @@ begin
     FAnimationMask.Update();
 end;
 
-procedure TCorpse.SaveState(var Mem: TBinMemoryWriter);
+
+procedure TCorpse.SaveState (st: TStream);
 var
-  sig: DWORD;
   anim: Boolean;
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà òðóïà:
-  sig := CORPSE_SIGNATURE; // 'CORP'
-  Mem.WriteDWORD(sig);
-// Ñîñòîÿíèå:
-  Mem.WriteByte(FState);
-// Íàêîïëåííûé óðîí:
-  Mem.WriteByte(FDamage);
-// Öâåò:
-  Mem.WriteByte(FColor.R);
-  Mem.WriteByte(FColor.G);
-  Mem.WriteByte(FColor.B);
-// Îáúåêò òðóïà:
-  Obj_SaveState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ:
-  anim := FAnimation <> nil;
-  Mem.WriteBoolean(anim);
-// Åñëè åñòü - ñîõðàíÿåì:
-  if anim then
-    FAnimation.SaveState(Mem);
-// Åñòü ëè ìàñêà àíèìàöèè:
-  anim := FAnimationMask <> nil;
-  Mem.WriteBoolean(anim);
-// Åñëè åñòü - ñîõðàíÿåì:
-  if anim then
-    FAnimationMask.SaveState(Mem);
-end;
-
-procedure TCorpse.LoadState(var Mem: TBinMemoryReader);
+  assert(st <> nil);
+
+  // Ñèãíàòóðà òðóïà
+  utils.writeSign(st, 'CORP');
+  utils.writeInt(st, Byte(0));
+  // Ñîñòîÿíèå
+  utils.writeInt(st, Byte(FState));
+  // Íàêîïëåííûé óðîí
+  utils.writeInt(st, Byte(FDamage));
+  // Öâåò
+  utils.writeInt(st, Byte(FColor.R));
+  utils.writeInt(st, Byte(FColor.G));
+  utils.writeInt(st, Byte(FColor.B));
+  // Îáúåêò òðóïà
+  Obj_SaveState(st, @FObj);
+  // Åñòü ëè àíèìàöèÿ
+  anim := (FAnimation <> nil);
+  utils.writeBool(st, anim);
+  // Åñëè åñòü - ñîõðàíÿåì
+  if anim then FAnimation.SaveState(st);
+  // Åñòü ëè ìàñêà àíèìàöèè
+  anim := (FAnimationMask <> nil);
+  utils.writeBool(st, anim);
+  // Åñëè åñòü - ñîõðàíÿåì
+  if anim then FAnimationMask.SaveState(st);
+end;
+
+
+procedure TCorpse.LoadState (st: TStream);
 var
-  sig: DWORD;
   anim: Boolean;
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà òðóïà:
-  Mem.ReadDWORD(sig);
-  if sig <> CORPSE_SIGNATURE then // 'CORP'
-  begin
-    raise EBinSizeError.Create('TCorpse.LoadState: Wrong Corpse Signature');
-  end;
-// Ñîñòîÿíèå:
-  Mem.ReadByte(FState);
-// Íàêîïëåííûé óðîí:
-  Mem.ReadByte(FDamage);
-// Öâåò:
-  Mem.ReadByte(FColor.R);
-  Mem.ReadByte(FColor.G);
-  Mem.ReadByte(FColor.B);
-// Îáúåêò òðóïà:
-  Obj_LoadState(@FObj, Mem);
-// Åñòü ëè àíèìàöèÿ:
-  Mem.ReadBoolean(anim);
-// Åñëè åñòü - çàãðóæàåì:
+  assert(st <> nil);
+
+  // Ñèãíàòóðà òðóïà
+  if not utils.checkSign(st, 'CORP') then raise XStreamError.Create('invalid corpse signature');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid corpse version');
+  // Ñîñòîÿíèå
+  FState := utils.readByte(st);
+  // Íàêîïëåííûé óðîí
+  FDamage := utils.readByte(st);
+  // Öâåò
+  FColor.R := utils.readByte(st);
+  FColor.G := utils.readByte(st);
+  FColor.B := utils.readByte(st);
+  // Îáúåêò òðóïà
+  Obj_LoadState(@FObj, st);
+  // Åñòü ëè àíèìàöèÿ
+  anim := utils.readBool(st);
+  // Åñëè åñòü - çàãðóæàåì
   if anim then
   begin
     Assert(FAnimation <> nil, 'TCorpse.LoadState: no FAnimation');
-    FAnimation.LoadState(Mem);
+    FAnimation.LoadState(st);
   end;
-// Åñòü ëè ìàñêà àíèìàöèè:
-  Mem.ReadBoolean(anim);
-// Åñëè åñòü - çàãðóæàåì:
+  // Åñòü ëè ìàñêà àíèìàöèè
+  anim := utils.readBool(st);
+  // Åñëè åñòü - çàãðóæàåì
   if anim then
   begin
     Assert(FAnimationMask <> nil, 'TCorpse.LoadState: no FAnimationMask');
-    FAnimationMask.LoadState(Mem);
+    FAnimationMask.LoadState(st);
   end;
 end;
 
@@ -7611,64 +7509,84 @@ begin
   Result := Round((255-a)/255*radius*(Random(2)-1));
 end;
 
-procedure TBot.SaveState(var Mem: TBinMemoryWriter);
+
+procedure TDifficult.save (st: TStream);
+begin
+  utils.writeInt(st, Byte(DiagFire));
+  utils.writeInt(st, Byte(InvisFire));
+  utils.writeInt(st, Byte(DiagPrecision));
+  utils.writeInt(st, Byte(FlyPrecision));
+  utils.writeInt(st, Byte(Cover));
+  utils.writeInt(st, Byte(CloseJump));
+  st.WriteBuffer(WeaponPrior[Low(WeaponPrior)], sizeof(WeaponPrior));
+  st.WriteBuffer(CloseWeaponPrior[Low(CloseWeaponPrior)], sizeof(CloseWeaponPrior));
+end;
+
+procedure TDifficult.load (st: TStream);
+begin
+  DiagFire := utils.readByte(st);
+  InvisFire := utils.readByte(st);
+  DiagPrecision := utils.readByte(st);
+  FlyPrecision := utils.readByte(st);
+  Cover := utils.readByte(st);
+  CloseJump := utils.readByte(st);
+  st.ReadBuffer(WeaponPrior[Low(WeaponPrior)], sizeof(WeaponPrior));
+  st.ReadBuffer(CloseWeaponPrior[Low(CloseWeaponPrior)], sizeof(CloseWeaponPrior));
+end;
+
+
+procedure TBot.SaveState (st: TStream);
 var
   i: Integer;
-  dw: DWORD;
-  p: Pointer;
-begin
-  inherited SaveState(Mem);
-
-// Âûáðàííîå îðóæèå:
-  Mem.WriteByte(FSelectedWeapon);
-// UID öåëè:
-  Mem.WriteWord(FTargetUID);
-// Âðåìÿ ïîòåðè öåëè:
-  Mem.WriteDWORD(FLastVisible);
-// Êîëè÷åñòâî ôëàãîâ ÈÈ:
+  dw: Integer;
+begin
+  inherited SaveState(st);
+  utils.writeSign(st, 'BOT0');
+  // Âûáðàííîå îðóæèå
+  utils.writeInt(st, Byte(FSelectedWeapon));
+  // UID öåëè
+  utils.writeInt(st, Word(FTargetUID));
+  // Âðåìÿ ïîòåðè öåëè
+  utils.writeInt(st, LongWord(FLastVisible));
+  // Êîëè÷åñòâî ôëàãîâ ÈÈ
   dw := Length(FAIFlags);
-  Mem.WriteDWORD(dw);
-// Ôëàãè ÈÈ:
-  for i := 0 to Integer(dw)-1 do
+  utils.writeInt(st, LongInt(dw));
+  // Ôëàãè ÈÈ
+  for i := 0 to dw-1 do
   begin
-    Mem.WriteString(FAIFlags[i].Name, 20);
-    Mem.WriteString(FAIFlags[i].Value, 20);
+    utils.writeStr(st, FAIFlags[i].Name, 20);
+    utils.writeStr(st, FAIFlags[i].Value, 20);
   end;
-// Íàñòðîéêè ñëîæíîñòè:
-  p := @FDifficult;
-  Mem.WriteMemory(p, SizeOf(TDifficult));
+  // Íàñòðîéêè ñëîæíîñòè
+  FDifficult.save(st);
 end;
 
-procedure TBot.LoadState(var Mem: TBinMemoryReader);
+
+procedure TBot.LoadState (st: TStream);
 var
   i: Integer;
-  dw: DWORD;
-  p: Pointer;
-begin
-  inherited LoadState(Mem);
-
-// Âûáðàííîå îðóæèå:
-  Mem.ReadByte(FSelectedWeapon);
-// UID öåëè:
-  Mem.ReadWord(FTargetUID);
-// Âðåìÿ ïîòåðè öåëè:
-  Mem.ReadDWORD(FLastVisible);
-// Êîëè÷åñòâî ôëàãîâ ÈÈ:
-  Mem.ReadDWORD(dw);
+  dw: Integer;
+begin
+  inherited LoadState(st);
+  if not utils.checkSign(st, 'BOT0') then raise XStreamError.Create('invalid bot signature');
+  // Âûáðàííîå îðóæèå
+  FSelectedWeapon := utils.readByte(st);
+  // UID öåëè
+  FTargetUID := utils.readWord(st);
+  // Âðåìÿ ïîòåðè öåëè
+  FLastVisible := utils.readLongWord(st);
+  // Êîëè÷åñòâî ôëàãîâ ÈÈ
+  dw := utils.readLongInt(st);
+  if (dw < 0) or (dw > 16384) then raise XStreamError.Create('invalid number of bot AI flags');
   SetLength(FAIFlags, dw);
-// Ôëàãè ÈÈ:
-  for i := 0 to Integer(dw)-1 do
-  begin
-    Mem.ReadString(FAIFlags[i].Name);
-    Mem.ReadString(FAIFlags[i].Value);
-  end;
-// Íàñòðîéêè ñëîæíîñòè:
-  Mem.ReadMemory(p, dw);
-  if dw <> SizeOf(TDifficult) then
+  // Ôëàãè ÈÈ
+  for i := 0 to dw-1 do
   begin
-    raise EBinSizeError.Create('TBot.LoadState: Wrong FDifficult Size');
+    FAIFlags[i].Name := utils.readStr(st, 20);
+    FAIFlags[i].Value := utils.readStr(st, 20);
   end;
-  FDifficult := TDifficult(p^);
+  // Íàñòðîéêè ñëîæíîñòè
+  FDifficult.load(st);
 end;
 
 
index 93c392ce8804dc298ccdbc9b9a5fa9be42037392..4391bf51b1bda3f9b76d5b02beb706ddf46c4a05 100644 (file)
@@ -19,320 +19,269 @@ unit g_saveload;
 interface
 
 uses
-  e_graphics, g_phys, g_textures, BinEditor;
+  SysUtils, Classes,
+  e_graphics, g_phys, g_textures;
 
 
-function g_GetSaveName(n: Integer): String;
-function g_SaveGame(n: Integer; Name: String): Boolean;
-function g_LoadGame(n: Integer): Boolean;
-procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter);
-procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader);
+function g_GetSaveName (n: Integer; out valid: Boolean): AnsiString;
+
+function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean;
+function g_LoadGameFrom (const filename: AnsiString): Boolean;
+
+function g_SaveGame (n: Integer; const aname: AnsiString): Boolean;
+function g_LoadGame (n: Integer): Boolean;
+
+procedure Obj_SaveState (st: TStream; o: PObj);
+procedure Obj_LoadState (o: PObj; st: TStream);
 
 
 implementation
 
 uses
+  MAPDEF, utils, xstreams,
   g_game, g_items, g_map, g_monsters, g_triggers,
-  g_basic, g_main, SysUtils, Math, wadreader,
-  MAPDEF, g_weapons, g_player, g_console,
+  g_basic, g_main, Math, wadreader,
+  g_weapons, g_player, g_console,
   e_log, g_language;
 
 const
   SAVE_SIGNATURE = $56534644; // 'DFSV'
-  SAVE_VERSION = $06;
+  SAVE_VERSION = $07;
   END_MARKER_STRING = 'END';
   PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW'
   OBJ_SIGNATURE = $4A424F5F; // '_OBJ'
 
 
-procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter);
-var
-  sig: DWORD;
+procedure Obj_SaveState (st: TStream; o: PObj);
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà îáúåêòà:
-  sig := OBJ_SIGNATURE; // '_OBJ'
-  Mem.WriteDWORD(sig);
-// Ïîëîæåíèå ïî-ãîðèçîíòàëè:
-  Mem.WriteInt(o^.X);
-// Ïîëîæåíèå ïî-âåðòèêàëè:
-  Mem.WriteInt(o^.Y);
-// Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
-  Mem.WriteInt(o^.Rect.X);
-  Mem.WriteInt(o^.Rect.Y);
-  Mem.WriteWord(o^.Rect.Width);
-  Mem.WriteWord(o^.Rect.Height);
-// Ñêîðîñòü:
-  Mem.WriteInt(o^.Vel.X);
-  Mem.WriteInt(o^.Vel.Y);
-// Ïðèáàâêà ê ñêîðîñòè:
-  Mem.WriteInt(o^.Accel.X);
-  Mem.WriteInt(o^.Accel.Y);
+  if (st = nil) then exit;
+  // Ñèãíàòóðà îáúåêòà
+  utils.writeSign(st, '_OBJ');
+  utils.writeInt(st, Byte(0)); // version
+  // Ïîëîæåíèå ïî-ãîðèçîíòàëè
+  utils.writeInt(st, LongInt(o^.X));
+  // Ïîëîæåíèå ïî-âåðòèêàëè
+  utils.writeInt(st, LongInt(o^.Y));
+  // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê
+  utils.writeInt(st, LongInt(o^.Rect.X));
+  utils.writeInt(st, LongInt(o^.Rect.Y));
+  utils.writeInt(st, Word(o^.Rect.Width));
+  utils.writeInt(st, Word(o^.Rect.Height));
+  // Ñêîðîñòü
+  utils.writeInt(st, LongInt(o^.Vel.X));
+  utils.writeInt(st, LongInt(o^.Vel.Y));
+  // Óñêîðåíèå
+  utils.writeInt(st, LongInt(o^.Accel.X));
+  utils.writeInt(st, LongInt(o^.Accel.Y));
 end;
 
-procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader);
-var
-  sig: DWORD;
+
+procedure Obj_LoadState (o: PObj; st: TStream);
 begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà îáúåêòà:
-  Mem.ReadDWORD(sig);
-  if sig <> OBJ_SIGNATURE then // '_OBJ'
-  begin
-    raise EBinSizeError.Create('Obj_LoadState: Wrong Object Signature');
-  end;
-// Ïîëîæåíèå ïî-ãîðèçîíòàëè:
-  Mem.ReadInt(o^.X);
-// Ïîëîæåíèå ïî-âåðòèêàëè:
-  Mem.ReadInt(o^.Y);
-// Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê:
-  Mem.ReadInt(o^.Rect.X);
-  Mem.ReadInt(o^.Rect.Y);
-  Mem.ReadWord(o^.Rect.Width);
-  Mem.ReadWord(o^.Rect.Height);
-// Ñêîðîñòü:
-  Mem.ReadInt(o^.Vel.X);
-  Mem.ReadInt(o^.Vel.Y);
-// Ïðèáàâêà ê ñêîðîñòè:
-  Mem.ReadInt(o^.Accel.X);
-  Mem.ReadInt(o^.Accel.Y);
+  if (st = nil) then exit;
+  // Ñèãíàòóðà îáúåêòà:
+  if not utils.checkSign(st, '_OBJ') then raise XStreamError.Create('invalid object signature');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid object version');
+  // Ïîëîæåíèå ïî-ãîðèçîíòàëè
+  o^.X := utils.readLongInt(st);
+  // Ïîëîæåíèå ïî-âåðòèêàëè
+  o^.Y := utils.readLongInt(st);
+  // Îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê
+  o^.Rect.X := utils.readLongInt(st);
+  o^.Rect.Y := utils.readLongInt(st);
+  o^.Rect.Width := utils.readWord(st);
+  o^.Rect.Height := utils.readWord(st);
+  // Ñêîðîñòü
+  o^.Vel.X := utils.readLongInt(st);
+  o^.Vel.Y := utils.readLongInt(st);
+  // Óñêîðåíèå
+  o^.Accel.X := utils.readLongInt(st);
+  o^.Accel.Y := utils.readLongInt(st);
 end;
 
-function g_GetSaveName(n: Integer): String;
-var
-  bFile: TBinFileReader;
-  bMem: TBinMemoryReader;
-  str: String;
+
+function buildSaveName (n: Integer): AnsiString;
 begin
-  Result := '';
-  str := '';
-  bMem := nil;
-  bFile := nil;
+  result := '';
+  if (n < 0) or (n > 65535) then exit;
+  result := formatstrf('%sSAVGAME%s.DAT', [DataDir, n]);
+end;
 
+
+function g_GetSaveName (n: Integer; out valid: Boolean): AnsiString;
+var
+  st: TStream = nil;
+  ver: Byte;
+  stlen: Word;
+begin
+  valid := false;
+  result := '';
+  if (n < 0) or (n > 65535) then exit;
   try
-  // Îòêðûâàåì ôàéë ñîõðàíåíèé:
-    bFile := TBinFileReader.Create();
-    if bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT',
-                      SAVE_SIGNATURE, SAVE_VERSION) then
-    begin
-    // ×èòàåì ïåðâûé áëîê - ñîñòîÿíèå èãðû:
-      bMem := TBinMemoryReader.Create();
-      bFile.ReadMemory(bMem);
-    // Èìÿ èãðû:
-      bMem.ReadString(str);
-
-    // Çàêðûâàåì ôàéë:
-      bFile.Close();
+    // Îòêðûâàåì ôàéë ñîõðàíåíèé
+    st := openDiskFileRO(buildSaveName(n));
+    try
+      if not utils.checkSign(st, 'DFSV') then raise XStreamError.Create('invalid save game signature');
+      ver := utils.readByte(st);
+      if (ver < 7) then
+      begin
+        utils.readLongWord(st); // section size
+        stlen := utils.readWord(st);
+        if (stlen < 1) or (stlen > 64) then raise XStreamError.Create('invalid save game version');
+        // Èìÿ ñýéâà
+        SetLength(result, stlen);
+        st.ReadBuffer(result[1], stlen);
+      end
+      else
+      begin
+        // 7+
+        // Èìÿ ñýéâà
+        result := utils.readStr(st, 64);
+      end;
+      valid := (ver = SAVE_VERSION);
+      //if (utils.readByte(st) <> SAVE_VERSION) then raise XStreamError.Create('invalid save game version');
+    finally
+      st.Free();
     end;
-
   except
-    on E1: EInOutError do
-      e_WriteLog('GetSaveName I/O Error: '+E1.Message, MSG_WARNING);
-    on E2: EBinSizeError do
-      e_WriteLog('GetSaveName Size Error: '+E2.Message, MSG_WARNING);
+    on e: Exception do
+    begin
+      e_WriteLog('GetSaveName Error: '+e.message, MSG_WARNING);
+      {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
+      result := '';
+    end;
   end;
-
-  bMem.Free();
-  bFile.Free();
-
-  Result := str;
 end;
 
-function g_SaveGame(n: Integer; Name: String): Boolean;
+
+function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean;
 var
-  bFile: TBinFileWriter;
-  bMem: TBinMemoryWriter;
-  sig: DWORD;
-  str: String;
-  nPlayers: Byte;
+  st: TStream = nil;
   i, k: Integer;
   PID1, PID2: Word;
 begin
-  Result := False;
-  bMem := nil;
-  bFile := nil;
-
+  result := false;
   try
-  // Ñîçäàåì ôàéë ñîõðàíåíèÿ:
-    bFile := TBinFileWriter.Create();
-    bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT',
-                   SAVE_SIGNATURE, SAVE_VERSION);
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå èãðû: /////
-    bMem := TBinMemoryWriter.Create(256);
-  // Èìÿ èãðû:
-    bMem.WriteString(Name, 32);
-  // Ïîëíûé ïóòü ê âàäó è êàðòà
-    if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('SAVE: current map is ''%s''...', [gCurrentMapFileName]);
-    bMem.WriteString(gCurrentMapFileName);
-  // Ïóòü ê êàðòå:
-    str := gGameSettings.WAD;
-    bMem.WriteString(str, 128);
-  // Èìÿ êàðòû:
-    str := g_ExtractFileName(gMapInfo.Map);
-    bMem.WriteString(str, 32);
-  // Êîëè÷åñòâî èãðîêîâ:
-    nPlayers := g_Player_GetCount();
-    bMem.WriteByte(nPlayers);
-  // Èãðîâîå âðåìÿ:
-    bMem.WriteDWORD(gTime);
-  // Òèï èãðû:
-    bMem.WriteByte(gGameSettings.GameType);
-  // Ðåæèì èãðû:
-    bMem.WriteByte(gGameSettings.GameMode);
-  // Ëèìèò âðåìåíè:
-    bMem.WriteWord(gGameSettings.TimeLimit);
-  // Ëèìèò î÷êîâ:
-    bMem.WriteWord(gGameSettings.GoalLimit);
-  // Ëèìèò æèçíåé:
-    bMem.WriteByte(gGameSettings.MaxLives);
-  // Èãðîâûå îïöèè:
-    bMem.WriteDWORD(gGameSettings.Options);
-  // Äëÿ êîîïà:
-    bMem.WriteWord(gCoopMonstersKilled);
-    bMem.WriteWord(gCoopSecretsFound);
-    bMem.WriteWord(gCoopTotalMonstersKilled);
-    bMem.WriteWord(gCoopTotalSecretsFound);
-    bMem.WriteWord(gCoopTotalMonsters);
-    bMem.WriteWord(gCoopTotalSecrets);
-  // Ñîõðàíÿåì ñîñòîÿíèå èãðû:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
-    bMem := TBinMemoryWriter.Create(8);
-    sig := PLAYER_VIEW_SIGNATURE;
-    bMem.WriteDWORD(sig); // 'PLVW'
-    PID1 := 0;
-    PID2 := 0;
-    if gPlayer1 <> nil then
-      PID1 := gPlayer1.UID;
-    if gPlayer2 <> nil then
-      PID2 := gPlayer2.UID;
-    bMem.WriteWord(PID1);
-    bMem.WriteWord(PID2);
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå êàðòû: /////
-    g_Map_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå êàðòû:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå ïðåäìåòîâ: /////
-    g_Items_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå ïðåäìåòîâ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå òðèããåðîâ: /////
-    g_Triggers_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå òðèããåðîâ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå îðóæèÿ: /////
-    g_Weapon_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå îðóæèÿ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå ìîíñòðîâ: /////
-    g_Monsters_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå ìîíñòðîâ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ïîëó÷àåì ñîñòîÿíèå òðóïîâ: /////
-    g_Player_Corpses_SaveState(bMem);
-  // Ñîõðàíÿåì ñîñòîÿíèå òðóïîâ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
-    if nPlayers > 0 then
-    begin
-      k := 0;
-      for i := 0 to High(gPlayers) do
-        if gPlayers[i] <> nil then
+    st := createDiskFile(filename);
+    try
+      utils.writeSign(st, 'DFSV');
+      utils.writeInt(st, Byte(SAVE_VERSION));
+      // Èìÿ ñýéâà
+      utils.writeStr(st, aname, 64);
+      // Ïîëíûé ïóòü ê âàäó è êàðòà
+      //if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('SAVE: current map is ''%s''...', [gCurrentMapFileName]);
+      utils.writeStr(st, gCurrentMapFileName);
+      // Ïóòü ê êàðòå
+      utils.writeStr(st, gGameSettings.WAD);
+      // Èìÿ êàðòû
+      utils.writeStr(st, g_ExtractFileName(gMapInfo.Map));
+      // Êîëè÷åñòâî èãðîêîâ
+      utils.writeInt(st, Word(g_Player_GetCount));
+      // Èãðîâîå âðåìÿ
+      utils.writeInt(st, LongWord(gTime));
+      // Òèï èãðû
+      utils.writeInt(st, Byte(gGameSettings.GameType));
+      // Ðåæèì èãðû
+      utils.writeInt(st, Byte(gGameSettings.GameMode));
+      // Ëèìèò âðåìåíè
+      utils.writeInt(st, Word(gGameSettings.TimeLimit));
+      // Ëèìèò î÷êîâ
+      utils.writeInt(st, Word(gGameSettings.GoalLimit));
+      // Ëèìèò æèçíåé
+      utils.writeInt(st, Byte(gGameSettings.MaxLives));
+      // Èãðîâûå îïöèè
+      utils.writeInt(st, LongWord(gGameSettings.Options));
+      // Äëÿ êîîïà
+      utils.writeInt(st, Word(gCoopMonstersKilled));
+      utils.writeInt(st, Word(gCoopSecretsFound));
+      utils.writeInt(st, Word(gCoopTotalMonstersKilled));
+      utils.writeInt(st, Word(gCoopTotalSecretsFound));
+      utils.writeInt(st, Word(gCoopTotalMonsters));
+      utils.writeInt(st, Word(gCoopTotalSecrets));
+
+      ///// Ñîõðàíÿåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà /////
+      utils.writeSign(st, 'PLVW');
+      utils.writeInt(st, Byte(0)); // version
+      PID1 := 0;
+      PID2 := 0;
+      if (gPlayer1 <> nil) then PID1 := gPlayer1.UID;
+      if (gPlayer2 <> nil) then PID2 := gPlayer2.UID;
+      utils.writeInt(st, Word(PID1));
+      utils.writeInt(st, Word(PID2));
+      ///// /////
+
+      ///// Ñîñòîÿíèå êàðòû /////
+      g_Map_SaveState(st);
+      ///// /////
+
+      ///// Ñîñòîÿíèå ïðåäìåòîâ /////
+      g_Items_SaveState(st);
+      ///// /////
+
+      ///// Ñîñòîÿíèå òðèããåðîâ /////
+      g_Triggers_SaveState(st);
+      ///// /////
+
+      ///// Ñîñòîÿíèå îðóæèÿ /////
+      g_Weapon_SaveState(st);
+      ///// /////
+
+      ///// Ñîñòîÿíèå ìîíñòðîâ /////
+      g_Monsters_SaveState(st);
+      ///// /////
+
+      ///// Ñîñòîÿíèå òðóïîâ /////
+      g_Player_Corpses_SaveState(st);
+      ///// /////
+
+      ///// Ñîõðàíÿåì èãðîêîâ (â òîì ÷èñëå áîòîâ) /////
+      if (g_Player_GetCount > 0) then
+      begin
+        k := 0;
+        for i := 0 to High(gPlayers) do
         begin
-        // Ïîëó÷àåì ñîñòîÿíèå èãðîêà:
-          gPlayers[i].SaveState(bMem);
-        // Ñîõðàíÿåì ñîñòîÿíèå èãðîêà:
-          bFile.WriteMemory(bMem);
-          bMem.Free();
-          bMem := nil;
-          Inc(k);
+          if (gPlayers[i] <> nil) then
+          begin
+            // Ñîñòîÿíèå èãðîêà
+            gPlayers[i].SaveState(st);
+            Inc(k);
+          end;
         end;
 
-    // Âñå ëè èãðîêè íà ìåñòå:
-      if k <> nPlayers then
-      begin
-        raise EInOutError.Create('g_SaveGame: Wrong Players Count');
+        // Âñå ëè èãðîêè íà ìåñòå
+        if (k <> g_Player_GetCount) then raise XStreamError.Create('g_SaveGame: wrong players count');
       end;
+      ///// /////
+
+      ///// Ìàðêåð îêîí÷àíèÿ /////
+      utils.writeSign(st, 'END');
+      utils.writeInt(st, Byte(0));
+      ///// /////
+      result := true;
+    finally
+      st.Free();
     end;
-  ///// /////
-
-  ///// Ìàðêåð îêîí÷àíèÿ: /////
-    bMem := TBinMemoryWriter.Create(4);
-  // Ñòðîêà - îáîçíà÷åíèå êîíöà:
-    str := END_MARKER_STRING; // 'END'
-    bMem.WriteString(str, 3);
-  // Ñîõðàíÿåì ìàðêåð îêîí÷àíèÿ:
-    bFile.WriteMemory(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  // Çàêðûâàåì ôàéë ñîõðàíåíèÿ:
-    bFile.Close();
-    Result := True;
 
   except
-    on E1: EInOutError do
-      begin
-        g_Console_Add(_lc[I_GAME_ERROR_SAVE]);
-        e_WriteLog('SaveState I/O Error: '+E1.Message, MSG_WARNING);
-      end;
-    on E2: EBinSizeError do
+    on e: Exception do
       begin
+        st.Free();
         g_Console_Add(_lc[I_GAME_ERROR_SAVE]);
-        e_WriteLog('SaveState Size Error: '+E2.Message, MSG_WARNING);
+        e_WriteLog('SaveState Error: '+e.message, MSG_WARNING);
+        if deleteOnError then DeleteFile(filename);
+        {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
+        result := false;
       end;
   end;
-
-  bMem.Free();
-  bFile.Free();
 end;
 
-function g_LoadGame(n: Integer): Boolean;
+
+function g_LoadGameFrom (const filename: AnsiString): Boolean;
 var
-  bFile: TBinFileReader;
-  bMem: TBinMemoryReader;
-  sig: DWORD;
-  str, WAD_Path, Map_Name: String;
-  nPlayers, Game_Type, Game_Mode, Game_MaxLives: Byte;
+  st: TStream = nil;
+  WAD_Path, Map_Name: AnsiString;
+  nPlayers: Integer;
+  Game_Type, Game_Mode, Game_MaxLives: Byte;
   Game_TimeLimit, Game_GoalLimit: Word;
   Game_Time, Game_Options: Cardinal;
   Game_CoopMonstersKilled,
@@ -345,269 +294,226 @@ var
   i: Integer;
   gameCleared: Boolean = false;
   curmapfile: AnsiString = '';
+  errpos: LongWord = 0;
 begin
-  Result := False;
-  bMem := nil;
-  bFile := nil;
+  result := false;
 
   try
-  // Îòêðûâàåì ôàéë ñ ñîõðàíåíèåì:
-    bFile := TBinFileReader.Create();
-    if not bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT',
-                          SAVE_SIGNATURE, SAVE_VERSION) then
-    begin
-      bFile.Free();
-      Exit;
-    end;
+    st := openDiskFileRO(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');
+
+      e_WriteLog('Loading saved game...', MSG_NOTIFY);
+
+      try
+        //g_Game_Free(false); // don't free textures for the same map
+        g_Game_ClearLoading();
+        g_Game_SetLoadingText(_lc[I_LOAD_SAVE_FILE], 0, False);
+        gLoadGameMode := True;
+
+        ///// Çàãðóæàåì ñîñòîÿíèå èãðû /////
+        // Èìÿ ñýéâà
+        {str :=} utils.readStr(st, 64);
+
+        // Ïîëíûé ïóòü ê âàäó è êàðòà
+        curmapfile := utils.readStr(st);
+
+        if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('LOAD: previous map was ''%s''...', [gCurrentMapFileName]);
+        if (Length(curmapfile) <> 0) then e_LogWritefln('LOAD: new map is ''%s''...', [curmapfile]);
+        // À âîò òóò, íàêîíåö, ÷èñòèì ðåñóðñû
+        g_Game_Free(curmapfile <> gCurrentMapFileName); // don't free textures for the same map
+        gameCleared := true;
+
+        // Ïóòü ê êàðòå
+        WAD_Path := utils.readStr(st);
+        // Èìÿ êàðòû
+        Map_Name := utils.readStr(st);
+        // Êîëè÷åñòâî èãðîêîâ
+        nPlayers := utils.readWord(st);
+        // Èãðîâîå âðåìÿ
+        Game_Time := utils.readLongWord(st);
+        // Òèï èãðû
+        Game_Type := utils.readByte(st);
+        // Ðåæèì èãðû
+        Game_Mode := utils.readByte(st);
+        // Ëèìèò âðåìåíè
+        Game_TimeLimit := utils.readWord(st);
+        // Ëèìèò î÷êîâ
+        Game_GoalLimit := utils.readWord(st);
+        // Ëèìèò æèçíåé
+        Game_MaxLives := utils.readByte(st);
+        // Èãðîâûå îïöèè
+        Game_Options := utils.readLongWord(st);
+        // Äëÿ êîîïà
+        Game_CoopMonstersKilled := utils.readWord(st);
+        Game_CoopSecretsFound := utils.readWord(st);
+        Game_CoopTotalMonstersKilled := utils.readWord(st);
+        Game_CoopTotalSecretsFound := utils.readWord(st);
+        Game_CoopTotalMonsters := utils.readWord(st);
+        Game_CoopTotalSecrets := utils.readWord(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà /////
+        if not utils.checkSign(st, 'PLVW') then raise XStreamError.Create('invalid viewport signature');
+        if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid viewport version');
+        PID1 := utils.readWord(st);
+        PID2 := utils.readWord(st);
+        ///// /////
+
+        // Çàãðóæàåì êàðòó:
+        ZeroMemory(@gGameSettings, sizeof(TGameSettings));
+        gAimLine := false;
+        gShowMap := false;
+        if (Game_Type = GT_NONE) or (Game_Type = GT_SINGLE) then
+        begin
+          // Íàñòðîéêè èãðû
+          gGameSettings.GameType := GT_SINGLE;
+          gGameSettings.MaxLives := 0;
+          gGameSettings.Options := gGameSettings.Options+GAME_OPTION_ALLOWEXIT;
+          gGameSettings.Options := gGameSettings.Options+GAME_OPTION_MONSTERS;
+          gGameSettings.Options := gGameSettings.Options+GAME_OPTION_BOTVSMONSTER;
+          gSwitchGameMode := GM_SINGLE;
+        end
+        else
+        begin
+          // Íàñòðîéêè èãðû
+          gGameSettings.GameType := GT_CUSTOM;
+          gGameSettings.GameMode := Game_Mode;
+          gSwitchGameMode := Game_Mode;
+          gGameSettings.TimeLimit := Game_TimeLimit;
+          gGameSettings.GoalLimit := Game_GoalLimit;
+          gGameSettings.MaxLives := IfThen(Game_Mode = GM_CTF, 0, Game_MaxLives);
+          gGameSettings.Options := Game_Options;
+        end;
+        g_Game_ExecuteEvent('ongamestart');
 
-    e_WriteLog('Loading saved game...', MSG_NOTIFY);
-    //g_Game_Free(false); // don't free textures for the same map
-
-    g_Game_ClearLoading();
-    g_Game_SetLoadingText(_lc[I_LOAD_SAVE_FILE], 0, False);
-    gLoadGameMode := True;
-
-  ///// Çàãðóæàåì ñîñòîÿíèå èãðû: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Èìÿ èãðû:
-    bMem.ReadString(str);
-
-  // Ïîëíûé ïóòü ê âàäó è êàðòà
-    bMem.ReadString(curmapfile);
-
-    if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('LOAD: previous map was ''%s''...', [gCurrentMapFileName]);
-    if (Length(curmapfile) <> 0) then e_LogWritefln('LOAD: new map is ''%s''...', [curmapfile]);
-  // À âîò òóò, íàêîíåö, ÷èñòèì ðåñóðñû
-    g_Game_Free(curmapfile <> gCurrentMapFileName); // don't free textures for the same map
-    gameCleared := true;
-
-  // Ïóòü ê êàðòå:
-    bMem.ReadString(WAD_Path);
-  // Èìÿ êàðòû:
-    bMem.ReadString(Map_Name);
-  // Êîëè÷åñòâî èãðîêîâ:
-    bMem.ReadByte(nPlayers);
-  // Èãðîâîå âðåìÿ:
-    bMem.ReadDWORD(Game_Time);
-  // Òèï èãðû:
-    bMem.ReadByte(Game_Type);
-  // Ðåæèì èãðû:
-    bMem.ReadByte(Game_Mode);
-  // Ëèìèò âðåìåíè:
-    bMem.ReadWord(Game_TimeLimit);
-  // Ëèìèò î÷êîâ:
-    bMem.ReadWord(Game_GoalLimit);
-  // Ëèìèò æèçíåé:
-    bMem.ReadByte(Game_MaxLives);
-  // Èãðîâûå îïöèè:
-    bMem.ReadDWORD(Game_Options);
-  // Äëÿ êîîïà:
-    bMem.ReadWord(Game_CoopMonstersKilled);
-    bMem.ReadWord(Game_CoopSecretsFound);
-    bMem.ReadWord(Game_CoopTotalMonstersKilled);
-    bMem.ReadWord(Game_CoopTotalSecretsFound);
-    bMem.ReadWord(Game_CoopTotalMonsters);
-    bMem.ReadWord(Game_CoopTotalSecrets);
-  // Cîñòîÿíèå èãðû çàãðóæåíî:
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå îáëàñòåé ïðîñìîòðà: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-    bMem.ReadDWORD(sig);
-    if sig <> PLAYER_VIEW_SIGNATURE then // 'PLVW'
-    begin
-      raise EInOutError.Create('g_LoadGame: Wrong Player View Signature');
-    end;
-    bMem.ReadWord(PID1);
-    bMem.ReadWord(PID2);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  // Çàãðóæàåì êàðòó:
-    ZeroMemory(@gGameSettings, SizeOf(TGameSettings));
-    gAimLine := False;
-    gShowMap := False;
-    if (Game_Type = GT_NONE) or (Game_Type = GT_SINGLE) then
-    begin
-    // Íàñòðîéêè èãðû:
-      gGameSettings.GameType := GT_SINGLE;
-      gGameSettings.MaxLives := 0;
-      gGameSettings.Options := gGameSettings.Options + GAME_OPTION_ALLOWEXIT;
-      gGameSettings.Options := gGameSettings.Options + GAME_OPTION_MONSTERS;
-      gGameSettings.Options := gGameSettings.Options + GAME_OPTION_BOTVSMONSTER;
-      gSwitchGameMode := GM_SINGLE;
-    end
-    else
-    begin
-    // Íàñòðîéêè èãðû:
-      gGameSettings.GameType := GT_CUSTOM;
-      gGameSettings.GameMode := Game_Mode;
-      gSwitchGameMode := Game_Mode;
-      gGameSettings.TimeLimit := Game_TimeLimit;
-      gGameSettings.GoalLimit := Game_GoalLimit;
-      gGameSettings.MaxLives := IfThen(Game_Mode = GM_CTF, 0, Game_MaxLives);
-      gGameSettings.Options := Game_Options;
-    end;
-    g_Game_ExecuteEvent('ongamestart');
+        // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ
+        g_Game_SetupScreenSize();
 
-  // Óñòàíîâêà ðàçìåðîâ îêîí èãðîêîâ:
-    g_Game_SetupScreenSize();
+        // Çàãðóçêà è çàïóñê êàðòû
+        if not g_Game_StartMap(WAD_Path+':\'+Map_Name, True, curmapfile) then
+        begin
+          g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name]));
+          exit;
+        end;
 
-  // Çàãðóçêà è çàïóñê êàðòû:
-    if not g_Game_StartMap(WAD_Path + ':\' + Map_Name, True, curmapfile) then
-    begin
-      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name]));
-      Exit;
-    end;
+        // Íàñòðîéêè èãðîêîâ è áîòîâ
+        g_Player_Init();
+
+        // Óñòàíàâëèâàåì âðåìÿ
+        gTime := Game_Time;
+        // Âîçâðàùàåì ñòàòû
+        gCoopMonstersKilled := Game_CoopMonstersKilled;
+        gCoopSecretsFound := Game_CoopSecretsFound;
+        gCoopTotalMonstersKilled := Game_CoopTotalMonstersKilled;
+        gCoopTotalSecretsFound := Game_CoopTotalSecretsFound;
+        gCoopTotalMonsters := Game_CoopTotalMonsters;
+        gCoopTotalSecrets := Game_CoopTotalSecrets;
+
+        ///// Çàãðóæàåì ñîñòîÿíèå êàðòû /////
+        g_Map_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ /////
+        g_Items_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ /////
+        g_Triggers_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ /////
+        g_Weapon_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ /////
+        g_Monsters_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ /////
+        g_Player_Corpses_LoadState(st);
+        ///// /////
+
+        ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ) /////
+        if nPlayers > 0 then
+        begin
+          // Çàãðóæàåì
+          for i := 0 to nPlayers-1 do g_Player_CreateFromState(st);
+        end;
 
-  // Íàñòðîéêè èãðîêîâ è áîòîâ:
-    g_Player_Init();
-
-  // Óñòàíàâëèâàåì âðåìÿ:
-    gTime := Game_Time;
-  // Âîçâðàùàåì ñòàòû:
-    gCoopMonstersKilled := Game_CoopMonstersKilled;
-    gCoopSecretsFound := Game_CoopSecretsFound;
-    gCoopTotalMonstersKilled := Game_CoopTotalMonstersKilled;
-    gCoopTotalSecretsFound := Game_CoopTotalSecretsFound;
-    gCoopTotalMonsters := Game_CoopTotalMonsters;
-    gCoopTotalSecrets := Game_CoopTotalSecrets;
-
-  ///// Çàãðóæàåì ñîñòîÿíèå êàðòû: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå êàðòû:
-    g_Map_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå ïðåäìåòîâ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå ïðåäìåòîâ:
-    g_Items_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå òðèããåðîâ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå òðèããåðîâ:
-    g_Triggers_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå îðóæèÿ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå îðóæèÿ:
-    g_Weapon_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå ìîíñòðîâ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå ìîíñòðîâ:
-    g_Monsters_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì ñîñòîÿíèå òðóïîâ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñîñòîÿíèå òðóïîâ:
-    g_Player_Corpses_LoadState(bMem);
-    bMem.Free();
-    bMem := nil;
-  ///// /////
-
-  ///// Çàãðóæàåì èãðîêîâ (â òîì ÷èñëå áîòîâ): /////
-    if nPlayers > 0 then
-    begin
-    // Çàãðóæàåì:
-      for i := 0 to nPlayers-1 do
-      begin
-      // Çàãðóæàåì ñîñòîÿíèå èãðîêà:
-        bMem := TBinMemoryReader.Create();
-        bFile.ReadMemory(bMem);
-      // Ñîñòîÿíèå èãðîêà/áîòà:
-        g_Player_CreateFromState(bMem);
-        bMem.Free();
-        bMem := nil;
-      end;
-    end;
-  // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà:
-    gPlayer1 := g_Player_Get(PID1);
-    gPlayer2 := g_Player_Get(PID2);
-    if gPlayer1 <> nil then
-    begin
-      gPlayer1.Name := gPlayer1Settings.Name;
-      gPlayer1.FPreferredTeam := gPlayer1Settings.Team;
-      gPlayer1.FActualModelName := gPlayer1Settings.Model;
-      gPlayer1.SetModel(gPlayer1.FActualModelName);
-      gPlayer1.SetColor(gPlayer1Settings.Color);
-    end;
-    if gPlayer2 <> nil then
-    begin
-      gPlayer2.Name := gPlayer2Settings.Name;
-      gPlayer2.FPreferredTeam := gPlayer2Settings.Team;
-      gPlayer2.FActualModelName := gPlayer2Settings.Model;
-      gPlayer2.SetModel(gPlayer2.FActualModelName);
-      gPlayer2.SetColor(gPlayer2Settings.Color);
-    end;
-  ///// /////
-
-  ///// Ìàðêåð îêîí÷àíèÿ: /////
-    bMem := TBinMemoryReader.Create();
-    bFile.ReadMemory(bMem);
-  // Ñòðîêà - îáîçíà÷åíèå êîíöà:
-    bMem.ReadString(str);
-    if str <> END_MARKER_STRING then // 'END'
-    begin
-      raise EInOutError.Create('g_LoadGame: No END Marker');
-    end;
-  // Ìàðêåð îêîí÷àíèÿ çàãðóæåí:
-    bMem.Free();
-    bMem := nil;
-  ///// /////
+        // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà
+        gPlayer1 := g_Player_Get(PID1);
+        gPlayer2 := g_Player_Get(PID2);
+
+        if (gPlayer1 <> nil) then
+        begin
+          gPlayer1.Name := gPlayer1Settings.Name;
+          gPlayer1.FPreferredTeam := gPlayer1Settings.Team;
+          gPlayer1.FActualModelName := gPlayer1Settings.Model;
+          gPlayer1.SetModel(gPlayer1.FActualModelName);
+          gPlayer1.SetColor(gPlayer1Settings.Color);
+        end;
 
-  // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ:
-    if {(gMonsters <> nil) and} (gTriggers <> nil) then
-      g_Map_ReAdd_DieTriggers();
+        if (gPlayer2 <> nil) then
+        begin
+          gPlayer2.Name := gPlayer2Settings.Name;
+          gPlayer2.FPreferredTeam := gPlayer2Settings.Team;
+          gPlayer2.FActualModelName := gPlayer2Settings.Model;
+          gPlayer2.SetModel(gPlayer2.FActualModelName);
+          gPlayer2.SetColor(gPlayer2Settings.Color);
+        end;
+        ///// /////
 
-  // Çàêðûâàåì ôàéë çàãðóçêè:
-    bFile.Close();
-    gLoadGameMode := False;
-    Result := True;
+        ///// Ìàðêåð îêîí÷àíèÿ /////
+        if not utils.checkSign(st, 'END') then raise XStreamError.Create('no end marker');
+        if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid end marker');
+        ///// /////
 
-  except
-    on E1: EInOutError do
-      begin
-        g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
-        e_WriteLog('LoadState I/O Error: '+E1.Message, MSG_WARNING);
-        if not gameCleared then g_Game_Free();
+        // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ
+        if (gTriggers <> nil) then g_Map_ReAdd_DieTriggers();
+
+        // done
+        gLoadGameMode := false;
+        result := true;
+      except
+        begin
+          errpos := LongWord(st.position);
+          raise;
+        end;
       end;
-    on E2: EBinSizeError do
+    finally
+      st.Free();
+    end;
+  except
+    on e: Exception do
       begin
         g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
-        e_WriteLog('LoadState Size Error: '+E2.Message, MSG_WARNING);
+        e_WriteLog('LoadState Error: '+e.message, MSG_WARNING);
+        {$IF DEFINED(D2F_DEBUG)}e_LogWritefln('stream error position: 0x%08x', [errpos], MSG_WARNING);{$ENDIF}
+        gLoadGameMode := false;
+        result := true;
         if not gameCleared then g_Game_Free();
+        {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
       end;
   end;
+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;
+
 
-  bMem.Free();
-  bFile.Free();
+function g_LoadGame (n: Integer): Boolean;
+begin
+  result := false;
+  if (n < 0) or (n > 65535) then exit;
+  result := g_LoadGameFrom(buildSaveName(n));
 end;
 
+
 end.
index f18695861cd8007a217f07983c312d10c93af076..07ef3be06bfddebd2f592181cef2132b1352cd50 100644 (file)
@@ -19,8 +19,9 @@ unit g_textures;
 interface
 
 uses
+  SysUtils, Classes,
   mempool,
-  e_graphics, MAPDEF, BinEditor, ImagingTypes, Imaging, ImagingUtility;
+  e_graphics, MAPDEF, ImagingTypes, Imaging, ImagingUtility;
 
 Type
   TLevelTexture = record
@@ -63,8 +64,8 @@ Type
     procedure   Enable();
     procedure   Disable();
     procedure   Revert(r: Boolean);
-    procedure   SaveState(Var Mem: TBinMemoryWriter);
-    procedure   LoadState(Var Mem: TBinMemoryReader);
+    procedure   SaveState(st: TStream);
+    procedure   LoadState(st: TStream);
     function    TotalFrames(): Integer;
 
     property    Played: Boolean read FPlayed;
@@ -115,8 +116,8 @@ function g_Texture_Light(): Integer;
 implementation
 
 uses
-  g_game, e_log, g_basic, SysUtils, g_console, wadreader,
-  g_language, GL;
+  g_game, e_log, g_basic, g_console, wadreader,
+  g_language, GL, utils, xstreams;
 
 type
   _TTexture = record
@@ -821,71 +822,60 @@ begin
   Reset();
 end;
 
-procedure TAnimation.SaveState(Var Mem: TBinMemoryWriter);
-var
-  sig: DWORD;
-begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà àíèìàöèè:
-  sig := ANIM_SIGNATURE; // 'ANIM'
-  Mem.WriteDWORD(sig);
-// Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè:
-  Mem.WriteByte(FCounter);
-// Òåêóùèé êàäð:
-  Mem.WriteInt(FCurrentFrame);
-// Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì:
-  Mem.WriteBoolean(FPlayed);
-// Alpha-êàíàë âñåé òåêñòóðû:
-  Mem.WriteByte(FAlpha);
-// Ðàçìûòèå òåêñòóðû:
-  Mem.WriteBoolean(FBlending);
-// Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè:
-  Mem.WriteByte(FSpeed);
-// Çàöèêëåíà ëè àíèìàöèÿ:
-  Mem.WriteBoolean(FLoop);
-// Âêëþ÷åíà ëè:
-  Mem.WriteBoolean(FEnabled);
-// Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ:
-  Mem.WriteByte(FMinLength);
-// Îáðàòíûé ëè ïîðÿäîê êàäðîâ:
-  Mem.WriteBoolean(FRevert);
-end;
-
-procedure TAnimation.LoadState(Var Mem: TBinMemoryReader);
-var
-  sig: DWORD;
-begin
-  if Mem = nil then
-    Exit;
-
-// Ñèãíàòóðà àíèìàöèè:
-  Mem.ReadDWORD(sig);
-  if sig <> ANIM_SIGNATURE then // 'ANIM'
-  begin
-    raise EBinSizeError.Create('TAnimation.LoadState: Wrong Animation Signature');
-  end;
-// Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè:
-  Mem.ReadByte(FCounter);
-// Òåêóùèé êàäð:
-  Mem.ReadInt(FCurrentFrame);
-// Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì:
-  Mem.ReadBoolean(FPlayed);
-// Alpha-êàíàë âñåé òåêñòóðû:
-  Mem.ReadByte(FAlpha);
-// Ðàçìûòèå òåêñòóðû:
-  Mem.ReadBoolean(FBlending);
-// Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè:
-  Mem.ReadByte(FSpeed);
-// Çàöèêëåíà ëè àíèìàöèÿ:
-  Mem.ReadBoolean(FLoop);
-// Âêëþ÷åíà ëè:
-  Mem.ReadBoolean(FEnabled);
-// Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ:
-  Mem.ReadByte(FMinLength);
-// Îáðàòíûé ëè ïîðÿäîê êàäðîâ:
-  Mem.ReadBoolean(FRevert);
+procedure TAnimation.SaveState (st: TStream);
+begin
+  if (st = nil) then exit;
+
+  utils.writeSign(st, 'ANIM');
+  utils.writeInt(st, Byte(0)); // version
+  // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
+  utils.writeInt(st, Byte(FCounter));
+  // Òåêóùèé êàäð
+  utils.writeInt(st, LongInt(FCurrentFrame));
+  // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
+  utils.writeBool(st, FPlayed);
+  // Alpha-êàíàë âñåé òåêñòóðû
+  utils.writeInt(st, Byte(FAlpha));
+  // Ðàçìûòèå òåêñòóðû
+  utils.writeInt(st, Byte(FBlending));
+  // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
+  utils.writeInt(st, Byte(FSpeed));
+  // Çàöèêëåíà ëè àíèìàöèÿ
+  utils.writeBool(st, FLoop);
+  // Âêëþ÷åíà ëè
+  utils.writeBool(st, FEnabled);
+  // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
+  utils.writeInt(st, Byte(FMinLength));
+  // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
+  utils.writeBool(st, FRevert);
+end;
+
+procedure TAnimation.LoadState (st: TStream);
+begin
+  if (st = nil) then exit;
+
+  if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
+  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
+  // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
+  FCounter := utils.readByte(st);
+  // Òåêóùèé êàäð
+  FCurrentFrame := utils.readLongInt(st);
+  // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
+  FPlayed := utils.readBool(st);
+  // Alpha-êàíàë âñåé òåêñòóðû
+  FAlpha := utils.readByte(st);
+  // Ðàçìûòèå òåêñòóðû
+  FBlending := utils.readBool(st);
+  // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
+  FSpeed := utils.readByte(st);
+  // Çàöèêëåíà ëè àíèìàöèÿ
+  FLoop := utils.readBool(st);
+  // Âêëþ÷åíà ëè
+  FEnabled := utils.readBool(st);
+  // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
+  FMinLength := utils.readByte(st);
+  // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
+  FRevert := utils.readBool(st);
 end;
 
 
index 2aa9cfa7fb9b6a17c1cfe81ab4a50f47dcfed398..4aca319356ce70040c524a93dbd1681de2c85c57 100644 (file)
@@ -19,9 +19,9 @@ unit g_triggers;
 interface
 
 uses
-  Variants,
+  SysUtils, Variants, Classes,
   MAPDEF, e_graphics, g_basic, g_sound,
-  BinEditor, xdynrec, hashtable, exoma;
+  xdynrec, hashtable, exoma;
 
 type
   THashStrVariant = specialize THashBase<AnsiString, Variant>;
@@ -43,7 +43,7 @@ type
     ActivateType:     Byte;
     Keys:             Byte;
     TexturePanelGUID: Integer;
-    TexturePanelType: Word;
+    //TexturePanelType: Word;
 
     TimeOut:          Word;
     ActivateUID:      Word;
@@ -57,7 +57,7 @@ type
     AutoSpawn:        Boolean;
     SpawnCooldown:    Integer;
     SpawnedCount:     Integer;
-    ShotPanelType:    Word;
+    //ShotPanelType:    Word;
     ShotPanelTime:    Integer;
     ShotSightTime:    Integer;
     ShotSightTimeout: Integer;
@@ -91,8 +91,8 @@ procedure g_Triggers_PressC(CX, CY: Integer; Radius: Word; UID: Word; ActivateTy
 procedure g_Triggers_OpenAll();
 procedure g_Triggers_DecreaseSpawner(ID: DWORD);
 procedure g_Triggers_Free();
-procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Triggers_LoadState(var Mem: TBinMemoryReader);
+procedure g_Triggers_SaveState (st: TStream);
+procedure g_Triggers_LoadState (st: TStream);
 
 
 var
@@ -108,8 +108,8 @@ 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, SysUtils, e_log, g_language,
-  g_options, g_net, g_netmsg, utils, xparser;
+  wadreader, g_main, e_log, g_language,
+  g_options, g_net, g_netmsg, utils, xparser, xstreams;
 
 const
   TRIGGER_SIGNATURE = $58475254; // 'TRGX'
@@ -838,7 +838,7 @@ begin
     begin
       if (trigPanelGUID <> -1) and (ShotPanelTime = 0) then
       begin
-        g_Map_SwitchTextureGUID(ShotPanelType, trigPanelGUID);
+        g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
         ShotPanelTime := 4; // òèêîâ íà âñïûøêó âûñòðåëà
       end;
 
@@ -2310,7 +2310,7 @@ begin
 
   if Result {and (Trigger.TexturePanel <> -1)} then
   begin
-    g_Map_SwitchTextureGUID(Trigger.TexturePanelType, Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
+    g_Map_SwitchTextureGUID({Trigger.TexturePanelType,} Trigger.TexturePanelGUID, IfThen(animonce, 2, 1));
   end;
 end;
 
@@ -2678,7 +2678,7 @@ begin
           if ShotPanelTime > 0 then
           begin
             Dec(ShotPanelTime);
-            if ShotPanelTime = 0 then g_Map_SwitchTextureGUID(ShotPanelType, trigPanelGUID);
+            if ShotPanelTime = 0 then g_Map_SwitchTextureGUID({ShotPanelType,} trigPanelGUID);
           end;
           if ShotSightTime > 0 then
           begin
@@ -3109,123 +3109,120 @@ begin
 end;
 
 
-procedure g_Triggers_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Triggers_SaveState (st: TStream);
 var
   count, actCount, i, j: Integer;
-  dw: DWORD;
   sg: Single;
   b: Boolean;
   kv: THashStrVariant.PEntry;
-  //it: THashStrVariant.TKeyValEnumerator;
-  //uname: AnsiString;
   t: LongInt;
 begin
   // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ òðèããåðîâ
   count := Length(gTriggers);
-  Mem := TBinMemoryWriter.Create((count+1)*200);
 
   // Êîëè÷åñòâî òðèããåðîâ
-  Mem.WriteInt(count);
+  utils.writeInt(st, LongInt(count));
   if (count = 0) then exit;
 
   for i := 0 to High(gTriggers) do
   begin
     // Ñèãíàòóðà òðèããåðà
-    dw := TRIGGER_SIGNATURE; // 'TRGX'
-    Mem.WriteDWORD(dw);
+    utils.writeSign(st, 'TRGX');
+    utils.writeInt(st, Byte(0));
     // Òèï òðèããåðà
-    Mem.WriteByte(gTriggers[i].TriggerType);
+    utils.writeInt(st, Byte(gTriggers[i].TriggerType));
     if (gTriggers[i].TriggerType = TRIGGER_NONE) then continue; // empty one
     // Ñïåöèàëüíûå äàííûå òðèããåðà: ïîòîì èç êàðòû îïÿòü âûòàùèì; ñîõðàíèì òîëüêî èíäåêñ
-    Mem.WriteInt(gTriggers[i].mapIndex);
+    utils.writeInt(st, LongInt(gTriggers[i].mapIndex));
     // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
-    Mem.WriteInt(gTriggers[i].X);
-    Mem.WriteInt(gTriggers[i].Y);
+    utils.writeInt(st, LongInt(gTriggers[i].X));
+    utils.writeInt(st, LongInt(gTriggers[i].Y));
     // Ðàçìåðû
-    Mem.WriteWord(gTriggers[i].Width);
-    Mem.WriteWord(gTriggers[i].Height);
+    utils.writeInt(st, Word(gTriggers[i].Width));
+    utils.writeInt(st, Word(gTriggers[i].Height));
     // Âêëþ÷åí ëè òðèããåð
-    Mem.WriteBoolean(gTriggers[i].Enabled);
+    utils.writeBool(st, gTriggers[i].Enabled);
     // Òèï àêòèâàöèè òðèããåðà
-    Mem.WriteByte(gTriggers[i].ActivateType);
+    utils.writeInt(st, Byte(gTriggers[i].ActivateType));
     // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
-    Mem.WriteByte(gTriggers[i].Keys);
+    utils.writeInt(st, Byte(gTriggers[i].Keys));
     // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
-    Mem.WriteInt(gTriggers[i].TexturePanelGUID);
+    utils.writeInt(st, LongInt(gTriggers[i].TexturePanelGUID));
     // Òèï ýòîé ïàíåëè
-    Mem.WriteWord(gTriggers[i].TexturePanelType);
+    //Mem.WriteWord(gTriggers[i].TexturePanelType);
     // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
-    Mem.WriteInt(gTriggers[i].trigPanelGUID);
+    utils.writeInt(st, LongInt(gTriggers[i].trigPanelGUID));
     // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
-    Mem.WriteWord(gTriggers[i].TimeOut);
+    utils.writeInt(st, Word(gTriggers[i].TimeOut));
     // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
-    Mem.WriteWord(gTriggers[i].ActivateUID);
+    utils.writeInt(st, Word(gTriggers[i].ActivateUID));
     // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
     actCount := Length(gTriggers[i].Activators);
-    Mem.WriteInt(actCount);
+    utils.writeInt(st, LongInt(actCount));
     for j := 0 to actCount-1 do
     begin
       // UID îáúåêòà
-      Mem.WriteWord(gTriggers[i].Activators[j].UID);
+      utils.writeInt(st, Word(gTriggers[i].Activators[j].UID));
       // Âðåìÿ îæèäàíèÿ
-      Mem.WriteWord(gTriggers[i].Activators[j].TimeOut);
+      utils.writeInt(st, Word(gTriggers[i].Activators[j].TimeOut));
     end;
     // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
-    Mem.WriteBoolean(gTriggers[i].PlayerCollide);
+    utils.writeBool(st, gTriggers[i].PlayerCollide);
     // Âðåìÿ äî çàêðûòèÿ äâåðè
-    Mem.WriteInt(gTriggers[i].DoorTime);
+    utils.writeInt(st, LongInt(gTriggers[i].DoorTime));
     // Çàäåðæêà àêòèâàöèè
-    Mem.WriteInt(gTriggers[i].PressTime);
+    utils.writeInt(st, LongInt(gTriggers[i].PressTime));
     // Ñ÷åò÷èê íàæàòèé
-    Mem.WriteInt(gTriggers[i].PressCount);
+    utils.writeInt(st, LongInt(gTriggers[i].PressCount));
     // Ñïàâíåð àêòèâåí
-    Mem.WriteBoolean(gTriggers[i].AutoSpawn);
+    utils.writeBool(st, gTriggers[i].AutoSpawn);
     // Çàäåðæêà ñïàâíåðà
-    Mem.WriteInt(gTriggers[i].SpawnCooldown);
+    utils.writeInt(st, LongInt(gTriggers[i].SpawnCooldown));
     // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
-    Mem.WriteInt(gTriggers[i].SpawnedCount);
+    utils.writeInt(st, LongInt(gTriggers[i].SpawnedCount));
     // Ñêîëüêî ðàç ïðîèãðàí çâóê
-    Mem.WriteInt(gTriggers[i].SoundPlayCount);
+    utils.writeInt(st, LongInt(gTriggers[i].SoundPlayCount));
     // Ïðîèãðûâàåòñÿ ëè çâóê?
     if (gTriggers[i].Sound <> nil) then b := gTriggers[i].Sound.IsPlaying() else b := false;
-    Mem.WriteBoolean(b);
+    utils.writeBool(st, b);
     if b then
     begin
       // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
-      dw := gTriggers[i].Sound.GetPosition();
-      Mem.WriteDWORD(dw);
+      utils.writeInt(st, LongWord(gTriggers[i].Sound.GetPosition()));
       // Ãðîìêîñòü çâóêà
       sg := gTriggers[i].Sound.GetVolume();
-      sg := sg / (gSoundLevel/255.0);
-      Mem.WriteSingle(sg);
+      sg := sg/(gSoundLevel/255.0);
+      //Mem.WriteSingle(sg);
+      st.WriteBuffer(sg, sizeof(sg)); // sorry
       // Ñòåðåî ñìåùåíèå çâóêà
       sg := gTriggers[i].Sound.GetPan();
-      Mem.WriteSingle(sg);
+      //Mem.WriteSingle(sg);
+      st.WriteBuffer(sg, sizeof(sg)); // sorry
     end;
     // uservars
     if (gTriggers[i].userVars = nil) then
     begin
-      Mem.WriteInt(0);
+      utils.writeInt(st, LongInt(0));
     end
     else
     begin
-      Mem.WriteInt(gTriggers[i].userVars.count);
+      utils.writeInt(st, LongInt(gTriggers[i].userVars.count)); //FIXME: check for overflow
       for kv in gTriggers[i].userVars.byKeyValue do
       begin
         //writeln('<', kv.key, '>:<', VarToStr(kv.value), '>');
-        Mem.WriteString(kv.key);
+        utils.writeStr(st, kv.key);
         t := LongInt(varType(kv.value));
-        Mem.WriteInt(t);
+        utils.writeInt(st, LongInt(t));
         case t of
-          varString: Mem.WriteString(AnsiString(kv.value));
-          varBoolean: Mem.WriteBoolean(Boolean(kv.value));
-          varShortInt: Mem.WriteInt(Integer(kv.value));
-          varSmallint: Mem.WriteInt(Integer(kv.value));
-          varInteger: Mem.WriteInt(Integer(kv.value));
+          varString: utils.writeStr(st, AnsiString(kv.value));
+          varBoolean: utils.writeBool(st, Boolean(kv.value));
+          varShortInt: utils.writeInt(st, LongInt(kv.value));
+          varSmallint: utils.writeInt(st, LongInt(kv.value));
+          varInteger: utils.writeInt(st, LongInt(kv.value));
           //varInt64: Mem.WriteInt(Integer(kv.value));
-          varByte: Mem.WriteInt(Integer(kv.value));
-          varWord: Mem.WriteInt(Integer(kv.value));
-          varLongWord: Mem.WriteInt(Integer(kv.value));
+          varByte: utils.writeInt(st, LongInt(kv.value));
+          varWord: utils.writeInt(st, LongInt(kv.value));
+          varLongWord: utils.writeInt(st, LongInt(kv.value));
           //varQWord:
           else raise Exception.CreateFmt('cannot save uservar ''%s''', [kv.key]);
         end;
@@ -3235,7 +3232,7 @@ begin
 end;
 
 
-procedure g_Triggers_LoadState (var Mem: TBinMemoryReader);
+procedure g_Triggers_LoadState (st: TStream);
 var
   count, actCount, i, j, a: Integer;
   dw: DWORD;
@@ -3246,93 +3243,97 @@ var
   uvcount: Integer;
   vt: LongInt;
   vv: Variant;
-  uvname: AnsiString;
-  ustr: AnsiString;
+  uvname: AnsiString = '';
+  ustr: AnsiString = '';
   uint: LongInt;
   ubool: Boolean;
 begin
-  if (Mem = nil) then exit;
+  assert(st <> nil);
 
   g_Triggers_Free();
 
   // Êîëè÷åñòâî òðèããåðîâ
-  Mem.ReadInt(count);
+  count := utils.readLongInt(st);
   if (count = 0) then exit;
+  if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid trigger count');
 
   for a := 0 to count-1 do
   begin
     // Ñèãíàòóðà òðèããåðà
-    Mem.ReadDWORD(dw); // 'TRGX'
-    if (dw <> TRIGGER_SIGNATURE) then raise EBinSizeError.Create('g_Triggers_LoadState: Wrong Trigger Signature');
+    if not utils.checkSign(st, 'TRGX') then raise XStreamError.Create('invalid trigger signature');
+    if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid trigger version');
     // Òèï òðèããåðà
-    Mem.ReadByte(Trig.TriggerType);
-    // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
+    Trig.TriggerType := utils.readByte(st);
     if (Trig.TriggerType = TRIGGER_NONE) then continue; // empty one
-    Mem.ReadInt(mapIndex);
+    // Ñïåöèàëüíûå äàííûå òðèããåðà: èíäåêñ â gCurrentMap.field['triggers']
+    mapIndex := utils.readLongInt(st);
     i := g_Triggers_CreateWithMapIndex(Trig, a, mapIndex);
-  // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà:
-    Mem.ReadInt(gTriggers[i].X);
-    Mem.ReadInt(gTriggers[i].Y);
-  // Ðàçìåðû:
-    Mem.ReadWord(gTriggers[i].Width);
-    Mem.ReadWord(gTriggers[i].Height);
-  // Âêëþ÷åí ëè òðèããåð:
-    Mem.ReadBoolean(gTriggers[i].Enabled);
-  // Òèï àêòèâàöèè òðèããåðà:
-    Mem.ReadByte(gTriggers[i].ActivateType);
-  // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè:
-    Mem.ReadByte(gTriggers[i].Keys);
-  // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ:
-    Mem.ReadInt(gTriggers[i].TexturePanelGUID);
-  // Òèï ýòîé ïàíåëè:
-    Mem.ReadWord(gTriggers[i].TexturePanelType);
-  // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
-    Mem.ReadInt(gTriggers[i].trigPanelGUID);
-  // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè:
-    Mem.ReadWord(gTriggers[i].TimeOut);
-  // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð:
-    Mem.ReadWord(gTriggers[i].ActivateUID);
-  // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì:
-    Mem.ReadInt(actCount);
-    if actCount > 0 then
+    // Êîîðäèíàòû ëåâîãî âåðõíåãî óãëà
+    gTriggers[i].X := utils.readLongInt(st);
+    gTriggers[i].Y := utils.readLongInt(st);
+    // Ðàçìåðû
+    gTriggers[i].Width := utils.readWord(st);
+    gTriggers[i].Height := utils.readWord(st);
+    // Âêëþ÷åí ëè òðèããåð
+    gTriggers[i].Enabled := utils.readBool(st);
+    // Òèï àêòèâàöèè òðèããåðà
+    gTriggers[i].ActivateType := utils.readByte(st);
+    // Êëþ÷è, íåîáõîäèìûå äëÿ àêòèâàöèè
+    gTriggers[i].Keys := utils.readByte(st);
+    // ID ïàíåëè, òåêñòóðà êîòîðîé èçìåíèòñÿ
+    gTriggers[i].TexturePanelGUID := utils.readLongInt(st);
+    // Òèï ýòîé ïàíåëè
+    //Mem.ReadWord(gTriggers[i].TexturePanelType);
+    // Âíóòðåííèé íîìåð äðóãîé ïàíåëè (ïî ñ÷àñòëèâîé ñëó÷àéíîñòè îí áóäåò ñîâïàäàòü ñ òåì, ÷òî ñîçäàíî ïðè çàãðóçêå êàðòû)
+    gTriggers[i].trigPanelGUID := utils.readLongInt(st);
+    // Âðåìÿ äî âîçìîæíîñòè àêòèâàöèè
+    gTriggers[i].TimeOut := utils.readWord(st);
+    // UID òîãî, êòî àêòèâèðîâàë ýòîò òðèããåð
+    gTriggers[i].ActivateUID := utils.readWord(st);
+    // Ñïèñîê UID-îâ îáúåêòîâ, êîòîðûå íàõîäèëèñü ïîä âîçäåéñòâèåì
+    actCount := utils.readLongInt(st);
+    if (actCount < 0) or (actCount > 1024*1024) then raise XStreamError.Create('invalid activated object count');
+    if (actCount > 0) then
     begin
       SetLength(gTriggers[i].Activators, actCount);
       for j := 0 to actCount-1 do
       begin
         // UID îáúåêòà
-        Mem.ReadWord(gTriggers[i].Activators[j].UID);
+        gTriggers[i].Activators[j].UID := utils.readWord(st);
         // Âðåìÿ îæèäàíèÿ
-        Mem.ReadWord(gTriggers[i].Activators[j].TimeOut);
+        gTriggers[i].Activators[j].TimeOut := utils.readWord(st);
       end;
     end;
-  // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà:
-    Mem.ReadBoolean(gTriggers[i].PlayerCollide);
-  // Âðåìÿ äî çàêðûòèÿ äâåðè:
-    Mem.ReadInt(gTriggers[i].DoorTime);
-  // Çàäåðæêà àêòèâàöèè:
-    Mem.ReadInt(gTriggers[i].PressTime);
-  // Ñ÷åò÷èê íàæàòèé:
-    Mem.ReadInt(gTriggers[i].PressCount);
-  // Ñïàâíåð àêòèâåí:
-    Mem.ReadBoolean(gTriggers[i].AutoSpawn);
-  // Çàäåðæêà ñïàâíåðà:
-    Mem.ReadInt(gTriggers[i].SpawnCooldown);
-  // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ:
-    Mem.ReadInt(gTriggers[i].SpawnedCount);
-  // Ñêîëüêî ðàç ïðîèãðàí çâóê:
-    Mem.ReadInt(gTriggers[i].SoundPlayCount);
-  // Ïðîèãðûâàåòñÿ ëè çâóê?
-    Mem.ReadBoolean(b);
+    // Ñòîèò ëè èãðîê â îáëàñòè òðèããåðà
+    gTriggers[i].PlayerCollide := utils.readBool(st);
+    // Âðåìÿ äî çàêðûòèÿ äâåðè
+    gTriggers[i].DoorTime := utils.readLongInt(st);
+    // Çàäåðæêà àêòèâàöèè
+    gTriggers[i].PressTime := utils.readLongInt(st);
+    // Ñ÷åò÷èê íàæàòèé
+    gTriggers[i].PressCount := utils.readLongInt(st);
+    // Ñïàâíåð àêòèâåí
+    gTriggers[i].AutoSpawn := utils.readBool(st);
+    // Çàäåðæêà ñïàâíåðà
+    gTriggers[i].SpawnCooldown := utils.readLongInt(st);
+    // Ñ÷åò÷èê ñîçäàíèÿ îáúåêòîâ
+    gTriggers[i].SpawnedCount := utils.readLongInt(st);
+    // Ñêîëüêî ðàç ïðîèãðàí çâóê
+    gTriggers[i].SoundPlayCount := utils.readLongInt(st);
+    // Ïðîèãðûâàåòñÿ ëè çâóê?
+    b := utils.readBool(st);
     if b then
     begin
-    // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà:
-      Mem.ReadDWORD(dw);
-    // Ãðîìêîñòü çâóêà:
-      Mem.ReadSingle(vol);
-    // Ñòåðåî ñìåùåíèå çâóêà:
-      Mem.ReadSingle(pan);
-    // Çàïóñêàåì çâóê, åñëè åñòü:
-      if gTriggers[i].Sound <> nil then
+      // Ïîçèöèÿ ïðîèãðûâàíèÿ çâóêà
+      dw := utils.readLongWord(st);
+      // Ãðîìêîñòü çâóêà
+      //Mem.ReadSingle(vol);
+      st.ReadBuffer(vol, sizeof(vol)); // sorry
+      // Ñòåðåî ñìåùåíèå çâóêà
+      //Mem.ReadSingle(pan);
+      st.ReadBuffer(pan, sizeof(pan)); // sorry
+      // Çàïóñêàåì çâóê, åñëè åñòü
+      if (gTriggers[i].Sound <> nil) then
       begin
         gTriggers[i].Sound.PlayPanVolume(pan, vol);
         gTriggers[i].Sound.Pause(True);
@@ -3342,7 +3343,8 @@ begin
     // uservars
     gTriggers[i].userVars.Free();
     gTriggers[i].userVars := nil;
-    Mem.ReadInt(uvcount);
+    uvcount := utils.readLongInt(st);
+    if (uvcount < 0) or (uvcount > 1024*1024) then raise XStreamError.Create('invalid number of user vars in trigger');
     if (uvcount > 0) then
     begin
       gTriggers[i].userVars := THashStrVariant.Create(hsihash, hsiequ);
@@ -3350,18 +3352,17 @@ begin
       while (uvcount > 0) do
       begin
         Dec(uvcount);
-        uvname := '';
-        Mem.ReadString(uvname);
-        Mem.ReadInt(vt);
+        uvname := utils.readStr(st);
+        vt := utils.readLongInt(st);
         case vt of
-          varString: begin ustr := ''; Mem.ReadString(ustr); vv := ustr; end;
-          varBoolean: begin Mem.ReadBoolean(ubool); vv := ubool; end;
-          varShortInt: begin Mem.ReadInt(uint); vv := ShortInt(uint); end;
-          varSmallint: begin Mem.ReadInt(uint); vv := SmallInt(uint); end;
-          varInteger: begin Mem.ReadInt(uint); vv := LongInt(uint); end;
-          varByte: begin Mem.ReadInt(uint); vv := Byte(uint); end;
-          varWord: begin Mem.ReadInt(uint); vv := Word(uint); end;
-          varLongWord: begin Mem.ReadInt(uint); vv := LongWord(uint); end;
+          varString: begin ustr := utils.readStr(st); vv := ustr; end;
+          varBoolean: begin ubool := utils.readBool(st); vv := ubool; end;
+          varShortInt: begin uint := utils.readLongInt(st); vv := ShortInt(uint); end;
+          varSmallint: begin uint := utils.readLongInt(st); vv := SmallInt(uint); end;
+          varInteger: begin uint := utils.readLongInt(st); vv := LongInt(uint); end;
+          varByte: begin uint := utils.readLongInt(st); vv := Byte(uint); end;
+          varWord: begin uint := utils.readLongInt(st); vv := Word(uint); end;
+          varLongWord: begin uint := utils.readLongInt(st); vv := LongWord(uint); end;
           else raise Exception.CreateFmt('cannot load uservar ''%s''', [uvname]);
         end;
         gTriggers[i].userVars.put(uvname, vv);
index 4a22a3b4a827a2a93c9079348a6fafaee9321734..182207f233740454ac355b5034c59625b78aea0b 100644 (file)
@@ -20,22 +20,9 @@ unit g_weapons;
 interface
 
 uses
-  g_textures, g_basic, e_graphics, g_phys, BinEditor, xprofiler;
+  SysUtils, Classes,
+  g_textures, g_basic, e_graphics, g_phys, xprofiler;
 
-{
-const
-  HIT_SOME    = 0;
-  HIT_ROCKET  = 1;
-  HIT_BFG     = 2;
-  HIT_TRAP    = 3;
-  HIT_FALL    = 4;
-  HIT_WATER   = 5;
-  HIT_ACID    = 6;
-  HIT_ELECTRO = 7;
-  HIT_FLAME   = 8;
-  HIT_SELF    = 9;
-  HIT_DISCON  = 10;
-}
 
 type
   TShot = record
@@ -91,8 +78,8 @@ procedure g_Weapon_Draw();
 function g_Weapon_Danger(UID: Word; X, Y: Integer; Width, Height: Word; Time: Byte): Boolean;
 procedure g_Weapon_DestroyShot(I: Integer; X, Y: Integer; Loud: Boolean = True);
 
-procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
-procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
+procedure g_Weapon_SaveState (st: TStream);
+procedure g_Weapon_LoadState (st: TStream);
 
 procedure g_Weapon_AddDynLights();
 
@@ -128,10 +115,10 @@ implementation
 
 uses
   Math, g_map, g_player, g_gfx, g_sound, g_main, g_panel,
-  g_console, SysUtils, g_options, g_game,
+  g_console, g_options, g_game,
   g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
   g_language, g_netmsg, g_grid,
-  binheap, hashtable;
+  binheap, hashtable, utils, xstreams;
 
 type
   TWaterPanel = record
@@ -2570,93 +2557,82 @@ begin
         end;
 end;
 
-procedure g_Weapon_SaveState(var Mem: TBinMemoryWriter);
+procedure g_Weapon_SaveState (st: TStream);
 var
   count, i, j: Integer;
-  dw: DWORD;
 begin
-// Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ:
+  // Ñ÷èòàåì êîëè÷åñòâî ñóùåñòâóþùèõ ñíàðÿäîâ
   count := 0;
-  if Shots <> nil then
-    for i := 0 to High(Shots) do
-      if Shots[i].ShotType <> 0 then
-        count := count + 1;
+  for i := 0 to High(Shots) do if (Shots[i].ShotType <> 0) then Inc(count);
 
-  Mem := TBinMemoryWriter.Create((count+1) * 80);
+  // Êîëè÷åñòâî ñíàðÿäîâ
+  utils.WriteInt(st, count);
 
-// Êîëè÷åñòâî ñíàðÿäîâ:
-  Mem.WriteInt(count);
-
-  if count = 0 then
-    Exit;
+  if (count = 0) then exit;
 
   for i := 0 to High(Shots) do
+  begin
     if Shots[i].ShotType <> 0 then
     begin
-    // Ñèãíàòóðà ñíàðÿäà:
-      dw := SHOT_SIGNATURE; // 'SHOT'
-      Mem.WriteDWORD(dw);
-    // Òèï ñíàðÿäà:
-      Mem.WriteByte(Shots[i].ShotType);
-    // Öåëü:
-      Mem.WriteWord(Shots[i].Target);
-    // UID ñòðåëÿâøåãî:
-      Mem.WriteWord(Shots[i].SpawnerUID);
-    // Ðàçìåð ïîëÿ Triggers:
-      dw := Length(Shots[i].Triggers);
-      Mem.WriteDWORD(dw);
-    // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
-      for j := 0 to Integer(dw)-1 do
-        Mem.WriteDWORD(Shots[i].Triggers[j]);
-    // Îáúåêò ñíàðÿäà:
-      Obj_SaveState(@Shots[i].Obj, Mem);
-    // Êîñòûëèíà åáàíàÿ:
-      Mem.WriteByte(Shots[i].Stopped);
+      // Ñèãíàòóðà ñíàðÿäà
+      utils.writeSign(st, 'SHOT');
+      utils.writeInt(st, Byte(0)); // version
+      // Òèï ñíàðÿäà
+      utils.writeInt(st, Byte(Shots[i].ShotType));
+      // Öåëü
+      utils.writeInt(st, Word(Shots[i].Target));
+      // UID ñòðåëÿâøåãî
+      utils.writeInt(st, Word(Shots[i].SpawnerUID));
+      // Ðàçìåð ïîëÿ Triggers
+      utils.writeInt(st, Integer(Length(Shots[i].Triggers)));
+      // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
+      for j := 0 to Length(Shots[i].Triggers)-1 do utils.writeInt(st, LongWord(Shots[i].Triggers[j]));
+      // Îáúåêò ñíàðÿäà
+      Obj_SaveState(st, @Shots[i].Obj);
+      // Êîñòûëèíà åáàíàÿ
+      utils.writeInt(st, Byte(Shots[i].Stopped));
     end;
+  end;
 end;
 
-procedure g_Weapon_LoadState(var Mem: TBinMemoryReader);
+procedure g_Weapon_LoadState (st: TStream);
 var
-  count, i, j: Integer;
-  dw: DWORD;
+  count, tc, i, j: Integer;
+  dw: LongWord;
 begin
-  if Mem = nil then
-    Exit;
+  if (st = nil) then exit;
 
-// Êîëè÷åñòâî ñíàðÿäîâ:
-  Mem.ReadInt(count);
+  // Êîëè÷åñòâî ñíàðÿäîâ
+  count := utils.readLongInt(st);
+  if (count < 0) or (count > 1024*1024) then raise XStreamError.Create('invalid shots counter');
 
   SetLength(Shots, count);
 
-  if count = 0 then
-    Exit;
+  if (count = 0) then exit;
 
   for i := 0 to count-1 do
   begin
-  // Ñèãíàòóðà ñíàðÿäà:
-    Mem.ReadDWORD(dw);
-    if dw <> SHOT_SIGNATURE then // 'SHOT'
-    begin
-      raise EBinSizeError.Create('g_Weapons_LoadState: Wrong Shot Signature');
-    end;
-  // Òèï ñíàðÿäà:
-    Mem.ReadByte(Shots[i].ShotType);
-  // Öåëü:
-    Mem.ReadWord(Shots[i].Target);
-  // UID ñòðåëÿâøåãî:
-    Mem.ReadWord(Shots[i].SpawnerUID);
-  // Ðàçìåð ïîëÿ Triggers:
-    Mem.ReadDWORD(dw);
-    SetLength(Shots[i].Triggers, dw);
-  // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì:
-    for j := 0 to Integer(dw)-1 do
-      Mem.ReadDWORD(Shots[i].Triggers[j]);
-  // Îáúåêò ïðåäìåòà:
-    Obj_LoadState(@Shots[i].Obj, Mem);
-  // Êîñòûëèíà åáàíàÿ:
-    Mem.ReadByte(Shots[i].Stopped);
-
-  // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè:
+    // Ñèãíàòóðà ñíàðÿäà
+    if not utils.checkSign(st, 'SHOT') then raise XStreamError.Create('invalid shot signature');
+    if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid shot version');
+    // Òèï ñíàðÿäà:
+    Shots[i].ShotType := utils.readByte(st);
+    // Öåëü
+    Shots[i].Target := utils.readWord(st);
+    // UID ñòðåëÿâøåãî
+    Shots[i].SpawnerUID := utils.readWord(st);
+    // Ðàçìåð ïîëÿ Triggers
+    tc := utils.readLongInt(st);
+    if (tc < 0) or (tc > 1024*1024) then raise XStreamError.Create('invalid shot triggers counter');
+    SetLength(Shots[i].Triggers, tc);
+    // Òðèããåðû, àêòèâèðîâàííûå âûñòðåëîì
+    for j := 0 to tc-1 do Shots[i].Triggers[j] := utils.readLongWord(st);
+    // Îáúåêò ïðåäìåòà
+    Obj_LoadState(@Shots[i].Obj, st);
+    // Êîñòûëèíà åáàíàÿ
+    Shots[i].Stopped := utils.readByte(st);
+
+    // Óñòàíîâêà òåêñòóðû èëè àíèìàöèè
     Shots[i].TextureID := DWORD(-1);
     Shots[i].Animation := nil;
 
diff --git a/src/shared/BinEditor.pas b/src/shared/BinEditor.pas
deleted file mode 100644 (file)
index 62fe9bf..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-(* 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, either version 3 of the License, or
- * (at your option) any later version.
- *
- * 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/>.
- *)
-{$INCLUDE a_modes.inc}
-unit BinEditor;
-
-interface
-
-Uses
-  SysUtils, Classes;
-
-type
-  EBinSizeError = class(Exception);
-
-  TBinMemoryWriter = class
-  private
-    FSize: LongWord;
-    FData: Pointer;
-    FPosition: LongWord;
-
-    procedure WriteVar (var x; varSize: LongWord);
-    procedure ExtendMemory (addLen: LongWord);
-
-  public
-    constructor Create (aSize: LongWord);
-    destructor Destroy (); override;
-
-    procedure WriteByte (x: Byte);
-    procedure WriteWord (x: Word);
-    procedure WriteDWORD (x: LongWord);
-    procedure WriteShortInt (x: ShortInt);
-    procedure WriteSmallInt (x: SmallInt);
-    procedure WriteInt (x: LongInt);
-    procedure WriteSingle (x: Single);
-    procedure WriteBoolean (x: Boolean);
-    procedure WriteString (const x: AnsiString; aMaxLen: Word=65535);
-    procedure WriteMemory (x: Pointer; memSize: LongWord);
-    procedure Fill (aLen: LongWord; aFillSym: Byte);
-    procedure SaveToFile (st: TStream);
-    procedure SaveToMemory (aMem: TBinMemoryWriter);
-  end;
-
-  TBinMemoryReader = class
-  private
-    FSize: LongWord;
-    FData: Pointer;
-    FPosition: LongWord;
-
-    procedure ReadVar (var x; varSize: LongWord);
-
-  public
-    constructor Create ();
-    destructor  Destroy (); override;
-    procedure ReadByte (var x: Byte);
-    procedure ReadWord (var x: Word);
-    procedure ReadDWORD (var x: LongWord);
-    procedure ReadShortInt (var x: ShortInt);
-    procedure ReadSmallInt (var x: SmallInt);
-    procedure ReadInt (var x: LongInt);
-    procedure ReadSingle (var x: Single);
-    procedure ReadBoolean (var x: Boolean);
-    procedure ReadString (var x: AnsiString);
-    procedure ReadMemory (var x: Pointer; var memSize: LongWord);
-    procedure Skip (aLen: LongWord);
-    procedure LoadFromFile (st: TStream);
-    procedure LoadFromMemory (aMem: TBinMemoryReader);
-  end;
-
-  TBinFileWriter = class
-  private
-    FHandle: TStream;
-
-  public
-    constructor Create ();
-    destructor Destroy (); override;
-    procedure OpenFile (const aFileName: AnsiString; aFileSig: LongWord;
-                        aFileVer: Byte; aOverWrite: Boolean=true);
-    procedure Close ();
-    procedure WriteMemory (aMemory: TBinMemoryWriter);
-  end;
-
-  TBinFileReader = class
-  private
-    FHandle: TStream;
-
-  public
-    constructor Create ();
-    destructor Destroy (); override;
-    function OpenFile (const aFileName: AnsiString; aFileSig: LongWord; aFileVer: Byte): Boolean;
-    procedure Close ();
-    procedure ReadMemory (aMemory: TBinMemoryReader);
-  end;
-
-procedure FillMemory (Dest: Pointer; Len: LongWord; Ch: Byte); inline;
-procedure CopyMemory (Dest: Pointer; Src: Pointer; Len: LongWord); inline;
-procedure ZeroMemory (Dest: Pointer; Len: LongWord); inline;
-
-
-implementation
-
-uses
-  Math, e_log, utils;
-
-const
-  MAX_BIN_SIZE = 42*1024*1024; // 42 MB
-
-
-procedure CopyMemory (Dest: Pointer; Src: Pointer; Len: LongWord); inline;
-begin
-  Move(Src^, Dest^, Len);
-end;
-
-procedure FillMemory (Dest: Pointer; Len: LongWord; Ch: Byte); inline;
-begin
-  FillChar(Dest^, Len, Ch);
-end;
-
-procedure ZeroMemory (Dest: Pointer; Len: LongWord); inline;
-begin
-  FillChar(Dest^, Len, 0);
-end;
-
-
-{ T B i n M e m o r y W r i t e r : }
-
-constructor TBinMemoryWriter.Create (aSize: LongWord);
-begin
-  if (aSize <= 0) then FSize := 1 else FSize := aSize;
-  if (FSize > MAX_BIN_SIZE) then FSize := MAX_BIN_SIZE;
-  GetMem(FData, FSize);
-  FPosition := 0;
-end;
-
-destructor TBinMemoryWriter.Destroy ();
-begin
-  if (FData <> nil) then
-  begin
-    FreeMem(FData);
-    FData := nil;
-  end;
-  inherited;
-end;
-
-procedure TBinMemoryWriter.WriteVar (var x; varSize: LongWord);
-begin
-  if (varSize > 0) then
-  begin
-    if (FPosition+varSize > FSize) then ExtendMemory(varSize);
-    CopyMemory(Pointer(PtrUInt(FData)+FPosition), @x, varSize);
-    FPosition := FPosition+varSize;
-  end;
-end;
-
-procedure TBinMemoryWriter.ExtendMemory (addLen: LongWord);
-var
-  tmp: Pointer;
-begin
-  while (FPosition+addLen > FSize) and (FSize <= MAX_BIN_SIZE) do FSize := FSize*2;
-
-  if (FSize > MAX_BIN_SIZE) then raise EBinSizeError.Create('TBinMemoryWriter.ExtendMemory: Tried to allocete more than 42 MB');
-
-  GetMem(tmp, FSize);
-
-  if (FPosition > 0) then CopyMemory(tmp, FData, FPosition);
-
-  FreeMem(FData);
-  FData := tmp;
-
-  e_WriteLog('Save Memory Extended: '+IntToStr(FSize), MSG_NOTIFY);
-end;
-
-procedure TBinMemoryWriter.WriteByte (x: Byte); begin WriteVar(x, sizeof(Byte)); end;
-procedure TBinMemoryWriter.WriteWord (x: Word); begin WriteVar(x, sizeof(Word)); end;
-procedure TBinMemoryWriter.WriteDWORD (x: LongWord); begin WriteVar(x, sizeof(LongWord)); end;
-procedure TBinMemoryWriter.WriteShortInt (x: ShortInt); begin WriteVar(x, sizeof(ShortInt)); end;
-procedure TBinMemoryWriter.WriteSmallInt (x: SmallInt); begin WriteVar(x, sizeof(SmallInt)); end;
-procedure TBinMemoryWriter.WriteInt (x: LongInt); begin WriteVar(x, sizeof(LongInt)); end;
-procedure TBinMemoryWriter.WriteSingle (x: Single); begin WriteVar(x, sizeof(Single)); end;
-
-procedure TBinMemoryWriter.WriteBoolean (x: Boolean);
-var
-  y: Byte;
-begin
-  if x then y := 1 else y := 0;
-  WriteVar(y, sizeof(Byte));
-end;
-
-procedure TBinMemoryWriter.WriteString (const x: AnsiString; aMaxLen: Word=65535);
-var
-  len: Word;
-begin
-  if (Length(x) > aMaxLen) then len := aMaxLen else len := Word(Length(x));
-
-  if (FPosition+sizeof(Byte)+len) > FSize then ExtendMemory(sizeof(Byte)+len);
-
-  // Äëèíà ñòðîêè
-  CopyMemory(Pointer(PtrUInt(FData)+FPosition), @len, sizeof(len));
-  FPosition := FPosition+sizeof(len);
-  // Ñòðîêà
-  if (len > 0) then
-  begin
-    CopyMemory(Pointer(PtrUInt(FData) + FPosition), @x[1], len);
-    FPosition := FPosition+len;
-  end;
-end;
-
-procedure TBinMemoryWriter.WriteMemory (x: Pointer; memSize: LongWord);
-begin
-  if (FPosition+sizeof(LongWord)+memSize) > FSize then ExtendMemory(sizeof(LongWord)+memSize);
-  // Äëèíà áëîêà ïàìÿòè
-  CopyMemory(Pointer(PtrUInt(FData)+FPosition), @memSize, sizeof(LongWord));
-  FPosition := FPosition+sizeof(LongWord);
-  // Áëîê ïàìÿòè
-  if (memSize > 0) then
-  begin
-    CopyMemory(Pointer(PtrUInt(FData)+FPosition), x, memSize);
-    FPosition := FPosition+memSize;
-  end;
-end;
-
-procedure TBinMemoryWriter.Fill (aLen: LongWord; aFillSym: Byte);
-begin
-  if (FPosition+aLen > FSize) then ExtendMemory(aLen);
-  if (aLen > 0) then
-  begin
-    FillMemory(Pointer(PtrUInt(FData) + FPosition), aLen, aFillSym);
-    FPosition := FPosition+aLen;
-  end;
-end;
-
-procedure TBinMemoryWriter.SaveToFile (st: TStream);
-begin
-  // Ðàçìåð áëîêà
-  utils.writeInt(st, LongWord(FPosition));
-  // Äàííûå áëîêà
-  if (FPosition > 0) then st.WriteBuffer(FData^, FPosition);
-end;
-
-procedure TBinMemoryWriter.SaveToMemory (aMem: TBinMemoryWriter);
-begin
-  if (aMem <> nil) then aMem.WriteMemory(FData, FPosition);
-end;
-
-
-{ T B i n M e m o r y R e a d e r : }
-
-constructor TBinMemoryReader.Create ();
-begin
-  FSize := 0;
-  FData := nil;
-  FPosition := 1;
-end;
-
-destructor TBinMemoryReader.Destroy ();
-begin
-  if (FData <> nil) then
-  begin
-    FreeMem(FData);
-    FData := nil;
-  end;
-  inherited;
-end;
-
-procedure TBinMemoryReader.ReadVar (var x; varSize: LongWord);
-begin
-  if (varSize = 0) then exit;
-  if (FPosition+varSize > FSize) then raise EBinSizeError.Create('TBinMemoryReader.ReadVar: End of Memory');
-  CopyMemory(@x, Pointer(PtrUInt(FData) + FPosition), varSize);
-  FPosition := FPosition+varSize;
-end;
-
-procedure TBinMemoryReader.ReadByte (var x: Byte); begin ReadVar(x, sizeof(Byte)); end;
-procedure TBinMemoryReader.ReadWord (var x: Word); begin ReadVar(x, sizeof(Word)); end;
-procedure TBinMemoryReader.ReadDWORD (var x: LongWord); begin ReadVar(x, sizeof(LongWord)); end;
-procedure TBinMemoryReader.ReadShortInt (var x: ShortInt); begin ReadVar(x, sizeof(ShortInt)); end;
-procedure TBinMemoryReader.ReadSmallInt (var x: SmallInt); begin ReadVar(x, sizeof(SmallInt)); end;
-procedure TBinMemoryReader.ReadInt (var x: LongInt); begin ReadVar(x, sizeof(LongInt)); end;
-procedure TBinMemoryReader.ReadSingle (var x: Single); begin ReadVar(x, sizeof(Single)); end;
-
-procedure TBinMemoryReader.ReadBoolean (var x: Boolean);
-var
-  y: Byte;
-begin
-  ReadVar(y, sizeof(Byte));
-  x := (y > 0);
-end;
-
-procedure TBinMemoryReader.ReadString (var x: AnsiString);
-var
-  len: Word;
-begin
-  if (FPosition+sizeof(len)) <= FSize then
-  begin
-    // Äëèíà ñòðîêè
-    CopyMemory(@len, Pointer(PtrUInt(FData)+FPosition), sizeof(len));
-    if (FPosition+sizeof(len)+len <= FSize) then
-    begin
-      FPosition := FPosition+sizeof(len);
-      // Ñòðîêà
-      UniqueString(x);
-      SetLength(x, len);
-      if (len > 0) then
-      begin
-        CopyMemory(@x[1], Pointer(PtrUInt(FData) + FPosition), len);
-        FPosition := FPosition+len;
-      end
-      else
-      begin
-        x := '';
-      end;
-    end
-    else
-    begin
-      raise EBinSizeError.Create('TBinMemoryReader.ReadString: Too Long AnsiString');
-    end;
-  end
-  else
-  begin
-    raise EBinSizeError.Create('TBinMemoryReader.ReadString: End of Memory');
-  end;
-end;
-
-procedure TBinMemoryReader.ReadMemory (var x: Pointer; var memSize: LongWord);
-begin
-  if (FPosition+sizeof(LongWord) > FSize) then raise EBinSizeError.Create('TBinMemoryReader.ReadMemory: End of Memory');
-  // Äëèíà áëîêà ïàìÿòè
-  CopyMemory(@memSize, Pointer(PtrUInt(FData)+FPosition), sizeof(LongWord));
-  if (FPosition+sizeof(LongWord)+memSize > FSize) then raise EBinSizeError.Create('TBinMemoryReader.ReadMemory: Too Long Memory');
-  FPosition := FPosition+sizeof(LongWord);
-  // Áëîê ïàìÿòè
-  if (memSize > 0) then
-  begin
-    GetMem(x, memSize);
-    CopyMemory(x, Pointer(PtrUInt(FData)+FPosition), memSize);
-    FPosition += memSize;
-  end
-  else
-  begin
-    x := nil;
-  end;
-end;
-
-procedure TBinMemoryReader.Skip(aLen: LongWord);
-begin
-  if (FPosition+aLen > FSize) then raise EBinSizeError.Create('TBinMemoryReader.Skip: End of Memory');
-  FPosition += aLen;
-end;
-
-procedure TBinMemoryReader.LoadFromFile (st: TStream);
-var
-  aSize: LongWord;
-begin
-  if (FData <> nil) then begin FreeMem(FData); FData := nil; end;
-  // Ðàçìåð áëîêà
-  aSize := utils.readLongWord(st);
-  FSize := aSize;
-  GetMem(FData, FSize);
-  FPosition := 0;
-  // Äàííûå áëîêà
-  if (aSize <> 0) then st.ReadBuffer(FData^, FSize);
-end;
-
-
-procedure TBinMemoryReader.LoadFromMemory (aMem: TBinMemoryReader);
-begin
-  if (FData <> nil) then begin FreeMem(FData); FData := nil; end;
-  if (aMem <> nil) then
-  begin
-    aMem.ReadMemory(FData, FSize);
-    FPosition := 0;
-  end;
-end;
-
-
-{ T B i n F i l e W r i t e r : }
-
-constructor TBinFileWriter.Create ();
-begin
-  FHandle := nil;
-end;
-
-destructor TBinFileWriter.Destroy ();
-begin
-  Close();
-  inherited;
-end;
-
-procedure TBinFileWriter.OpenFile (const aFileName: AnsiString; aFileSig: LongWord;
-                                   aFileVer: Byte; aOverWrite: Boolean=true);
-begin
-  Close();
-  if (not FileExists(aFileName)) or aOverWrite then
-  begin
-    try
-      FHandle := createDiskFile(aFileName);
-      // Ñèãíàòóðà
-      utils.writeInt(FHandle, LongWord(aFileSig));
-      // Âåðñèÿ
-      utils.writeInt(FHandle, Byte(aFileVer));
-    except
-      FHandle.Free();
-      FHandle := nil;
-      raise;
-    end;
-  end;
-end;
-
-procedure TBinFileWriter.Close();
-begin
-  if (FHandle <> nil) then
-  begin
-    FHandle.Free();
-    FHandle := nil;
-  end;
-end;
-
-procedure TBinFileWriter.WriteMemory (aMemory: TBinMemoryWriter);
-begin
-  if (FHandle <> nil) and (aMemory <> nil) then aMemory.SaveToFile(FHandle);
-end;
-
-
-{ T B i n F i l e R e a d e r : }
-
-constructor TBinFileReader.Create ();
-begin
-  FHandle := nil;
-end;
-
-destructor TBinFileReader.Destroy ();
-begin
-  Close();
-  inherited;
-end;
-
-function TBinFileReader.OpenFile (const aFileName: AnsiString; aFileSig: LongWord; aFileVer: Byte): Boolean;
-var
-  sig: LongWord;
-  ver: Byte;
-begin
-  result := false;
-
-  Close();
-
-  if FileExists(aFileName) then
-  begin
-    FHandle := openDiskFileRO(aFileName);
-    try
-      // Ñèãíàòóðà
-      sig := utils.readLongWord(FHandle);
-      if (sig <> aFileSig) then raise EInOutError.Create('TBinFileReader.OpenFile: Wrong File Signature');
-      // Âåðñèÿ
-      ver := utils.readByte(FHandle);
-      if (ver <> aFileVer) then raise EInOutError.Create('TBinFileReader.OpenFile: Wrong File Version');
-      result := true;
-    except
-      FHandle.Free();
-      FHandle := nil;
-      raise;
-    end;
-  end;
-end;
-
-procedure TBinFileReader.Close ();
-begin
-  if (FHandle <> nil) then
-  begin
-    FHandle.Free();
-    FHandle := nil;
-  end;
-end;
-
-procedure TBinFileReader.ReadMemory (aMemory: TBinMemoryReader);
-begin
-  if (FHandle <> nil) and (aMemory <> nil) then aMemory.LoadFromFile(FHandle);
-end;
-
-
-end.
index 155ebacabbeee0357f7e6fbd21225d2541c014f8..7ecc51e70fb180944e2dd2c36b48ce917bb3f1a3 100644 (file)
@@ -61,7 +61,7 @@ type
 implementation
 
 uses
-  SysUtils, BinEditor;
+  SysUtils, utils;
 
 { TConfig }
 
index d8b6e85c266dcf6fb2142759b53e8b5e35e913aa..d0a0ac53375a5b563b46cdfe99be12a61b5eec5c 100644 (file)
@@ -100,6 +100,15 @@ function openDiskFileRO (pathname: AnsiString): TStream;
 function createDiskFile (pathname: AnsiString): TStream;
 
 // little endian
+procedure writeSign (st: TStream; const sign: AnsiString);
+function checkSign (st: TStream; const sign: AnsiString): Boolean;
+
+procedure writeBool (st: TStream; b: Boolean);
+function readBool (st: TStream): Boolean;
+
+procedure writeStr (st: TStream; const str: AnsiString; maxlen: LongWord=65535);
+function readStr (st: TStream; maxlen: LongWord=65535): AnsiString;
+
 procedure writeInt (st: TStream; v: Byte); overload;
 procedure writeInt (st: TStream; v: ShortInt); overload;
 procedure writeInt (st: TStream; v: Word); overload;
@@ -245,8 +254,33 @@ type
   end;
 
 
+procedure FillMemory (Dest: Pointer; Len: LongWord; Ch: Byte); inline;
+procedure CopyMemory (Dest: Pointer; Src: Pointer; Len: LongWord); inline;
+procedure ZeroMemory (Dest: Pointer; Len: LongWord); inline;
+
+
 implementation
 
+uses
+  xstreams;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure CopyMemory (Dest: Pointer; Src: Pointer; Len: LongWord); inline;
+begin
+  Move(Src^, Dest^, Len);
+end;
+
+procedure FillMemory (Dest: Pointer; Len: LongWord; Ch: Byte); inline;
+begin
+  FillChar(Dest^, Len, Ch);
+end;
+
+procedure ZeroMemory (Dest: Pointer; Len: LongWord); inline;
+begin
+  FillChar(Dest^, Len, 0);
+end;
+
 
 // ////////////////////////////////////////////////////////////////////////// //
 constructor TSimpleList.TEnumerator.Create (const aitems: TItemArr; acount: Integer);
@@ -1140,6 +1174,36 @@ begin
 end;
 {$ENDIF}
 
+procedure writeSign (st: TStream; const sign: AnsiString);
+begin
+  if (Length(sign) > 0) then st.WriteBuffer(sign[1], Length(sign));
+end;
+
+function checkSign (st: TStream; const sign: AnsiString): Boolean;
+var
+  buf: packed array[0..7] of Char;
+  f: Integer;
+begin
+  result := false;
+  if (Length(sign) > 0) then
+  begin
+    if (Length(sign) <= 8) then
+    begin
+      st.ReadBuffer(buf[0], Length(sign));
+      for f := 1 to Length(sign) do if (buf[f-1] <> sign[f]) then exit;
+    end
+    else
+    begin
+      for f := 1 to Length(sign) do
+      begin
+        st.ReadBuffer(buf[0], 1);
+        if (buf[0] <> sign[f]) then exit;
+      end;
+    end;
+  end;
+  result := true;
+end;
+
 procedure writeInt (st: TStream; v: Byte); overload; begin writeIntegerLE(st, @v, 1); end;
 procedure writeInt (st: TStream; v: ShortInt); overload; begin writeIntegerLE(st, @v, 1); end;
 procedure writeInt (st: TStream; v: Word); overload; begin writeIntegerLE(st, @v, 2); end;
@@ -1158,6 +1222,31 @@ procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(s
 procedure writeIntBE (st: TStream; v: Int64); overload; begin writeIntegerBE(st, @v, 8); end;
 procedure writeIntBE (st: TStream; v: UInt64); overload; begin writeIntegerBE(st, @v, 8); end;
 
+procedure writeBool (st: TStream; b: Boolean); begin writeInt(st, Byte(b)); end;
+function readBool (st: TStream): Boolean; begin result := (readByte(st) <> 0); end;
+
+
+procedure writeStr (st: TStream; const str: AnsiString; maxlen: LongWord=65535);
+begin
+  if (Length(str) > maxlen) then raise XStreamError.Create('string too long');
+  if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
+  if (Length(str) > 0) then st.WriteBuffer(str[1], Length(str));
+end;
+
+function readStr (st: TStream; maxlen: LongWord=65535): AnsiString;
+var
+  len: Integer;
+begin
+  result := '';
+  if (maxlen <= 65535) then len := readWord(st) else len := Integer(readLongWord(st));
+  if (len < 0) or (len > maxlen) then raise XStreamError.Create('string too long');
+  if (len > 0) then
+  begin
+    SetLength(result, len);
+    st.ReadBuffer(result[1], len);
+  end;
+end;
+
 
 procedure readIntegerLE (st: TStream; vp: Pointer; size: Integer);
 {$IFDEF ENDIAN_LITTLE}