- transitioned all JSON-based savegame code to FSerializer and removed sjson.

Now everything is in place to transition the rest of the savegame code as well.
This commit is contained in:
Christoph Oelckers 2020-02-23 14:03:03 +01:00
parent 1b6b43291b
commit 985e441d80
21 changed files with 212 additions and 2821 deletions

View file

@ -653,7 +653,6 @@ set (PCH_SOURCES
glbackend/hw_draw2d.cpp glbackend/hw_draw2d.cpp
thirdparty/src/base64.cpp thirdparty/src/base64.cpp
thirdparty/src/sjson.cpp
thirdparty/src/fix16.cpp thirdparty/src/fix16.cpp
thirdparty/src/fix16_str.cpp thirdparty/src/fix16_str.cpp
thirdparty/src/md4.cpp thirdparty/src/md4.cpp

View file

@ -61,6 +61,7 @@ void CompositeSavegameWriter::AddCompressedElement(const char* filename, FCompre
subbuffers.Push(buffer); subbuffers.Push(buffer);
buffer = {}; buffer = {};
subfiles.Push(nullptr); subfiles.Push(nullptr);
isCompressed.Push(true);
} }
FCompressedBuffer CompositeSavegameWriter::CompressElement(BufferWriter *bw, bool compress) FCompressedBuffer CompositeSavegameWriter::CompressElement(BufferWriter *bw, bool compress)

View file

@ -44,7 +44,6 @@
#include "v_draw.h" #include "v_draw.h"
#include "files.h" #include "files.h"
#include "resourcefile.h" #include "resourcefile.h"
#include "sjson.h"
#include "savegamehelp.h" #include "savegamehelp.h"
#include "i_specialpaths.h" #include "i_specialpaths.h"
#include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up. #include "../../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up.

View file

@ -44,7 +44,6 @@
#include "v_draw.h" #include "v_draw.h"
#include "files.h" #include "files.h"
#include "resourcefile.h" #include "resourcefile.h"
#include "sjson.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "files.h" #include "files.h"
#include "savegamehelp.h" #include "savegamehelp.h"
@ -52,6 +51,7 @@
#include "c_dispatch.h" #include "c_dispatch.h"
#include "i_system.h" #include "i_system.h"
#include "build.h" #include "build.h"
#include "serializer.h"
FSavegameManager savegameManager; FSavegameManager savegameManager;
@ -378,23 +378,24 @@ unsigned FSavegameManager::ExtractSaveData(int index)
// this should not happen because the file has already been verified. // this should not happen because the file has already been verified.
return index; return index;
} }
auto fr = info->NewReader();
auto data = fr.ReadPadded(1); void* data = info->Get();
fr.Close(); FSerializer arc;
sjson_context* ctx = sjson_create_context(0, 0, NULL); if (!arc.OpenReader((const char*)data, info->LumpSize))
if (ctx)
{ {
sjson_node* root = sjson_decode(ctx, (const char*)data.Data()); return index;
}
FString comment, fcomment, ncomment, mtime;
arc("Creation Time", comment)
("Map Label", fcomment)
("Map Name", ncomment)
("Map Time", mtime);
FString comment = sjson_get_string(root, "Creation Time", "");
FString fcomment = sjson_get_string(root, "Map Label", "");
FString ncomment = sjson_get_string(root, "Map Name", "");
FString mtime = sjson_get_string(root, "Map Time", "");
comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars()); comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars());
SaveCommentString = comment; SaveCommentString = comment;
// Extract pic (todo: let the renderer write a proper PNG file instead of a raw canvas dunp of the software renderer - and make it work for all games.)
FResourceLump *pic = resf->FindLump("savepic.png"); FResourceLump *pic = resf->FindLump("savepic.png");
if (pic != nullptr) if (pic != nullptr)
{ {
@ -421,8 +422,6 @@ unsigned FSavegameManager::ExtractSaveData(int index)
} }
} }
} }
sjson_destroy_context(ctx);
}
delete resf; delete resf;
} }
return index; return index;

View file

