mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-25 03:00:46 +00:00
- fixed savegame issues.
Since the code is extremely volatile I changed the setup so that the save is a zip file with the regular snapshot plus all added data as separate entries. This allows compressing everything properly without savegame breaking interference. Blood does not yet load its savegames, need to check.
This commit is contained in:
parent
0ae9d3b93e
commit
ba117554b0
13 changed files with 339 additions and 197 deletions
|
@ -746,6 +746,8 @@ set (PCH_SOURCES
|
||||||
common/optionmenu/optionmenu.cpp
|
common/optionmenu/optionmenu.cpp
|
||||||
common/statistics.cpp
|
common/statistics.cpp
|
||||||
common/secrets.cpp
|
common/secrets.cpp
|
||||||
|
common/compositesavegame.cpp
|
||||||
|
common/savegamehelp.cpp
|
||||||
|
|
||||||
common/2d/v_2ddrawer.cpp
|
common/2d/v_2ddrawer.cpp
|
||||||
common/2d/v_draw.cpp
|
common/2d/v_draw.cpp
|
||||||
|
|
|
@ -48,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
#include "statistics.h"
|
#include "statistics.h"
|
||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
|
|
||||||
BEGIN_BLD_NS
|
BEGIN_BLD_NS
|
||||||
|
|
||||||
|
@ -117,7 +118,8 @@ void LoadSave::LoadGame(char *pzFile)
|
||||||
memset(sprite, 0, sizeof(spritetype)*kMaxSprites);
|
memset(sprite, 0, sizeof(spritetype)*kMaxSprites);
|
||||||
automapping = 1;
|
automapping = 1;
|
||||||
}
|
}
|
||||||
hLFile = fopenFileReader(pzFile, 0);
|
OpenSaveGameForRead(pzFile);
|
||||||
|
hLFile = ReadSavegameChunk("snapshot.bld");
|
||||||
if (!hLFile.isOpen())
|
if (!hLFile.isOpen())
|
||||||
ThrowError("Error loading save file.");
|
ThrowError("Error loading save file.");
|
||||||
LoadSave *rover = head.next;
|
LoadSave *rover = head.next;
|
||||||
|
@ -126,10 +128,11 @@ void LoadSave::LoadGame(char *pzFile)
|
||||||
rover->Load();
|
rover->Load();
|
||||||
rover = rover->next;
|
rover = rover->next;
|
||||||
}
|
}
|
||||||
if (!ReadStatistics(hLFile) || !SECRET_Load(hLFile)) // read the rest...
|
if (!ReadStatistics() || !SECRET_Load()) // read the rest...
|
||||||
ThrowError("Error loading save file.");
|
ThrowError("Error loading save file.");
|
||||||
|
|
||||||
hLFile.Close();
|
hLFile.Close();
|
||||||
|
FinishSavegameRead();
|
||||||
if (!gGameStarted)
|
if (!gGameStarted)
|
||||||
scrLoadPLUs();
|
scrLoadPLUs();
|
||||||
InitSectorFX();
|
InitSectorFX();
|
||||||
|
@ -193,7 +196,8 @@ void LoadSave::LoadGame(char *pzFile)
|
||||||
|
|
||||||
void LoadSave::SaveGame(char *pzFile)
|
void LoadSave::SaveGame(char *pzFile)
|
||||||
{
|
{
|
||||||
hSFile = FileWriter::Open(pzFile);
|
OpenSaveGameForWrite(pzFile);
|
||||||
|
hSFile = WriteSavegameChunk("snapshot.bld");
|
||||||
if (hSFile == NULL)
|
if (hSFile == NULL)
|
||||||
ThrowError("File error #%d creating save file.", errno);
|
ThrowError("File error #%d creating save file.", errno);
|
||||||
dword_27AA38 = 0;
|
dword_27AA38 = 0;
|
||||||
|
@ -207,9 +211,9 @@ void LoadSave::SaveGame(char *pzFile)
|
||||||
dword_27AA38 = 0;
|
dword_27AA38 = 0;
|
||||||
rover = rover->next;
|
rover = rover->next;
|
||||||
}
|
}
|
||||||
SaveStatistics(*hSFile);
|
SaveStatistics();
|
||||||
SECRET_Save(*hSFile);
|
SECRET_Save();
|
||||||
delete hSFile;
|
FinishSavegameWrite();
|
||||||
hSFile = NULL;
|
hSFile = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,39 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "zstring.h"
|
#include "zstring.h"
|
||||||
#include "tarray.h"
|
#include "tarray.h"
|
||||||
|
#include "filesystem/resourcefile.h"
|
||||||
|
|
||||||
class CompositeSavegameWriter
|
class CompositeSavegameWriter
|
||||||
{
|
{
|
||||||
|
FString filename;
|
||||||
TDeletingArray<BufferWriter*> subfiles;
|
TDeletingArray<BufferWriter*> subfiles;
|
||||||
TArray<FString> subfilenames;
|
TArray<FString> subfilenames;
|
||||||
TArray<bool> isCompressed;
|
TArray<bool> isCompressed;
|
||||||
|
|
||||||
FCompressedBuffer CompressElement(BufferWriter* element, bool compress);
|
FCompressedBuffer CompressElement(BufferWriter* element, bool compress);
|
||||||
public:
|
public:
|
||||||
|
void Clear()
|
||||||
FileWriter &NewEleemnt(const char *filename, bool compress = true);
|
{
|
||||||
bool WriteToFile(const char *filename);
|
subfiles.Reset();
|
||||||
|
subfilenames.Reset();
|
||||||
|
isCompressed.Reset();
|
||||||
|
}
|
||||||
|
void SetFileName(const char* fn)
|
||||||
|
{
|
||||||
|
filename = fn;
|
||||||
|
}
|
||||||
|
void SetFileName(const FString& fn)
|
||||||
|
{
|
||||||
|
filename = fn;
|
||||||
|
}
|
||||||
|
~CompositeSavegameWriter()
|
||||||
|
{
|
||||||
|
assert(subfiles.Size() == 0); // must be written out.
|
||||||
|
}
|
||||||
|
FileWriter& NewElement(const char* filename, bool compress = true);
|
||||||
|
bool WriteToFile();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,15 +34,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "compositesavegame.h"
|
#include "compositesaveame.h"
|
||||||
#include "file_zip.h"
|
#include "file_zip.h"
|
||||||
#include "resourcefiles.h"
|
#include "resourcefile.h"
|
||||||
|
|
||||||
|
|
||||||
bool WriteZip(const char *filename, TArray<FString> &filenames, TArray<FCompressedBuffer> &content);
|
bool WriteZip(const char *filename, TArray<FString> &filenames, TArray<FCompressedBuffer> &content);
|
||||||
|
|
||||||
|
|
||||||
FileWriter &CompositeSavegameWriter::NewEleemnt(const char *filename, bool compress)
|
FileWriter &CompositeSavegameWriter::NewElement(const char *filename, bool compress)
|
||||||
{
|
{
|
||||||
subfilenames.Push(filename);
|
subfilenames.Push(filename);
|
||||||
isCompressed.Push(compress);
|
isCompressed.Push(compress);
|
||||||
|
@ -100,14 +100,15 @@ FCompressedBuffer CompositeSavegameWriter::CompressElement(BufferWriter *bw, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
memcpy(compressbuf, buffer->Data(), buff.mSize + 1);
|
if (buff.mSize) memcpy(compressbuf, buffer->Data(), buff.mSize + 1);
|
||||||
|
buff.mBuffer = (char*)compressbuf;
|
||||||
buff.mCompressedSize = buff.mSize;
|
buff.mCompressedSize = buff.mSize;
|
||||||
buff.mMethod = METHOD_STORED;
|
buff.mMethod = METHOD_STORED;
|
||||||
return buff;
|
return buff;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompositeSavegameWriter::WriteToFile(const char *filename)
|
bool CompositeSavegameWriter::WriteToFile()
|
||||||
{
|
{
|
||||||
TArray<FCompressedBuffer> compressed(subfiles.Size(), 1);
|
TArray<FCompressedBuffer> compressed(subfiles.Size(), 1);
|
||||||
for (unsigned i = 0; i < subfiles.Size(); i++)
|
for (unsigned i = 0; i < subfiles.Size(); i++)
|
||||||
|
@ -118,14 +119,16 @@ bool CompositeSavegameWriter::WriteToFile(const char *filename)
|
||||||
if (WriteZip(filename, subfilenames, compressed))
|
if (WriteZip(filename, subfilenames, compressed))
|
||||||
{
|
{
|
||||||
// Check whether the file is ok by trying to open it.
|
// Check whether the file is ok by trying to open it.
|
||||||
FResourceFile *test = FResourceFile::OpenResourceFile(filename, true);
|
//FResourceFile *test = FResourceFile::OpenResourceFile(filename, true);
|
||||||
if (test != nullptr)
|
//if (test != nullptr)
|
||||||
{
|
{
|
||||||
delete test;
|
Clear();
|
||||||
|
//delete test;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "c_dispatch.h"
|
#include "c_dispatch.h"
|
||||||
#include "i_specialpaths.h"
|
#include "i_specialpaths.h"
|
||||||
#include "z_music.h"
|
#include "z_music.h"
|
||||||
|
#include "statistics.h"
|
||||||
#ifndef NETCODE_DISABLE
|
#ifndef NETCODE_DISABLE
|
||||||
#include "enet.h"
|
#include "enet.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -379,6 +380,7 @@ int CONFIG_Init()
|
||||||
V_InitFonts();
|
V_InitFonts();
|
||||||
buttonMap.SetGameAliases();
|
buttonMap.SetGameAliases();
|
||||||
Mus_Init();
|
Mus_Init();
|
||||||
|
InitStatistics();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
82
source/common/savegamehelp.cpp
Normal file
82
source/common/savegamehelp.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
** savegame.cpp
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2019 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OFf
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
** This is for keeping my sanity while working with the horrible mess
|
||||||
|
** that is the savegame code in Duke Nukem.
|
||||||
|
** Without handling this in global variables it is a losing proposition
|
||||||
|
** to save custom data along with the regular snapshot. :(
|
||||||
|
** With this the savegame code can mostly pretend to load from and write
|
||||||
|
** to files while really using a composite archive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compositesaveame.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
|
|
||||||
|
|
||||||
|
static CompositeSavegameWriter savewriter;
|
||||||
|
static FResourceFile *savereader;
|
||||||
|
|
||||||
|
void OpenSaveGameForWrite(const char *name)
|
||||||
|
{
|
||||||
|
savewriter.Clear();
|
||||||
|
savewriter.SetFileName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenSaveGameForRead(const char *name)
|
||||||
|
{
|
||||||
|
if (savereader) delete savereader;
|
||||||
|
savereader = FResourceFile::OpenResourceFile(name, true, true);
|
||||||
|
return savereader != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileWriter *WriteSavegameChunk(const char *name)
|
||||||
|
{
|
||||||
|
return &savewriter.NewElement(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileReader ReadSavegameChunk(const char *name)
|
||||||
|
{
|
||||||
|
if (!savereader) return FileReader();
|
||||||
|
auto lump = savereader->FindLump(name);
|
||||||
|
if (!lump) return FileReader();
|
||||||
|
return lump->NewReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FinishSavegameWrite()
|
||||||
|
{
|
||||||
|
return savewriter.WriteToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishSavegameRead()
|
||||||
|
{
|
||||||
|
delete savereader;
|
||||||
|
savereader = nullptr;
|
||||||
|
}
|
12
source/common/savegamehelp.h
Normal file
12
source/common/savegamehelp.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "filesystem/resourcefile.h"
|
||||||
|
|
||||||
|
void OpenSaveGameForWrite(const char *name);
|
||||||
|
bool OpenSaveGameForRead(const char *name);
|
||||||
|
|
||||||
|
FileWriter *WriteSavegameChunk(const char *name);
|
||||||
|
FileReader ReadSavegameChunk(const char *name);
|
||||||
|
|
||||||
|
bool FinishSavegameWrite();
|
||||||
|
void FinishSavegameRead();
|
|
@ -6,6 +6,7 @@
|
||||||
#include "c_cvars.h"
|
#include "c_cvars.h"
|
||||||
#include "v_font.h"
|
#include "v_font.h"
|
||||||
#include "v_draw.h"
|
#include "v_draw.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
|
|
||||||
// Unlike in GZDoom we have to maintain this list here, because we got different game frontents that all store this info differently.
|
// Unlike in GZDoom we have to maintain this list here, because we got different game frontents that all store this info differently.
|
||||||
// So the games will have to report the credited secrets so that this code can keep track of how to display them.
|
// So the games will have to report the credited secrets so that this code can keep track of how to display them.
|
||||||
|
@ -106,20 +107,23 @@ CCMD(secret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SECRET_Save(FileWriter &fil)
|
void SECRET_Save()
|
||||||
{
|
{
|
||||||
fil.Write("SECR", 4);
|
auto fil = WriteSavegameChunk("secrets.dat");
|
||||||
|
fil->Write("SECR", 4);
|
||||||
unsigned count = discovered_secrets.Size();
|
unsigned count = discovered_secrets.Size();
|
||||||
fil.Write(&count, 4);
|
fil->Write(&count, 4);
|
||||||
fil.Write(discovered_secrets.Data(), 4 * count);
|
fil->Write(discovered_secrets.Data(), 4 * count);
|
||||||
fil.Write("RCES", 4);
|
fil->Write("RCES", 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SECRET_Load(FileReader &fil)
|
bool SECRET_Load()
|
||||||
{
|
{
|
||||||
char buf[4];
|
char buf[4];
|
||||||
unsigned count;
|
unsigned count;
|
||||||
|
auto fil = ReadSavegameChunk("secrets.dat");
|
||||||
|
if (!fil.isOpen()) return false;
|
||||||
fil.Read(buf, 4);
|
fil.Read(buf, 4);
|
||||||
if (memcmp(buf, "SECR", 4)) return false;
|
if (memcmp(buf, "SECR", 4)) return false;
|
||||||
fil.Read(&count, 4);
|
fil.Read(&count, 4);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
|
|
||||||
void SECRET_Save(FileWriter &fil);
|
void SECRET_Save();
|
||||||
bool SECRET_Load(FileReader &fil);
|
bool SECRET_Load();
|
||||||
void SECRET_SetMapName(const char *filename, const char *maptitle);
|
void SECRET_SetMapName(const char *filename, const char *maptitle);
|
||||||
void SECRET_Trigger(int num);
|
void SECRET_Trigger(int num);
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "c_cvars.h"
|
#include "c_cvars.h"
|
||||||
#include "sc_man.h"
|
#include "sc_man.h"
|
||||||
#include "baselayer.h"
|
#include "baselayer.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
|
|
||||||
CVAR(Int, savestatistics, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR(Int, savestatistics, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
CVAR(String, statfile, "demolitionstat.txt", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
CVAR(String, statfile, "demolitionstat.txt", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||||
|
@ -171,7 +172,7 @@ static void ParseStatistics(const char *fn, TArray<FStatistics> &statlist)
|
||||||
//
|
//
|
||||||
// ====================================================================
|
// ====================================================================
|
||||||
|
|
||||||
void ReadStatistics()
|
void InitStatistics()
|
||||||
{
|
{
|
||||||
ParseStatistics(statfile, EpisodeStatistics);
|
ParseStatistics(statfile, EpisodeStatistics);
|
||||||
}
|
}
|
||||||
|
@ -484,25 +485,27 @@ void ReadOneLevel(FileReader& fil, OneLevel& l)
|
||||||
l.Levelname.UnlockBuffer();
|
l.Levelname.UnlockBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveStatistics(FileWriter& fil)
|
void SaveStatistics()
|
||||||
{
|
{
|
||||||
fil.Write("STAT", 4);
|
auto fil = WriteSavegameChunk("statistics.dat");
|
||||||
fil.Write(LevelName, MAX_PATH);
|
fil->Write("STAT", 4);
|
||||||
fil.Write(&StartEpisode, MAX_PATH);
|
fil->Write(LevelName, MAX_PATH);
|
||||||
fil.Write(&StartSkill, 4);
|
fil->Write(&StartEpisode, MAX_PATH);
|
||||||
|
fil->Write(&StartSkill, 4);
|
||||||
int p = LevelData.Size();
|
int p = LevelData.Size();
|
||||||
fil.Write(&p, 4);
|
fil->Write(&p, 4);
|
||||||
for (auto& lev : LevelData)
|
for (auto& lev : LevelData)
|
||||||
{
|
{
|
||||||
SaveOneLevel(fil, lev);
|
SaveOneLevel(*fil, lev);
|
||||||
}
|
}
|
||||||
fil.Write("TATS", 4);
|
fil->Write("TATS", 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadStatistics(FileReader& fil)
|
bool ReadStatistics()
|
||||||
{
|
{
|
||||||
char id[4];
|
char id[4];
|
||||||
|
auto fil = ReadSavegameChunk("statistics.dat");
|
||||||
|
if (!fil.isOpen()) return false;
|
||||||
fil.Read(id, 4);
|
fil.Read(id, 4);
|
||||||
if (memcmp(id, "STAT", 4)) return false;
|
if (memcmp(id, "STAT", 4)) return false;
|
||||||
fil.Read(LevelName, MAX_PATH);
|
fil.Read(LevelName, MAX_PATH);
|
||||||
|
|
|
@ -6,5 +6,6 @@ void STAT_NewLevel(const char* mapname);
|
||||||
void STAT_Update(bool endofgame);
|
void STAT_Update(bool endofgame);
|
||||||
void STAT_Cancel();
|
void STAT_Cancel();
|
||||||
|
|
||||||
void SaveStatistics(FileWriter& fil);
|
void InitStatistics();
|
||||||
bool ReadStatistics(FileReader& fil);
|
void SaveStatistics();
|
||||||
|
bool ReadStatistics();
|
||||||
|
|
|
@ -32,10 +32,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "statistics.h"
|
#include "statistics.h"
|
||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
|
|
||||||
BEGIN_DUKE_NS
|
BEGIN_DUKE_NS
|
||||||
|
|
||||||
|
|
||||||
static OutputFileCounter savecounter;
|
static OutputFileCounter savecounter;
|
||||||
char previousboardfilename[BMAX_PATH];
|
char previousboardfilename[BMAX_PATH];
|
||||||
|
|
||||||
|
@ -152,27 +152,25 @@ uint16_t g_nummenusaves;
|
||||||
static menusave_t * g_internalsaves;
|
static menusave_t * g_internalsaves;
|
||||||
static uint16_t g_numinternalsaves;
|
static uint16_t g_numinternalsaves;
|
||||||
|
|
||||||
static FileReader OpenSavegame(const char *fn)
|
static FileReader *OpenSavegame(const char *fn)
|
||||||
{
|
{
|
||||||
auto file = fopenFileReader(fn, 0);
|
if (!OpenSaveGameForRead(fn))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto file = ReadSavegameChunk("DEMOLITION_ED");
|
||||||
if (!file.isOpen())
|
if (!file.isOpen())
|
||||||
return file;
|
|
||||||
|
|
||||||
char buffer[13];
|
|
||||||
file.Read(buffer, 13);
|
|
||||||
if (memcmp(buffer, "DEMOLITION_ED", 13))
|
|
||||||
return FileReader();
|
|
||||||
|
|
||||||
FileReader fr;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
fr.OpenDecompressor(file, file.GetLength()-13, METHOD_DEFLATE, false, nullptr);
|
FinishSavegameRead();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
catch(std::runtime_error & err)
|
file = ReadSavegameChunk("snapshot.dat");
|
||||||
|
if (!file.isOpen())
|
||||||
{
|
{
|
||||||
Printf("%s: %s\n", fn, err.what());
|
FinishSavegameRead();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
return fr;
|
return new FileReader(std::move(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
|
static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
|
||||||
|
@ -182,14 +180,15 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
|
||||||
for (FString &save : saves)
|
for (FString &save : saves)
|
||||||
{
|
{
|
||||||
auto fil = OpenSavegame(save);
|
auto fil = OpenSavegame(save);
|
||||||
if (!fil.isOpen())
|
if (!fil)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
menusave_t & msv = g_internalsaves[g_numinternalsaves];
|
menusave_t & msv = g_internalsaves[g_numinternalsaves];
|
||||||
|
|
||||||
msv.brief.isExt = 0;
|
msv.brief.isExt = 0;
|
||||||
|
|
||||||
int32_t k = sv_loadheader(fil, 0, &h);
|
int32_t k = sv_loadheader(*fil, 0, &h);
|
||||||
|
delete fil;
|
||||||
if (k)
|
if (k)
|
||||||
{
|
{
|
||||||
if (k < 0)
|
if (k < 0)
|
||||||
|
@ -198,8 +197,7 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
|
||||||
{
|
{
|
||||||
if (FURY)
|
if (FURY)
|
||||||
{
|
{
|
||||||
FStringf extfn("%s.ext", save.GetChars());
|
auto extfil = ReadSavegameChunk("ext.json");
|
||||||
auto extfil = fopenFileReader(extfn, 0);
|
|
||||||
if (extfil.isOpen())
|
if (extfil.isOpen())
|
||||||
{
|
{
|
||||||
msv.brief.isExt = 1;
|
msv.brief.isExt = 1;
|
||||||
|
@ -224,6 +222,7 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString> &saves)
|
||||||
msv.isUnreadable = 1;
|
msv.isUnreadable = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
FinishSavegameRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadSaveGameHeaders_Internal(void)
|
static void ReadSaveGameHeaders_Internal(void)
|
||||||
|
@ -315,22 +314,21 @@ void ReadSaveGameHeaders(void)
|
||||||
|
|
||||||
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
||||||
{
|
{
|
||||||
|
FileReader ssfil;
|
||||||
auto fil = OpenSavegame(fn);
|
auto fil = OpenSavegame(fn);
|
||||||
if (!fil.isOpen())
|
if (!fil)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int32_t i = sv_loadheader(fil, 0, saveh);
|
int32_t i = sv_loadheader(*fil, 0, saveh);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
goto corrupt;
|
goto corrupt;
|
||||||
|
|
||||||
int32_t screenshotofs;
|
ssfil = ReadSavegameChunk("screenshot.dat");
|
||||||
if (fil.Read(&screenshotofs, 4) != 4)
|
|
||||||
goto corrupt;
|
|
||||||
|
|
||||||
TileFiles.tileCreate(TILE_LOADSHOT, 200, 320);
|
TileFiles.tileCreate(TILE_LOADSHOT, 200, 320);
|
||||||
if (screenshotofs)
|
if (ssfil.isOpen())
|
||||||
{
|
{
|
||||||
if (fil.Read(tileData(TILE_LOADSHOT), 320 * 200) != 320 * 200)
|
if (ssfil.Read(tileData(TILE_LOADSHOT), 320 * 200) != 320 * 200)
|
||||||
{
|
{
|
||||||
OSD_Printf("G_LoadSaveHeaderNew(): failed reading screenshot in \"%s\"\n", fn);
|
OSD_Printf("G_LoadSaveHeaderNew(): failed reading screenshot in \"%s\"\n", fn);
|
||||||
goto corrupt;
|
goto corrupt;
|
||||||
|
@ -340,11 +338,16 @@ int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
||||||
{
|
{
|
||||||
Bmemset(tileData(TILE_LOADSHOT), 0, 320*200);
|
Bmemset(tileData(TILE_LOADSHOT), 0, 320*200);
|
||||||
}
|
}
|
||||||
|
ssfil.Close();
|
||||||
tileInvalidate(TILE_LOADSHOT, 0, 255);
|
tileInvalidate(TILE_LOADSHOT, 0, 255);
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
corrupt:
|
corrupt:
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,11 +368,11 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
int skill = -1;
|
int skill = -1;
|
||||||
|
|
||||||
auto fil = OpenSavegame(sv.path);
|
auto fil = OpenSavegame(sv.path);
|
||||||
|
if (!fil) return -1;
|
||||||
|
|
||||||
if (fil.isOpen())
|
|
||||||
{
|
{
|
||||||
savehead_t h;
|
savehead_t h;
|
||||||
int status = sv_loadheader(fil, 0, &h);
|
int status = sv_loadheader(*fil, 0, &h);
|
||||||
if (status >= 0)
|
if (status >= 0)
|
||||||
{
|
{
|
||||||
volume = h.volnum;
|
volume = h.volnum;
|
||||||
|
@ -378,17 +381,21 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FStringf extfn("%s.ext", sv.path);
|
auto extfil = ReadSavegameChunk("ext.json");
|
||||||
auto extfil = fopenFileReader(extfn, 0);
|
|
||||||
if (!extfil.isOpen())
|
if (!extfil.isOpen())
|
||||||
{
|
{
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto text = extfil.ReadPadded(1);
|
auto text = extfil.ReadPadded(1);
|
||||||
|
extfil.Close();
|
||||||
|
|
||||||
if (text.Size() == 0)
|
if (text.Size() == 0)
|
||||||
{
|
{
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,6 +412,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
if (volume == -1 || level == -1 || skill == -1)
|
if (volume == -1 || level == -1 || skill == -1)
|
||||||
{
|
{
|
||||||
sjson_destroy_context(ctx);
|
sjson_destroy_context(ctx);
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,6 +426,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps);
|
P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps);
|
||||||
|
|
||||||
sjson_destroy_context(ctx);
|
sjson_destroy_context(ctx);
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,18 +599,20 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
|
|
||||||
VM_OnEvent(EVENT_LOADGAME, g_player[screenpeek].ps->i, screenpeek);
|
VM_OnEvent(EVENT_LOADGAME, g_player[screenpeek].ps->i, screenpeek);
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fil = OpenSavegame(sv.path);
|
auto fil = OpenSavegame(sv.path);
|
||||||
|
|
||||||
if (!fil.isOpen())
|
if (!fil)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ready2send = 0;
|
ready2send = 0;
|
||||||
|
|
||||||
savehead_t h;
|
savehead_t h;
|
||||||
int status = sv_loadheader(fil, 0, &h);
|
int status = sv_loadheader(*fil, 0, &h);
|
||||||
|
|
||||||
if (status < 0 || h.numplayers != ud.multimode)
|
if (status < 0 || h.numplayers != ud.multimode)
|
||||||
{
|
{
|
||||||
|
@ -611,6 +624,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
ototalclock = totalclock;
|
ototalclock = totalclock;
|
||||||
ready2send = 1;
|
ready2send = 1;
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +680,7 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
|
|
||||||
if (status == 2)
|
if (status == 2)
|
||||||
G_NewGame_EnterLevel();
|
G_NewGame_EnterLevel();
|
||||||
else if ((status = sv_loadsnapshot(fil, 0, &h)) || !ReadStatistics(fil) || !SECRET_Load(fil)) // read the rest...
|
else if ((status = sv_loadsnapshot(*fil, 0, &h)) || !ReadStatistics() || !SECRET_Load()) // read the rest...
|
||||||
{
|
{
|
||||||
// in theory, we could load into an initial dump first and trivially
|
// in theory, we could load into an initial dump first and trivially
|
||||||
// recover if things go wrong...
|
// recover if things go wrong...
|
||||||
|
@ -676,6 +691,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
sv_postudload(); // ud.m_XXX = ud.XXX
|
sv_postudload(); // ud.m_XXX = ud.XXX
|
||||||
VM_OnEvent(EVENT_LOADGAME, g_player[screenpeek].ps->i, screenpeek);
|
VM_OnEvent(EVENT_LOADGAME, g_player[screenpeek].ps->i, screenpeek);
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,8 +735,6 @@ void G_DeleteSave(savebrief_t const & sv)
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(temp);
|
remove(temp);
|
||||||
Bstrcat(temp, ".ext");
|
|
||||||
remove(temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void G_DeleteOldSaves(void)
|
void G_DeleteOldSaves(void)
|
||||||
|
@ -768,7 +783,8 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
if (sv.isValid())
|
if (sv.isValid())
|
||||||
{
|
{
|
||||||
fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path);
|
fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path);
|
||||||
fil = FileWriter::Open(fn);
|
OpenSaveGameForWrite(fn);
|
||||||
|
fil = WriteSavegameChunk("snapshot.dat");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -778,6 +794,13 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
auto fnp = fn.LockBuffer();
|
auto fnp = fn.LockBuffer();
|
||||||
char* zeros = fnp + (fn.Len() - 8);
|
char* zeros = fnp + (fn.Len() - 8);
|
||||||
fil = savecounter.opennextfile(fnp, zeros);
|
fil = savecounter.opennextfile(fnp, zeros);
|
||||||
|
if (fil)
|
||||||
|
{
|
||||||
|
delete fil;
|
||||||
|
remove(fnp);
|
||||||
|
OpenSaveGameForWrite(fnp);
|
||||||
|
fil = WriteSavegameChunk("snapshot.dat");
|
||||||
|
}
|
||||||
fn.UnlockBuffer();
|
fn.UnlockBuffer();
|
||||||
savecounter.count++;
|
savecounter.count++;
|
||||||
// don't copy the mod dir into sv.path
|
// don't copy the mod dir into sv.path
|
||||||
|
@ -797,9 +820,8 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fil->Write("DEMOLITION_ED", 13);
|
WriteSavegameChunk("DEMOLITION_ED");
|
||||||
|
auto& fw = *fil;
|
||||||
CompressedFileWriter fw(fil, true);
|
|
||||||
|
|
||||||
sv.isExt = 0;
|
sv.isExt = 0;
|
||||||
|
|
||||||
|
@ -812,10 +834,11 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
|
|
||||||
// SAVE!
|
// SAVE!
|
||||||
sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave);
|
sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave);
|
||||||
SaveStatistics(fw);
|
SaveStatistics();
|
||||||
SECRET_Save(fw);
|
SECRET_Save();
|
||||||
|
|
||||||
fw.Close();
|
fw.Close();
|
||||||
|
FinishSavegameWrite();
|
||||||
|
|
||||||
if (!g_netServer && ud.multimode < 2)
|
if (!g_netServer && ud.multimode < 2)
|
||||||
{
|
{
|
||||||
|
@ -1686,6 +1709,8 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
{
|
{
|
||||||
// savegame
|
// savegame
|
||||||
Bstrncpyz(h.savename, name, sizeof(h.savename));
|
Bstrncpyz(h.savename, name, sizeof(h.savename));
|
||||||
|
auto fw = WriteSavegameChunk("header.dat");
|
||||||
|
fw->Write(&h, sizeof(savehead_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1698,27 +1723,17 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
if (t>=0 && (st = localtime(&t)))
|
if (t>=0 && (st = localtime(&t)))
|
||||||
Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
|
Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
|
||||||
st->tm_year+1900, st->tm_mon+1, st->tm_mday, GetGitDescription());
|
st->tm_year+1900, st->tm_mon+1, st->tm_mday, GetGitDescription());
|
||||||
|
fil.Write(&h, sizeof(savehead_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// write header
|
// write header
|
||||||
fil.Write(&h, sizeof(savehead_t));
|
|
||||||
|
|
||||||
// for savegames, the file offset after the screenshot goes here;
|
|
||||||
// for demos, we keep it 0 to signify that we didn't save one
|
|
||||||
if (spot >= 0 && tileData(TILE_SAVESHOT))
|
if (spot >= 0 && tileData(TILE_SAVESHOT))
|
||||||
{
|
{
|
||||||
|
auto fw = WriteSavegameChunk("screenshot.dat");
|
||||||
|
fw->Write(tileData(TILE_SAVESHOT), 320*200);
|
||||||
|
|
||||||
int v = 64000;
|
|
||||||
fil.Write(&v, 4);
|
|
||||||
// write the screenshot compressed
|
|
||||||
fil.Write(tileData(TILE_SAVESHOT), 320*200);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int v = 0;
|
|
||||||
fil.Write(&v, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1745,11 +1760,18 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
|
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
|
||||||
int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h)
|
int32_t sv_loadheader(FileReader &fill, int32_t spot, savehead_t *h)
|
||||||
{
|
{
|
||||||
|
FileReader filc;
|
||||||
|
FileReader* filp = &fill;
|
||||||
int32_t havedemo = (spot < 0);
|
int32_t havedemo = (spot < 0);
|
||||||
|
if (!havedemo)
|
||||||
|
{
|
||||||
|
filc = ReadSavegameChunk("header.dat");
|
||||||
|
filp = &filc;
|
||||||
|
}
|
||||||
|
|
||||||
if (fil.Read(h, sizeof(savehead_t)) != sizeof(savehead_t))
|
if (filp->Read(h, sizeof(savehead_t)) != sizeof(savehead_t))
|
||||||
{
|
{
|
||||||
OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
|
OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
|
||||||
Bmemset(h->headerstr, 0, sizeof(h->headerstr));
|
Bmemset(h->headerstr, 0, sizeof(h->headerstr));
|
||||||
|
@ -1825,20 +1847,6 @@ int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h)
|
||||||
OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
|
OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fil.Read(&i, 4) != 4)
|
|
||||||
{
|
|
||||||
OSD_Printf("sv_snapshot: couldn't read 4 bytes after header.\n");
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
if (fil.Seek(i, FileReader::SeekCur) < 0)
|
|
||||||
{
|
|
||||||
OSD_Printf("sv_snapshot: failed skipping over the screenshot.\n");
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
savegame_comprthres = h->comprthres;
|
savegame_comprthres = h->comprthres;
|
||||||
|
|
||||||
if (spot >= 0)
|
if (spot >= 0)
|
||||||
|
|
|
@ -30,7 +30,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "statistics.h"
|
#include "statistics.h"
|
||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
|
#include "savegamehelp.h"
|
||||||
BEGIN_RR_NS
|
BEGIN_RR_NS
|
||||||
|
|
||||||
|
|
||||||
|
@ -149,28 +149,25 @@ uint16_t g_nummenusaves;
|
||||||
static menusave_t * g_internalsaves;
|
static menusave_t * g_internalsaves;
|
||||||
static uint16_t g_numinternalsaves;
|
static uint16_t g_numinternalsaves;
|
||||||
|
|
||||||
static FileReader OpenSavegame(const char *fn)
|
static FileReader *OpenSavegame(const char *fn)
|
||||||
{
|
{
|
||||||
auto file = fopenFileReader(fn, 0);
|
if (!OpenSaveGameForRead(fn))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto file = ReadSavegameChunk("DEMOLITION_RN");
|
||||||
if (!file.isOpen())
|
if (!file.isOpen())
|
||||||
return file;
|
|
||||||
|
|
||||||
char buffer[13];
|
|
||||||
file.Read(buffer, 13);
|
|
||||||
if (memcmp(buffer, "DEMOLITION_RN", 13))
|
|
||||||
return FileReader();
|
|
||||||
|
|
||||||
FileReader fr;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
fr.OpenDecompressor(file, file.GetLength()-13, METHOD_DEFLATE|METHOD_TRANSFEROWNER, false, nullptr);
|
FinishSavegameRead();
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
catch(std::runtime_error & err)
|
file = ReadSavegameChunk("snapshot.dat");
|
||||||
|
if (!file.isOpen())
|
||||||
{
|
{
|
||||||
Printf("%s: %s\n", fn, err.what());
|
FinishSavegameRead();
|
||||||
return FileReader();
|
return nullptr;
|
||||||
}
|
}
|
||||||
return fr;
|
return new FileReader(std::move(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
|
static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
|
||||||
|
@ -179,14 +176,14 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
|
||||||
|
|
||||||
for (FString &save : saves)
|
for (FString &save : saves)
|
||||||
{
|
{
|
||||||
char const* fn = save;
|
auto fil = OpenSavegame(save);
|
||||||
auto fil = OpenSavegame(fn);
|
if (!fil)
|
||||||
if (!fil.isOpen())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
menusave_t & msv = g_internalsaves[g_numinternalsaves];
|
menusave_t & msv = g_internalsaves[g_numinternalsaves];
|
||||||
|
|
||||||
int32_t k = sv_loadheader(fil, 0, &h);
|
int32_t k = sv_loadheader(*fil, 0, &h);
|
||||||
|
delete fil;
|
||||||
if (k)
|
if (k)
|
||||||
{
|
{
|
||||||
if (k < 0)
|
if (k < 0)
|
||||||
|
@ -198,7 +195,7 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
|
||||||
|
|
||||||
msv.isAutoSave = h.isAutoSave();
|
msv.isAutoSave = h.isAutoSave();
|
||||||
|
|
||||||
strncpy(msv.brief.path, fn, ARRAY_SIZE(msv.brief.path));
|
strncpy(msv.brief.path, save.GetChars(), ARRAY_SIZE(msv.brief.path));
|
||||||
++g_numinternalsaves;
|
++g_numinternalsaves;
|
||||||
|
|
||||||
if (k >= 0 && h.savename[0] != '\0')
|
if (k >= 0 && h.savename[0] != '\0')
|
||||||
|
@ -208,6 +205,7 @@ static void ReadSaveGameHeaders_CACHE1D(TArray<FString>& saves)
|
||||||
else
|
else
|
||||||
msv.isUnreadable = 1;
|
msv.isUnreadable = 1;
|
||||||
}
|
}
|
||||||
|
FinishSavegameRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadSaveGameHeaders_Internal(void)
|
static void ReadSaveGameHeaders_Internal(void)
|
||||||
|
@ -300,22 +298,21 @@ void ReadSaveGameHeaders(void)
|
||||||
|
|
||||||
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
||||||
{
|
{
|
||||||
|
FileReader ssfil;
|
||||||
auto fil = OpenSavegame(fn);
|
auto fil = OpenSavegame(fn);
|
||||||
if (!fil.isOpen())
|
if (!fil)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int32_t i = sv_loadheader(fil, 0, saveh);
|
int32_t i = sv_loadheader(*fil, 0, saveh);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
goto corrupt;
|
goto corrupt;
|
||||||
|
|
||||||
int32_t screenshotofs;
|
ssfil = ReadSavegameChunk("screenshot.dat");
|
||||||
if (fil.Read(&screenshotofs, 4) != 4)
|
|
||||||
goto corrupt;
|
|
||||||
|
|
||||||
TileFiles.tileCreate(TILE_LOADSHOT, 200, 320);
|
TileFiles.tileCreate(TILE_LOADSHOT, 200, 320);
|
||||||
if (screenshotofs)
|
if (ssfil.isOpen())
|
||||||
{
|
{
|
||||||
if (fil.Read(tileData(TILE_LOADSHOT), 320 * 200) != 320 * 200)
|
if (ssfil.Read(tileData(TILE_LOADSHOT), 320 * 200) != 320 * 200)
|
||||||
{
|
{
|
||||||
OSD_Printf("G_LoadSaveHeaderNew(): failed reading screenshot in \"%s\"\n", fn);
|
OSD_Printf("G_LoadSaveHeaderNew(): failed reading screenshot in \"%s\"\n", fn);
|
||||||
goto corrupt;
|
goto corrupt;
|
||||||
|
@ -325,11 +322,16 @@ int32_t G_LoadSaveHeaderNew(char const *fn, savehead_t *saveh)
|
||||||
{
|
{
|
||||||
Bmemset(tileData(TILE_LOADSHOT), 0, 320*200);
|
Bmemset(tileData(TILE_LOADSHOT), 0, 320*200);
|
||||||
}
|
}
|
||||||
|
ssfil.Close();
|
||||||
tileInvalidate(TILE_LOADSHOT, 0, 255);
|
tileInvalidate(TILE_LOADSHOT, 0, 255);
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
corrupt:
|
corrupt:
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,13 +346,13 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
{
|
{
|
||||||
auto fil = OpenSavegame(sv.path);
|
auto fil = OpenSavegame(sv.path);
|
||||||
|
|
||||||
if (!fil.isOpen())
|
if (!fil)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ready2send = 0;
|
ready2send = 0;
|
||||||
|
|
||||||
savehead_t h;
|
savehead_t h;
|
||||||
int status = sv_loadheader(fil, 0, &h);
|
int status = sv_loadheader(*fil, 0, &h);
|
||||||
|
|
||||||
if (status < 0 || h.numplayers != ud.multimode)
|
if (status < 0 || h.numplayers != ud.multimode)
|
||||||
{
|
{
|
||||||
|
@ -362,6 +364,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
ototalclock = totalclock;
|
ototalclock = totalclock;
|
||||||
ready2send = 1;
|
ready2send = 1;
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +415,7 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
|
|
||||||
if (status == 2)
|
if (status == 2)
|
||||||
G_NewGame_EnterLevel();
|
G_NewGame_EnterLevel();
|
||||||
else if ((status = sv_loadsnapshot(fil, 0, &h)) || !ReadStatistics(fil) || !SECRET_Load(fil)) // read the rest...
|
else if ((status = sv_loadsnapshot(*fil, 0, &h)) || !ReadStatistics() || !SECRET_Load()) // read the rest...
|
||||||
{
|
{
|
||||||
// in theory, we could load into an initial dump first and trivially
|
// in theory, we could load into an initial dump first and trivially
|
||||||
// recover if things go wrong...
|
// recover if things go wrong...
|
||||||
|
@ -421,6 +425,8 @@ int32_t G_LoadPlayer(savebrief_t & sv)
|
||||||
|
|
||||||
sv_postudload(); // ud.m_XXX = ud.XXX
|
sv_postudload(); // ud.m_XXX = ud.XXX
|
||||||
|
|
||||||
|
delete fil;
|
||||||
|
FinishSavegameRead();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +471,7 @@ void G_DeleteSave(savebrief_t const & sv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink(temp);
|
remove(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void G_DeleteOldSaves(void)
|
void G_DeleteOldSaves(void)
|
||||||
|
@ -514,7 +520,8 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
if (sv.isValid())
|
if (sv.isValid())
|
||||||
{
|
{
|
||||||
fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path);
|
fn.Format("%s%s", M_GetSavegamesPath().GetChars(), sv.path);
|
||||||
fil = FileWriter::Open(fn);
|
OpenSaveGameForWrite(fn);
|
||||||
|
fil = WriteSavegameChunk("snapshot.dat");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -524,6 +531,13 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
auto fnp = fn.LockBuffer();
|
auto fnp = fn.LockBuffer();
|
||||||
char* zeros = fnp + (fn.Len() - 8);
|
char* zeros = fnp + (fn.Len() - 8);
|
||||||
fil = savecounter.opennextfile(fnp, zeros);
|
fil = savecounter.opennextfile(fnp, zeros);
|
||||||
|
if (fil)
|
||||||
|
{
|
||||||
|
delete fil;
|
||||||
|
remove(fnp);
|
||||||
|
OpenSaveGameForWrite(fnp);
|
||||||
|
fil = WriteSavegameChunk("snapshot.dat");
|
||||||
|
}
|
||||||
fn.UnlockBuffer();
|
fn.UnlockBuffer();
|
||||||
savecounter.count++;
|
savecounter.count++;
|
||||||
// don't copy the mod dir into sv.path
|
// don't copy the mod dir into sv.path
|
||||||
|
@ -543,8 +557,9 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fil->Write("DEMOLITION_RN", 13);
|
WriteSavegameChunk("DEMOLITION_RN");
|
||||||
CompressedFileWriter fw(fil, true);
|
auto& fw = *fil;
|
||||||
|
//CompressedFileWriter fw(fil, true);
|
||||||
|
|
||||||
// temporary hack
|
// temporary hack
|
||||||
ud.user_map = G_HaveUserMap();
|
ud.user_map = G_HaveUserMap();
|
||||||
|
@ -552,10 +567,11 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave)
|
||||||
|
|
||||||
// SAVE!
|
// SAVE!
|
||||||
sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave);
|
sv_saveandmakesnapshot(fw, sv.name, 0, 0, 0, 0, isAutoSave);
|
||||||
SaveStatistics(fw);
|
SaveStatistics();
|
||||||
SECRET_Save(fw);
|
SECRET_Save();
|
||||||
|
|
||||||
fw.Close();
|
fw.Close();
|
||||||
|
FinishSavegameWrite();
|
||||||
|
|
||||||
if (!g_netServer && ud.multimode < 2)
|
if (!g_netServer && ud.multimode < 2)
|
||||||
{
|
{
|
||||||
|
@ -1367,6 +1383,8 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
{
|
{
|
||||||
// savegame
|
// savegame
|
||||||
Bstrncpyz(h.savename, name, sizeof(h.savename));
|
Bstrncpyz(h.savename, name, sizeof(h.savename));
|
||||||
|
auto fw = WriteSavegameChunk("header.dat");
|
||||||
|
fw->Write(&h, sizeof(savehead_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1379,27 +1397,17 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
if (t>=0 && (st = localtime(&t)))
|
if (t>=0 && (st = localtime(&t)))
|
||||||
Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
|
Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
|
||||||
st->tm_year+1900, st->tm_mon+1, st->tm_mday, GetGitDescription());
|
st->tm_year+1900, st->tm_mon+1, st->tm_mday, GetGitDescription());
|
||||||
|
fil.Write(&h, sizeof(savehead_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// write header
|
// write header
|
||||||
fil.Write(&h, sizeof(savehead_t));
|
|
||||||
|
|
||||||
// for savegames, the file offset after the screenshot goes here;
|
|
||||||
// for demos, we keep it 0 to signify that we didn't save one
|
|
||||||
if (spot >= 0 && tileData(TILE_SAVESHOT))
|
if (spot >= 0 && tileData(TILE_SAVESHOT))
|
||||||
{
|
{
|
||||||
|
auto fw = WriteSavegameChunk("screenshot.dat");
|
||||||
|
fw->Write(tileData(TILE_SAVESHOT), 320*200);
|
||||||
|
|
||||||
int v = 64000;
|
|
||||||
fil.Write(&v, 4);
|
|
||||||
// write the screenshot compressed
|
|
||||||
fil.Write(tileData(TILE_SAVESHOT), 320*200);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int v = 0;
|
|
||||||
fil.Write(&v, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1426,11 +1434,18 @@ int32_t sv_saveandmakesnapshot(FileWriter &fil, char const *name, int8_t spot, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
|
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
|
||||||
int32_t sv_loadheader(FileReader &fil, int32_t spot, savehead_t *h)
|
int32_t sv_loadheader(FileReader &fill, int32_t spot, savehead_t *h)
|
||||||
{
|
{
|
||||||
|
FileReader filc;
|
||||||
|
FileReader* filp = &fill;
|
||||||
int32_t havedemo = (spot < 0);
|
int32_t havedemo = (spot < 0);
|
||||||
|
if (!havedemo)
|
||||||
|
{
|
||||||
|
filc = ReadSavegameChunk("header.dat");
|
||||||
|
filp = &filc;
|
||||||
|
}
|
||||||
|
|
||||||
if (fil.Read(h, sizeof(savehead_t)) != sizeof(savehead_t))
|
if (filp->Read(h, sizeof(savehead_t)) != sizeof(savehead_t))
|
||||||
{
|
{
|
||||||
OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
|
OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
|
||||||
Bmemset(h->headerstr, 0, sizeof(h->headerstr));
|
Bmemset(h->headerstr, 0, sizeof(h->headerstr));
|
||||||
|
@ -1506,20 +1521,6 @@ int32_t sv_loadsnapshot(FileReader &fil, int32_t spot, savehead_t *h)
|
||||||
OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
|
OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fil.Read(&i, 4) != 4)
|
|
||||||
{
|
|
||||||
OSD_Printf("sv_snapshot: couldn't read 4 bytes after header.\n");
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
if (fil.Seek(i, FileReader::SeekCur) < 0)
|
|
||||||
{
|
|
||||||
OSD_Printf("sv_snapshot: failed skipping over the screenshot.\n");
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
savegame_comprthres = h->comprthres;
|
savegame_comprthres = h->comprthres;
|
||||||
|
|
||||||
if (spot >= 0)
|
if (spot >= 0)
|
||||||
|
|
Loading…
Reference in a new issue