index 93c392ce8804dc298ccdbc9b9a5fa9be42037392..003c58b4df7f5be2923be888ebe0bdb3881ded3f 100644 (file)
--- a/src/game/g_saveload.pas
+++ b/src/game/g_saveload.pas
-(* Copyright (C) DooM 2D:Forever Developers
+(* 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
*
* 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.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
interface
uses
interface
uses
- e_graphics, g_phys, g_textures, BinEditor;
+ SysUtils, Classes, 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
implementation
uses
+ MAPDEF, utils, xstreams,
g_game, g_items, g_map, g_monsters, g_triggers,
g_game, g_items, g_map, g_monsters, g_triggers,
- g_basic, g_main, SysUtils, Math, wadreader,
- MAPDEF, g_weapons, g_player, g_console,
- e_log, g_language;
+ g_basic, Math, wadreader,
+ g_weapons, g_player, g_console,
+ e_log, e_res, g_language, g_options;
const
SAVE_SIGNATURE = $56534644; // 'DFSV'
const
SAVE_SIGNATURE = $56534644; // 'DFSV'
- SAVE_VERSION = $06;
+ SAVE_VERSION = $07;
END_MARKER_STRING = 'END';
PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW'
OBJ_SIGNATURE = $4A424F5F; // '_OBJ'
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
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;
end;
-procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader);
-var
- sig: DWORD;
+
+procedure Obj_LoadState (o: PObj; st: TStream);
begin
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;
end;
-function g_GetSaveName(n: Integer): String;
-var
- bFile: TBinFileReader;
- bMem: TBinMemoryReader;
- str: String;
+
+function buildSaveName (n: Integer): AnsiString;
begin
begin
- Result := '';
- str := '';
- bMem := nil;
- bFile := nil;
+ result := 'SAVGAME' + IntToStr(n) + '.DAT'
+end;
+
+function g_GetSaveName (n: Integer; out valid: Boolean): AnsiString;
+var
+ st: TStream = nil;
+ ver: Byte;
+ stlen: Word;
+ filename: AnsiString;
+begin
+ valid := false;
+ result := '';
+ if (n < 0) or (n > 65535) then exit;
try
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();
+ // Îòêðûâàåì ôàéë ñîõðàíåíèé
+ filename := buildSaveName(n);
+ st := e_OpenResourceRO(SaveDirs, filename);
+ try
+ if not utils.checkSign(st, 'DFSV') then
+ begin
+ e_LogWritefln('GetSaveName: not a save file: ''%s''', [st], TMsgType.Warning);
+ //raise XStreamError.Create('invalid save game signature');
+ exit;
+ end;
+ 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
+ begin
+ e_LogWritefln('GetSaveName: not a save file: ''%s''', [st], TMsgType.Warning);
+ //raise XStreamError.Create('invalid save game version');
+ exit;
+ end;
+ // Èìÿ ñýéâà
+ 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;
end;
-
except
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);
+ begin
+ //e_WriteLog('GetSaveName Error: '+e.message, MSG_WARNING);
+ //{$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
+ result := '';
+ end;
end;
end;
-
- bMem.Free();
- bFile.Free();
-
- Result := str;
end;
end;
-function g_SaveGame(n: Integer; Name: String): Boolean;
+
+function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean;
var
var
- bFile: TBinFileWriter;
- bMem: TBinMemoryWriter;
- sig: DWORD;
- str: String;
- nPlayers: Byte;
+ st: TStream = nil;
i, k: Integer;
PID1, PID2: Word;
begin
i, k: Integer;
PID1, PID2: Word;
begin
- Result := False;
- bMem := nil;
- bFile := nil;
-
+ result := false;
try
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 := e_CreateResource(SaveDirs, 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, ExtractFileName(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.ScoreLimit));
+ // Ëèìèò æèçíåé
+ 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
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;
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;
end;
+ ///// /////
+
+ ///// Ìàðêåð îêîí÷àíèÿ /////
+ utils.writeSign(st, 'END');
+ utils.writeInt(st, Byte(0));
+ ///// /////
+ result := true;
+ finally
+ st.Free();
end;
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
except
- on E1: EInOutError do
+ on e: Exception do
begin
begin
+ st.Free();
g_Console_Add(_lc[I_GAME_ERROR_SAVE]);
g_Console_Add(_lc[I_GAME_ERROR_SAVE]);
- e_WriteLog('SaveState I/O Error: '+E1.Message, MSG_WARNING);
- end;
- on E2: EBinSizeError do
- begin
- g_Console_Add(_lc[I_GAME_ERROR_SAVE]);
- e_WriteLog('SaveState Size Error: '+E2.Message, MSG_WARNING);
+ e_WriteLog('SaveState Error: '+e.message, TMsgType.Warning);
+ if deleteOnError then DeleteFile(filename);
+ {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
+e_WriteStackTrace(e.message);
+ result := false;
end;
end;
end;
end;
-
- bMem.Free();
- bFile.Free();
end;
end;
-function g_LoadGame(n: Integer): Boolean;
+
+function g_LoadGameFrom (const filename: AnsiString): Boolean;
var
var
- bFile: TBinFileReader;
- bMem: TBinMemoryReader;
- sig: DWORD;
- str, WAD_Path, Map_Name: String;
- nPlayers, Game_Type, Game_Mode, Game_MaxLives: Byte;
- Game_TimeLimit, Game_GoalLimit: Word;
+ st: TStream = nil;
+ WAD_Path, Map_Name: AnsiString;
+ nPlayers: Integer;
+ Game_Type, Game_Mode, Game_MaxLives: Byte;
+ Game_TimeLimit, Game_ScoreLimit: Word;
Game_Time, Game_Options: Cardinal;
Game_CoopMonstersKilled,
Game_CoopSecretsFound,
Game_Time, Game_Options: Cardinal;
Game_CoopMonstersKilled,
Game_CoopSecretsFound,
i: Integer;
gameCleared: Boolean = false;
curmapfile: AnsiString = '';
i: Integer;
gameCleared: Boolean = false;
curmapfile: AnsiString = '';
+ {$IF DEFINED(D2F_DEBUG)}
+ errpos: LongWord = 0;
+ {$ENDIF}
begin
begin
- Result := False;
- bMem := nil;
- bFile := nil;
+ result := false;
try
try
- // Îòêðûâàåì ôàéë ñ ñîõðàíåíèåì:
- bFile := TBinFileReader.Create();
- if not bFile.OpenFile(DataDir + 'SAVGAME' + IntToStr(n) + '.DAT',
- SAVE_SIGNATURE, SAVE_VERSION) then
- begin
- bFile.Free();
- Exit;
- end;
-
- 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');
+ st := e_OpenResourceRO(SaveDirs, filename);
+ try
+ if not utils.checkSign(st, 'DFSV') then raise XStreamError.Create('invalid save game signature');
+ if (utils.readByte(st) <> SAVE_VERSION) then raise XStreamError.Create('invalid save game version');
+
+ e_WriteLog('Loading saved game...', TMsgType.Notify);
+
+{$IF DEFINED(D2F_DEBUG)}
+ try
+{$ENDIF}
+ //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_ScoreLimit := 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.ScoreLimit := Game_ScoreLimit;
+ gGameSettings.MaxLives := IfThen(Game_Mode = GM_CTF, 0, Game_MaxLives);
+ gGameSettings.Options := Game_Options;
+ end;
+ g_Game_ExecuteEvent('ongamestart');
+
+ // Çàãðóçêà è çàïóñê êàðòû
+ //FIXME: save/load `asMegawad`
+ if not g_Game_StartMap(false{asMegawad}, WAD_Path+':\'+Map_Name, True, curmapfile) then
+ raise Exception.Create(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name]));
+
+ // Íàñòðîéêè èãðîêîâ è áîòîâ
+ 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_Game_SetupScreenSize();
+ // Ïðèâÿçûâàåì îñíîâíûõ èãðîêîâ ê îáëàñòÿì ïðîñìîòðà
+ gPlayer1 := g_Player_Get(PID1);
+ gPlayer2 := g_Player_Get(PID2);
- // Çàãðóçêà è çàïóñê êàðòû:
- 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 (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;
- // Íàñòðîéêè èãðîêîâ è áîòîâ:
- 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;
- ///// /////
+ 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;
+ ///// /////
- // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ:
- if {(gMonsters <> nil) and} (gTriggers <> nil) then
- g_Map_ReAdd_DieTriggers();
+ ///// Ìàðêåð îêîí÷àíèÿ /////
+ 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');
+ ///// /////
- // Çàêðûâàåì ôàéë çàãðóçêè:
- bFile.Close();
- gLoadGameMode := False;
- Result := True;
+ // Èùåì òðèããåðû ñ óñëîâèåì ñìåðòè ìîíñòðîâ
+ if (gTriggers <> nil) then g_Map_ReAdd_DieTriggers();
+ // done
+ gLoadGameMode := false;
+ result := true;
+{$IF DEFINED(D2F_DEBUG)}
+ except
+ begin
+ errpos := LongWord(st.position);
+ raise;
+ end;
+ end;
+{$ENDIF}
+ finally
+ st.Free();
+ end;
except
except
- on E1: EInOutError do
+ on e: EFileNotFoundException do
begin
g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
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();
+ g_Console_Add('LoadState Error: '+e.message);
+ e_WriteLog('LoadState Error: '+e.message, TMsgType.Warning);
+ gLoadGameMode := false;
+ result := false;
end;
end;
- on E2: EBinSizeError do
+ on e: Exception do
begin
g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
begin
g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
- e_WriteLog('LoadState Size Error: '+E2.Message, MSG_WARNING);
- if not gameCleared then g_Game_Free();
+ g_Console_Add('LoadState Error: '+e.message);
+ e_WriteLog('LoadState Error: '+e.message, TMsgType.Warning);
+ {$IF DEFINED(D2F_DEBUG)}e_LogWritefln('stream error position: 0x%08x', [errpos], TMsgType.Warning);{$ENDIF}
+ gLoadGameMode := false;
+ result := false;
+ if gState <> STATE_MENU then
+ g_FatalError(_lc[I_GAME_ERROR_LOAD])
+ else if not gameCleared then
+ g_Game_Free();
+ {$IF DEFINED(D2F_DEBUG)}e_WriteStackTrace(e.message);{$ENDIF}
end;
end;
end;
end;
+end;
+
- bMem.Free();
- bFile.Free();
+function g_SaveGame (n: Integer; const aname: AnsiString): Boolean;
+begin
+ result := g_SaveGameTo(buildSaveName(n), aname, true);
end;
end;
+
+function g_LoadGame (n: Integer): Boolean;
+begin
+ result := g_LoadGameFrom(buildSaveName(n));
+end;
+
+
end.
end.