@ -50,10 +50,9 @@
#include "c_dispatch.h" #include "c_dispatch.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "filereadermusicinterface.h" #include "filereadermusicinterface.h"
#include "savegamehelp.h"
#include "sjson.h"
#include "v_text.h" #include "v_text.h"
#include "mapinfo.h" #include "mapinfo.h"
#include "serializer.h"
MusPlayingInfo mus_playing; MusPlayingInfo mus_playing;
MusicAliasMap MusicAliases; MusicAliasMap MusicAliases;
@ -707,61 +706,27 @@ void Mus_SetPaused(bool on)
else S_ResumeMusic(); else S_ResumeMusic();
} }
void MUS_Save() void Mus_Serialize(FSerializer &arc)
{
if (arc.BeginObject("music"))
{
if (arc.isWriting())
{ {
FString music = mus_playing.name; FString music = mus_playing.name;
if (music.IsEmpty()) music = mus_playing.LastSong; if (music.IsEmpty()) music = mus_playing.LastSong;
sjson_context* ctx = sjson_create_context(0, 0, NULL); arc.AddString("music", music);
if (!ctx)
{
return;
} }
sjson_node* root = sjson_mkobject(ctx); else arc("music", mus_playing.LastSong);
sjson_put_string(ctx, root, "music", music);
sjson_put_int(ctx, root, "baseorder", mus_playing.baseorder);
sjson_put_bool(ctx, root, "loop", mus_playing.loop);
char* encoded = sjson_stringify(ctx, root, " "); arc("baseorder", mus_playing.baseorder)
("loop", mus_playing.loop)
.EndObject();
FileWriter* fil = WriteSavegameChunk("music.json"); // this is to prevent scripts from resetting the music after it has been loaded from the savegame.
if (!fil) if (arc.isReading()) mus_blocked = true;
{ // Actual music resuming cannot be performed here, it must be done in the game code.
sjson_destroy_context(ctx);
return;
} }
fil->Write(encoded, strlen(encoded));
sjson_free_string(ctx, encoded);
sjson_destroy_context(ctx);
}
bool MUS_Restore()
{
auto fil = ReadSavegameChunk("music.json");
if (!fil.isOpen())
{
return false;
}
auto text = fil.ReadPadded(1);
fil.Close();
if (text.Size() == 0)
{
return false;
}
sjson_context* ctx = sjson_create_context(0, 0, NULL);
sjson_node* root = sjson_decode(ctx, (const char*)text.Data());
mus_playing.LastSong = sjson_get_string(root, "music", "");
mus_playing.baseorder = sjson_get_int(root, "baseorder", 0);
mus_playing.loop = sjson_get_bool(root, "loop", true);
sjson_destroy_context(ctx);
mus_blocked = true; // this is to prevent scripts from resetting the music after it has been loaded from the savegame.
return true;
} }
void Mus_ResumeSaved() void Mus_ResumeSaved()

View file

@ -71,9 +71,6 @@ extern MusPlayingInfo mus_playing;
extern float relative_volume, saved_relative_volume; extern float relative_volume, saved_relative_volume;
void MUS_Save();
bool MUS_Restore();
// Note for later when the OPL player is ported. // Note for later when the OPL player is ported.
// DN3D and related games use "d3dtimbr.tmb" // DN3D and related games use "d3dtimbr.tmb"

View file

@ -12,3 +12,5 @@ void Mus_Fade(double seconds);
void Mus_SetPaused(bool on); void Mus_SetPaused(bool on);
void Mus_ResumeSaved(); void Mus_ResumeSaved();
FString G_SetupFilenameBasedMusic(const char* fileName, const char *defaultfn); FString G_SetupFilenameBasedMusic(const char* fileName, const char *defaultfn);
class FSerializer;
void Mus_Serialize(FSerializer& arc);

View file

@ -12,6 +12,8 @@ enum
MAXQUOTES = 16384, MAXQUOTES = 16384,
}; };
class FSerializer;
class Quotes class Quotes
{ {
FString quotes[MAXQUOTES]; FString quotes[MAXQUOTES];
@ -55,11 +57,9 @@ public:
} }
void AppendQuote(int dst, int src, int len = -1); void AppendQuote(int dst, int src, int len = -1);
void AppendExQuote(int dst, int src, int len = -1);
void FormatQuote(int dst, const char* fmt, ...); void FormatQuote(int dst, const char* fmt, ...);
void Substitute(int num, const char* text, const char* replc); void Substitute(int num, const char* text, const char* replc);
void ReadFromSavegame(); void Serialize(FSerializer &arc);
void WriteToSavegame();
}; };
extern Quotes quoteMgr; extern Quotes quoteMgr;

View file

@ -37,7 +37,8 @@
#include "quotemgr.h" #include "quotemgr.h"
#include "savegamehelp.h" #include "savegamehelp.h"
#include "sjson.h" #include "serializer.h"
#include "printf.h"
void Quotes::MakeStringLabel(FString &quote) void Quotes::MakeStringLabel(FString &quote)
@ -72,15 +73,6 @@ void Quotes::AppendQuote(int dst, int src, int len)
else quotes[dst] += FString(GStrings.localize(quotes[src]), len); else quotes[dst] += FString(GStrings.localize(quotes[src]), len);
} }
void Quotes::AppendExQuote(int dst, int src, int len)
{
// This needs to apply the localization because the combined string is not localizable anymore.
if (quotes[dst][0] == '$') quotes[dst] = GStrings.localize(quotes[dst]);
if (len < 0) quotes[dst] << GStrings.localize(exquotes[src]);
else quotes[dst] += FString(GStrings.localize(exquotes[src]), len);
}
void Quotes::FormatQuote(int dst, const char* fmt, ...) void Quotes::FormatQuote(int dst, const char* fmt, ...)
{ {
va_list ap; va_list ap;
@ -95,88 +87,20 @@ void Quotes::Substitute(int dst, const char* text, const char* replc)
} }
void Quotes::ReadFromSavegame() void Quotes::Serialize(FSerializer &arc)
{ {
for (auto& q : quotes) q = ""; // This only saves the regular quotes. The ExQuotes array is immutable once initialized.
for (auto& q : exquotes) q = ""; if (arc.BeginObject("quotes"))
auto fil = ReadSavegameChunk("quotes.json");
if (!fil.isOpen())
{ {
return; for (int i = 0; i < MAXQUOTES; i++)
{
char buf[10];
mysnprintf(buf, 10, "%d", i);
FString nulstr;
arc(buf, quotes[i], nulstr);
} }
arc.EndObject();
auto text = fil.ReadPadded(1);
fil.Close();
if (text.Size() == 0)
{
return;
} }
sjson_context* ctx = sjson_create_context(0, 0, NULL);
sjson_node* root = sjson_decode(ctx, (const char*)text.Data());
auto qs = sjson_find_member(root, "quotes");
auto xs = sjson_find_member(root, "exquotes");
sjson_node* q;
sjson_foreach(q, qs)
{
int index = (int)strtoll(q->key, nullptr, 10);
quotes[index] = q->string_;
}
sjson_foreach(q, xs)
{
int index = (int)strtoll(q->key, nullptr, 10);
exquotes[index] = q->string_;
}
sjson_destroy_context(ctx);
}
void Quotes::WriteToSavegame()
{
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (!ctx)
{
return;
}
sjson_node* root = sjson_mkobject(ctx);
sjson_node* qs = sjson_mkobject(ctx);
sjson_node* xs = sjson_mkobject(ctx);
for (unsigned i = 0; i < MAXQUOTES; i++)
{
if (quotes[i].IsNotEmpty())
{
char buff[10];
snprintf(buff, 10, "%d", i);
sjson_append_member(ctx, qs, buff, sjson_mkstring(ctx, quotes[i]));
}
if (exquotes[i].IsNotEmpty())
{
char buff[10];
snprintf(buff, 10, "%d", i);
sjson_append_member(ctx, xs, buff, sjson_mkstring(ctx, exquotes[i]));
}
}
sjson_append_member(ctx, root, "quotes", qs);
sjson_append_member(ctx, root, "exquotes", xs);
char* encoded = sjson_stringify(ctx, root, " ");
FileWriter* fil = WriteSavegameChunk("quotes.json");
if (!fil)
{
sjson_destroy_context(ctx);
return;
}
fil->Write(encoded, strlen(encoded));
sjson_free_string(ctx, encoded);
sjson_destroy_context(ctx);
return;
} }
Quotes quoteMgr; Quotes quoteMgr;

View file

@ -35,7 +35,6 @@
#include "compositesaveame.h" #include "compositesaveame.h"
#include "savegamehelp.h" #include "savegamehelp.h"
#include "sjson.h"
#include "baselayer.h" #include "baselayer.h"
#include "gstrings.h" #include "gstrings.h"
#include "i_specialpaths.h" #include "i_specialpaths.h"
@ -43,7 +42,6 @@
#include "filesystem/filesystem.h" #include "filesystem/filesystem.h"
#include "statistics.h" #include "statistics.h"
#include "secrets.h" #include "secrets.h"
#include "s_music.h"
#include "quotemgr.h" #include "quotemgr.h"
#include "mapinfo.h" #include "mapinfo.h"
#include "v_video.h" #include "v_video.h"
@ -51,6 +49,7 @@
#include "m_argv.h" #include "m_argv.h"
#include "serializer.h" #include "serializer.h"
#include "version.h" #include "version.h"
#include "z_music.h"
static CompositeSavegameWriter savewriter; static CompositeSavegameWriter savewriter;
static FResourceFile *savereader; static FResourceFile *savereader;
@ -59,6 +58,20 @@ void SaveEngineState();
CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//=============================================================================
//
//
//
//=============================================================================
static void SerializeSession(FSerializer& arc)
{
SerializeStatistics(arc);
SECRET_Serialize(arc);
Mus_Serialize(arc);
quoteMgr.Serialize(arc);
}
//============================================================================= //=============================================================================
// //
// This is for keeping my sanity while working with the horrible mess // This is for keeping my sanity while working with the horrible mess
@ -80,12 +93,21 @@ bool OpenSaveGameForRead(const char *name)
if (savereader != nullptr) if (savereader != nullptr)
{ {
FResourceLump* info = savereader->FindLump("session.json");
if (info == nullptr)
{
return false;
}
FSerializer arc;
void* data = info->Get();
if (!arc.OpenReader((const char*)data, info->LumpSize))
{
return false;
}
// Load system-side data from savegames. // Load system-side data from savegames.
ReadStatistics(); SerializeSession(arc);
SECRET_Load();
MUS_Restore();
quoteMgr.ReadFromSavegame();
LoadEngineState(); LoadEngineState();
auto file = ReadSavegameChunk("info.json"); auto file = ReadSavegameChunk("info.json");
@ -152,7 +174,6 @@ bool OpenSaveGameForWrite(const char* filename, const char *name)
FSerializer savegameengine; // saved play state. FSerializer savegameengine; // saved play state.
savegameinfo.OpenWriter(true); savegameinfo.OpenWriter(true);
savegamesession.OpenWriter(save_formatted);
savegameengine.OpenWriter(save_formatted); savegameengine.OpenWriter(save_formatted);
char buf[100]; char buf[100];
@ -192,10 +213,11 @@ bool OpenSaveGameForWrite(const char* filename, const char *name)
// Handle system-side modules that need to persist data in savegames here, in a central place. // Handle system-side modules that need to persist data in savegames here, in a central place.
SaveStatistics(); savegamesession.OpenWriter(save_formatted);
SECRET_Save(); SerializeSession(savegamesession);
MUS_Save(); buff = savegamesession.GetCompressedOutput();
quoteMgr.WriteToSavegame(); AddCompressedSavegameChunk("session.json", buff);
SaveEngineState(); SaveEngineState();
auto picfile = WriteSavegameChunk("savepic.png"); auto picfile = WriteSavegameChunk("savepic.png");
screen->WriteSavePic(picfile, 240, 180); screen->WriteSavePic(picfile, 240, 180);
@ -270,33 +292,25 @@ static bool G_CheckSaveGameWads (const char *gamegrp, const char *mapgrp, bool p
int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu) int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
{ {
#if 0 auto data = fr.Read();
FSerializer arc(nullptr); FSerializer arc;
if (!arc.OpenReader((const char*)data, info->LumpSize)) if (!arc.OpenReader((const char*)data.Data(), data.Size()))
{ {
LoadGameError("TXT_FAILEDTOREADSG"); return -2;
return;
} }
#endif
auto data = fr.ReadPadded(1); int savever;
FString engine, gamegrp, mapgrp, title, filename;
sjson_context* ctx = sjson_create_context(0, 0, NULL); arc("Save Version", savever)
if (ctx) ("Engine", engine)
{ ("Game Resource", gamegrp)
sjson_node* root = sjson_decode(ctx, (const char*)data.Data()); ("Map Resource", mapgrp)
("Title", title)
("Map File", filename);
int savever = sjson_get_int(root, "Save Version", -1);
FString engine = sjson_get_string(root, "Engine", "");
FString gamegrp = sjson_get_string(root, "Game Resource", "");
FString mapgrp = sjson_get_string(root, "Map Resource", "");
FString title = sjson_get_string(root, "Title", "");
FString filename = sjson_get_string(root, "Map File", "");
auto savesig = gi->GetSaveSig(); auto savesig = gi->GetSaveSig();
sjson_destroy_context(ctx);
if (savetitle) *savetitle = title; if (savetitle) *savetitle = title;
if (engine.Compare(savesig.savesig) != 0 || savever > savesig.currentsavever) if (engine.Compare(savesig.savesig) != 0 || savever > savesig.currentsavever)
{ {
@ -348,7 +362,6 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu)
return 0; return 0;
} }
} }
}
return 0; return 0;
} }

View file

@ -6,8 +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 "sjson.h" #include "serializer.h"
#include "savegamehelp.h"
#include "mapinfo.h" #include "mapinfo.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.
@ -109,54 +108,13 @@ CCMD(secret)
} }
} }
void SECRET_Save() void SECRET_Serialize(FSerializer &arc)
{ {
sjson_context* ctx = sjson_create_context(0, 0, NULL); if (arc.BeginObject("secrets"))
if (!ctx)
{ {
return; arc("secrets", discovered_secrets)
.EndObject();
} }
sjson_node* root = sjson_mkobject(ctx);
sjson_put_ints(ctx, root, "secrets", discovered_secrets.Data(), discovered_secrets.Size());
char* encoded = sjson_stringify(ctx, root, " ");
FileWriter* fil = WriteSavegameChunk("secrets.json");
if (!fil)
{
sjson_destroy_context(ctx);
return;
}
fil->Write(encoded, strlen(encoded));
sjson_free_string(ctx, encoded);
sjson_destroy_context(ctx);
}
bool SECRET_Load()
{
auto fil = ReadSavegameChunk("secrets.json");
if (!fil.isOpen())
{
return false;
}
auto text = fil.ReadPadded(1);
fil.Close();
if (text.Size() == 0)
{
return false;
}
sjson_context* ctx = sjson_create_context(0, 0, NULL);
sjson_node* root = sjson_decode(ctx, (const char*)text.Data());
discovered_secrets.Resize(1000); // Retarted interface alert
int realsize = sjson_get_ints(discovered_secrets.Data(), 1000, root, "secrets");
discovered_secrets.Resize(realsize);
sjson_destroy_context(ctx);
return true;
} }
void SECRET_SetMapName(const char *filename, const char *_maptitle) void SECRET_SetMapName(const char *filename, const char *_maptitle)

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "files.h" #include "files.h"
void SECRET_Save(); class FSerializer;
bool SECRET_Load(); void SECRET_Serialize(FSerializer &arc);
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);

View file

@ -51,6 +51,8 @@
#include "printf.h" #include "printf.h"
#include "s_soundinternal.h" #include "s_soundinternal.h"
bool save_full = false;
//========================================================================== //==========================================================================
// //
// This will double-encode already existing UTF-8 content. // This will double-encode already existing UTF-8 content.

View file

@ -50,6 +50,7 @@
#include "s_music.h" #include "s_music.h"
#include "z_music.h" #include "z_music.h"
#include "gamecvars.h" #include "gamecvars.h"
#include "gamecontrol.h"
#include <zmusic.h> #include <zmusic.h>
EXTERN_CVAR (Float, snd_sfxvolume) EXTERN_CVAR (Float, snd_sfxvolume)
@ -70,8 +71,6 @@ CVAR(String, snd_backend, DEF_BACKEND, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR (Bool, snd_pitched, false, CVAR_ARCHIVE) CVAR (Bool, snd_pitched, false, CVAR_ARCHIVE)
SoundRenderer *GSnd; SoundRenderer *GSnd;
bool nosound;
bool nosfx;
void I_CloseSound (); void I_CloseSound ();
@ -248,12 +247,9 @@ public:
void I_InitSound () void I_InitSound ()
{ {
FModule_SetProgDir(progdir); FModule_SetProgDir(progdir);
/* Get command line options: */
nosound = !!Args->CheckParm ("-nosound");
nosfx = !!Args->CheckParm ("-nosfx");
GSnd = NULL; GSnd = NULL;
if (nosound) if (userConfig.nosound)
{ {
GSnd = new NullSoundRenderer; GSnd = new NullSoundRenderer;
return; return;

View file

@ -159,8 +159,6 @@ public:
}; };
extern SoundRenderer *GSnd; extern SoundRenderer *GSnd;
extern bool nosfx;
extern bool nosound;
void I_InitSound (); void I_InitSound ();
void I_CloseSound(); void I_CloseSound();

View file

@ -43,6 +43,7 @@
#include "name.h" #include "name.h"
#include "filesystem.h" #include "filesystem.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "gamecontrol.h"
enum enum
@ -382,7 +383,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
FVector3 pos, vel; FVector3 pos, vel;
FRolloffInfo *rolloff; FRolloffInfo *rolloff;
if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) if (sound_id <= 0 || volume <= 0 || userConfig.nosound )
return NULL; return NULL;
// prevent crashes. // prevent crashes.

View file

@ -45,8 +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" #include "serializer.h"
#include "sjson.h"
#include "gstrings.h" #include "gstrings.h"
#include "version.h" #include "version.h"
@ -468,112 +467,31 @@ void STAT_Cancel()
// //
//========================================================================== //==========================================================================
void SaveOneLevel(sjson_context *ctx, sjson_node *lev, OneLevel& l) FSerializer& Serialize(FSerializer& arc, const char* key, OneLevel& l, OneLevel* def)
{ {
sjson_put_int(ctx, lev, "totalkills", l.totalkills); if (arc.BeginObject(key))
sjson_put_int(ctx, lev, "kills", l.killcount); {
sjson_put_int(ctx, lev, "totalsecrets", l.totalsecrets); arc("totalkills", l.totalkills)
sjson_put_int(ctx, lev, "secrets", l.secretcount); ("killcount", l.killcount)
sjson_put_int(ctx, lev, "leveltime", l.leveltime); ("totalsecrets", l.totalsecrets)
sjson_put_string(ctx, lev, "levelname", l.Levelname); ("secretcount", l.secretcount)
("leveltime", l.leveltime)
("levelname", l.Levelname)
.EndObject();
}
return arc;
} }
void ReadOneLevel(sjson_node *lev, OneLevel& l) void SerializeStatistics(FSerializer &arc)
{ {
if (arc.BeginObject("statistics"))
l.totalkills = sjson_get_int(lev, "totalkills", 0); {
l.killcount = sjson_get_int(lev, "kills", 0); arc("levelname", LevelName)
l.totalsecrets = sjson_get_int(lev, "totalsecrets", 0); ("episode", StartEpisode)
l.secretcount = sjson_get_int(lev, "secrets", 0); ("skill", StartSkill)
l.leveltime = sjson_get_int(lev, "leveltime", 0); ("levels", LevelData)
l.Levelname = sjson_get_string(lev, "levelname", ""); .EndObject();
} }
void SaveStatistics()
{
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (!ctx)
{
return;
}
sjson_node* root = sjson_mkobject(ctx);
sjson_put_string(ctx, root, "levelname", LevelName);
sjson_put_string(ctx, root, "episode", StartEpisode);
sjson_put_int(ctx, root, "skill", StartSkill);
sjson_node* levels = sjson_mkarray(ctx);
for (auto& lev : LevelData)
{
sjson_node* levj = sjson_mkobject(ctx);
SaveOneLevel(ctx, levj, lev);
sjson_append_element(levels, levj);
}
sjson_append_member(ctx, root, "levels", levels);
char errmsg[256];
if (!sjson_check(root, errmsg))
{
buildprint(errmsg, "\n");
sjson_destroy_context(ctx);
return;
}
char* encoded = sjson_stringify(ctx, root, " ");
FileWriter* fil = WriteSavegameChunk("statistics.json");
if (!fil)
{
sjson_destroy_context(ctx);
return;
}
fil->Write(encoded, strlen(encoded));
sjson_free_string(ctx, encoded);
sjson_destroy_context(ctx);
return;
}
bool ReadStatistics()
{
auto fil = ReadSavegameChunk("statistics.json");
if (!fil.isOpen())
{
return false;
}
auto text = fil.ReadPadded(1);
fil.Close();
if (text.Size() == 0)
{
return false;
}
sjson_context* ctx = sjson_create_context(0, 0, NULL);
sjson_node* root = sjson_decode(ctx, (const char*)text.Data());
LevelName = sjson_get_string(root, "levelname", "");
StartEpisode = sjson_get_string(root, "episode", "");
StartSkill = sjson_get_int(root, "skill", -1);
sjson_node* levels = sjson_find_member(root, "levels");
if (LevelName.Len() == 0 || StartEpisode.Len() == 0 || StartSkill == -1 || levels == nullptr)
{
sjson_destroy_context(ctx);
return true; // do not error out on this.
}
int numlevels = sjson_child_count(levels);
LevelData.Resize(numlevels);
int i = 0;
for (auto& lev : LevelData)
{
ReadOneLevel(sjson_find_element(levels, i++), lev);
}
sjson_destroy_context(ctx);
return true;
} }

View file

@ -6,6 +6,6 @@ void STAT_NewLevel(const char* mapname);
void STAT_Update(bool endofgame); void STAT_Update(bool endofgame);
void STAT_Cancel(); void STAT_Cancel();
class FSerializer;
void InitStatistics(); void InitStatistics();
void SaveStatistics(); void SerializeStatistics(FSerializer &);
bool ReadStatistics();

View file

@ -66,17 +66,17 @@ const char *GetVersionString();
#define SAVESIG_SW GAMENAME ".ShadowWarrior" #define SAVESIG_SW GAMENAME ".ShadowWarrior"
#define SAVESIG_PS GAMENAME ".Exhumed" #define SAVESIG_PS GAMENAME ".Exhumed"
#define MINSAVEVER_DN3D 3 #define MINSAVEVER_DN3D 4
#define MINSAVEVER_BLD 3 #define MINSAVEVER_BLD 4
#define MINSAVEVER_RR 3 #define MINSAVEVER_RR 4
#define MINSAVEVER_SW 2 #define MINSAVEVER_SW 3
#define MINSAVEVER_PS 2 #define MINSAVEVER_PS 3
#define SAVEVER_DN3D 3 #define SAVEVER_DN3D 4
#define SAVEVER_BLD 3 #define SAVEVER_BLD 4
#define SAVEVER_RR 3 #define SAVEVER_RR 4
#define SAVEVER_SW 2 #define SAVEVER_SW 3
#define SAVEVER_PS 2 #define SAVEVER_PS 3
#if defined(__APPLE__) || defined(_WIN32) #if defined(__APPLE__) || defined(_WIN32)
#define GAME_DIR GAMENAME #define GAME_DIR GAMENAME

File diff suppressed because it is too large Load diff

View file

@ -1,5 +0,0 @@
#include "compat.h"
#define SJSON_IMPLEMENT
#include "sjson.h"