From 817fa8aba32587f6f41213f762f15db3f920e25a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 13 Oct 2020 19:43:45 +0200 Subject: [PATCH] - save the entire engine state as JSON. The sprite lists may still need optimization. Due to different handling between Blood and the core engine they need to be written out completely which is quite wasteful. --- source/blood/src/common_game.h | 1 - source/blood/src/db.cpp | 4 +- source/build/include/build.h | 6 - source/build/include/compat.h | 14 ++ source/build/src/engine.cpp | 2 - source/build/src/polymost.cpp | 53 +------ source/common/engine/serializer.h | 29 ++++ source/core/savegamehelp.cpp | 248 ++++++++++++++++-------------- source/core/savegamehelp.h | 2 - source/sw/src/sprite.cpp | 2 +- 10 files changed, 180 insertions(+), 181 deletions(-) diff --git a/source/blood/src/common_game.h b/source/blood/src/common_game.h index 711bce245..5a52a73b1 100644 --- a/source/blood/src/common_game.h +++ b/source/blood/src/common_game.h @@ -82,7 +82,6 @@ kMediumGoo = 2, // STATNUMS ///////////////////////////////////////////////////// enum { -kStatNothing = -1, kStatDecoration = 0, kStatFX = 1, kStatExplosion = 2, diff --git a/source/blood/src/db.cpp b/source/blood/src/db.cpp index 13e9bad13..e2a3639a5 100644 --- a/source/blood/src/db.cpp +++ b/source/blood/src/db.cpp @@ -104,7 +104,7 @@ void RemoveSpriteSect(int nSprite) { headspritesect[nSector] = nextspritesect[nSprite]; } - sprite[nSprite].sectnum = -1; + sprite[nSprite].sectnum = MAXSECTORS; } void InsertSpriteStat(int nSprite, int nStat) @@ -148,7 +148,7 @@ void RemoveSpriteStat(int nSprite) { headspritestat[nStat] = nextspritestat[nSprite]; } - sprite[nSprite].statnum = kStatNothing; + sprite[nSprite].statnum = MAXSTATUS; gStatCount[nStat]--; } diff --git a/source/build/include/build.h b/source/build/include/build.h index 001372cff..9c60120e6 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -201,12 +201,7 @@ typedef struct { int16_t angoff, pitch, roll; vec3_t pivot_offset, position_offset; uint8_t flags; - uint8_t xpanning, ypanning; // EDuke script hacks. - uint8_t filler; - uint32_t filler2; float alpha; - // NOTE: keep 'tspr' on an 8-byte boundary: - tspriteptr_t tspr; } spriteext_t; typedef struct { @@ -328,7 +323,6 @@ enum { PALETTE_TRANSLUC = 1<<2, }; -EXTERN char showinvisibility; EXTERN int32_t g_visibility, parallaxvisibility; // blendtable[1] to blendtable[numalphatabs] are considered to be diff --git a/source/build/include/compat.h b/source/build/include/compat.h index 70de736da..6c03955a1 100644 --- a/source/build/include/compat.h +++ b/source/build/include/compat.h @@ -11,6 +11,7 @@ #include "m_alloc.h" #include "intvec.h" #include "m_swap.h" +#include "serializer.h" ////////// Compiler detection ////////// @@ -242,5 +243,18 @@ void bfirst_search_try(T *const list, uint8_t *const bitmap, T *const eltnumptr, /* End dependence on compat.o object. */ +inline FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def) +{ + if (def && !memcmp(&c, def, sizeof(c))) return arc; + if (arc.BeginObject(key)) + { + arc("x", c.x, def? &def->x : nullptr) + ("y", c.y, def ? &def->y : nullptr) + ("z", c.z, def ? &def->z : nullptr) + .EndObject(); + } + return arc; +} + #endif // compat_h_ diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index 5655bc537..d4e4226eb 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -943,8 +943,6 @@ int32_t engineInit(void) xyaspect = -1; - showinvisibility = 0; - voxelmemory.Reset(); for (i=0; icstat & 0x8000 && !showinvisibility) || spr->xrepeat == 0 || spr->yrepeat == 0) + if ((spr->cstat & 0x8000) || spr->xrepeat == 0 || spr->yrepeat == 0) continue; vec2_t const s = { spr->x-globalposx, spr->y-globalposy }; @@ -3204,21 +3204,6 @@ void polymost_drawsprite(int32_t snum) otex.v = -ytex.v * (pxy[3].y + .001f); } - // sprite panning - if (spriteext[spritenum].xpanning) - { - ytex.u -= ytex.d * ((float) (spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x; - otex.u -= otex.d * ((float) (spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x; - drawpoly_srepeat = 1; - } - - if (spriteext[spritenum].ypanning) - { - ytex.v -= ytex.d * ((float) (spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y; - otex.v -= otex.d * ((float) (spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y; - drawpoly_trepeat = 1; - } - // Clip sprites to ceilings/floors when no parallaxing and not sloped if (!(sector[tspr->sectnum].ceilingstat & 3)) { @@ -3363,15 +3348,6 @@ void polymost_drawsprite(int32_t snum) t1 = 1.f - t1; } - // sprite panning - if (spriteext[spritenum].xpanning) - { - float const xpan = ((float)(spriteext[spritenum].xpanning) * (1.0f / 255.f)); - t0 -= xpan; - t1 -= xpan; - drawpoly_srepeat = 1; - } - xtex.u = (t0 * ryp0 - t1 * ryp1) * gxyaspect * ftsiz.x / (sx0 - sx1); ytex.u = 0; otex.u = t0 * ryp0 * gxyaspect * ftsiz.x - xtex.u * sx0; @@ -3401,16 +3377,6 @@ void polymost_drawsprite(int32_t snum) otex.v = -xtex.v * sx0 - ytex.v * sf0; } - // sprite panning - if (spriteext[spritenum].ypanning) - { - float const ypan = ((float)(spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y; - xtex.v -= xtex.d * ypan; - ytex.v -= ytex.d * ypan; - otex.v -= otex.d * ypan; - drawpoly_trepeat = 1; - } - // Clip sprites to ceilings/floors when no parallaxing if (!(sector[tspr->sectnum].ceilingstat & 1)) { @@ -3596,23 +3562,6 @@ void polymost_drawsprite(int32_t snum) otex.u = ftsiz.x * otex.d - otex.u; } - // sprite panning - if (spriteext[spritenum].xpanning) - { - float const f = ((float)(spriteext[spritenum].xpanning) * (1.0f / 255.f)) * ftsiz.x; - ytex.u -= ytex.d * f; - otex.u -= otex.d * f; - drawpoly_srepeat = 1; - } - - if (spriteext[spritenum].ypanning) - { - float const f = ((float)(spriteext[spritenum].ypanning) * (1.0f / 255.f)) * ftsiz.y; - ytex.v -= ytex.d * f; - otex.v -= otex.d * f; - drawpoly_trepeat = 1; - } - vec2_16_t tempsiz = { (int16_t)tsiz.x, (int16_t)tsiz.y }; pow2xsplit = 0; diff --git a/source/common/engine/serializer.h b/source/common/engine/serializer.h index 6110ca4b4..22f32f73c 100644 --- a/source/common/engine/serializer.h +++ b/source/common/engine/serializer.h @@ -125,6 +125,12 @@ public: return Serialize(*this, key, obj, save_full? nullptr : &def); } + template + FSerializer& operator()(const char* key, T& obj, T* def) + { + return Serialize(*this, key, obj, !def || save_full ? nullptr : def); + } + template FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false) { @@ -172,6 +178,29 @@ public: return *this; } + template + FSerializer &SparseArray(const char *key, T *obj, int count, const Map &map, bool fullcompare = false) + { + if (BeginArray(key)) + { + int max = count; + if (isReading()) + { + max = ArraySize(); + } + for (int i = 0; i < count; i++) + { + if (map[i]) + { + Serialize(*this, nullptr, obj[i], (T*)nullptr); + if (--max < 0) break; + } + } + EndArray(); + } + return *this; + } + template FSerializer &Enum(const char *key, T &obj) { diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index e93023b4e..75b5b2f4a 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -62,11 +62,10 @@ walltype wallbackup[MAXWALLS]; static CompositeSavegameWriter savewriter; static FResourceFile *savereader; -void LoadEngineState(); -void SaveEngineState(); void WriteSavePic(FileWriter* file, int width, int height); extern FString BackupSaveGame; void SerializeMap(FSerializer &arc); +FixedBitArray activeSprites; CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -140,8 +139,7 @@ bool OpenSaveGameForRead(const char *name) // Load system-side data from savegames. loadMapBackup(currentLevel->fileName); - LoadEngineState(); - SerializeSession(arc); // must be AFTER LoadEngineState because it needs info from it. + SerializeSession(arc); gi->SerializeGameState(arc); } return savereader != nullptr; @@ -236,7 +234,6 @@ bool OpenSaveGameForWrite(const char* filename, const char *name) // Handle system-side modules that need to persist data in savegames here, in a central place. savegamesession.OpenWriter(save_formatted); SerializeSession(savegamesession); - SaveEngineState(); gi->SerializeGameState(savegamesession); buff = savegamesession.GetCompressedOutput(); AddCompressedSavegameChunk("session.json", buff); @@ -431,24 +428,6 @@ FString G_BuildSaveName (const char *prefix) #include "build.h" #include "mmulti.h" -static void sv_prespriteextsave() -{ - for (int i = 0; i < MAXSPRITES; i++) - if (spriteext[i].mdanimtims) - { - spriteext[i].mdanimtims -= mdtims; - if (spriteext[i].mdanimtims == 0) - spriteext[i].mdanimtims++; - } -} -static void sv_postspriteext() -{ - for (int i = 0; i < MAXSPRITES; i++) - if (spriteext[i].mdanimtims) - spriteext[i].mdanimtims += mdtims; -} - - static const int magic = 0xbeefcafe; void WriteMagic(FileWriter *fw) { @@ -468,8 +447,68 @@ void CheckMagic(FileReader& fr) #define V(x) x static spritetype zsp; -static sectortype zsec; -static walltype zwal; +static spriteext_t zspx; + +FSerializer &Serialize(FSerializer &arc, const char *key, spritetype &c, spritetype *def) +{ + def = &zsp; // always delta against 0 + if (arc.BeginObject(key)) + { + arc("x", c.x, def->x) + ("y", c.y, def->y) + ("z", c.z, def->z) + ("cstat", c.cstat, def->cstat) + ("picnum", c.picnum, def->picnum) + ("shade", c.shade, def->shade) + ("pal", c.pal, def->pal) + ("clipdist", c.clipdist, def->clipdist) + ("blend", c.blend, def->blend) + ("xrepeat", c.xrepeat, def->xrepeat) + ("yrepeat", c.yrepeat, def->yrepeat) + ("xoffset", c.xoffset, def->xoffset) + ("yoffset", c.yoffset, def->yoffset) + ("statnum", c.statnum) + ("sectnum", c.sectnum) + ("ang", c.ang, def->ang) + ("owner", c.owner, def->owner) + ("xvel", c.xvel, def->xvel) + ("yvel", c.yvel, def->yvel) + ("zvel", c.zvel, def->zvel) + ("lotag", c.lotag, def->lotag) + ("hitag", c.hitag, def->hitag) + ("extra", c.extra, def->extra) + ("detail", c.detail, def->detail) + .EndObject(); + } + return arc; +} + +FSerializer& Serialize(FSerializer& arc, const char* key, spriteext_t& c, spriteext_t* def) +{ + if (arc.isWriting() && c.mdanimtims) + { + c.mdanimtims -= mdtims; + if (c.mdanimtims == 0) c.mdanimtims++; + } + + def = &zspx; // always delta against 0 + if (arc.BeginObject(key)) + { + arc("mdanimtims", c.mdanimtims, def->mdanimtims) + ("mdanimcur", c.mdanimcur, def->mdanimcur) + ("angoff", c.angoff, def->angoff) + ("pitch", c.pitch, def->pitch) + ("roll", c.roll, def->roll) + ("pivot_offset", c.pivot_offset, def->pivot_offset) + ("position_offset", c.position_offset, def->position_offset) + ("flags", c.flags, def->flags) + ("alpha", c.alpha, def->alpha) + .EndObject(); + } + + if (c.mdanimtims) c.mdanimtims += mdtims; + return arc; +} FSerializer &Serialize(FSerializer &arc, const char *key, sectortype &c, sectortype *def) { @@ -529,109 +568,88 @@ FSerializer &Serialize(FSerializer &arc, const char *key, walltype &c, walltype return arc; } + void SerializeMap(FSerializer& arc) { + // create a map of all used sprites so that we can use that elsewhere to only save what's needed. + activeSprites.Zero(); + if (arc.isWriting()) + { + for (int i=0; iWrite(sprite, sizeof(spritetype) * MAXSPRITES); - WriteMagic(fw); - fw->Write(headspritesect, sizeof(headspritesect)); - fw->Write(prevspritesect, sizeof(prevspritesect)); - fw->Write(nextspritesect, sizeof(nextspritesect)); - fw->Write(headspritestat, sizeof(headspritestat)); - fw->Write(prevspritestat, sizeof(prevspritestat)); - fw->Write(nextspritestat, sizeof(nextspritestat)); - WriteMagic(fw); - for (int i = 0; i < MAXTILES; i++) - { - fw->Write(&picanm[i], sizeof(picanm[i])); - } - WriteMagic(fw); - - - fw->Write(&tailspritefree, sizeof(tailspritefree)); - fw->Write(&myconnectindex, sizeof(myconnectindex)); - fw->Write(&connecthead, sizeof(connecthead)); - fw->Write(connectpoint2, sizeof(connectpoint2)); - fw->Write(&randomseed, sizeof(randomseed)); - fw->Write(&numshades, sizeof(numshades)); - fw->Write(&showinvisibility, sizeof(showinvisibility)); - WriteMagic(fw); - - fw->Write(&g_visibility, sizeof(g_visibility)); - fw->Write(¶llaxtype, sizeof(parallaxtype)); - fw->Write(¶llaxvisibility, sizeof(parallaxvisibility)); - fw->Write(¶llaxyoffs_override, sizeof(parallaxyoffs_override)); - fw->Write(¶llaxyscale_override, sizeof(parallaxyscale_override)); - fw->Write(&pskybits_override, sizeof(pskybits_override)); - WriteMagic(fw); - - fw->Write(&Numsprites, sizeof(Numsprites)); - sv_prespriteextsave(); - fw->Write(spriteext, sizeof(spriteext_t) * MAXSPRITES); - fw->Write(&randomseed, sizeof(randomseed)); - sv_postspriteext(); - WriteMagic(fw); - -} - -void LoadEngineState() -{ - auto fr = ReadSavegameChunk("engine.bin"); - if (fr.isOpen()) - { - memset(sprite, 0, sizeof(sprite[0]) * MAXSPRITES); - - fr.Read(sprite, sizeof(spritetype) * MAXSPRITES); - CheckMagic(fr); - fr.Read(headspritesect, sizeof(headspritesect)); - fr.Read(prevspritesect, sizeof(prevspritesect)); - fr.Read(nextspritesect, sizeof(nextspritesect)); - fr.Read(headspritestat, sizeof(headspritestat)); - fr.Read(prevspritestat, sizeof(prevspritestat)); - fr.Read(nextspritestat, sizeof(nextspritestat)); - CheckMagic(fr); - for (int i = 0; i < MAXTILES; i++) + if (arc.BeginArray("picanm")) // write this in the most compact form available. { - fr.Read(&picanm[i], sizeof(picanm[i])); + for (int i = 0; i < MAXTILES; i++) + { + arc(nullptr, picanm[i].sf) + (nullptr, picanm[i].extra); + } + arc.EndArray(); } - CheckMagic(fr); - fr.Read(&tailspritefree, sizeof(tailspritefree)); - fr.Read(&myconnectindex, sizeof(myconnectindex)); - fr.Read(&connecthead, sizeof(connecthead)); - fr.Read(connectpoint2, sizeof(connectpoint2)); - fr.Read(&randomseed, sizeof(randomseed)); - fr.Read(&numshades, sizeof(numshades)); - fr.Read(&showinvisibility, sizeof(showinvisibility)); - CheckMagic(fr); + arc.EndObject(); + } - fr.Read(&g_visibility, sizeof(g_visibility)); - fr.Read(¶llaxtype, sizeof(parallaxtype)); - fr.Read(¶llaxvisibility, sizeof(parallaxvisibility)); - fr.Read(¶llaxyoffs_override, sizeof(parallaxyoffs_override)); - fr.Read(¶llaxyscale_override, sizeof(parallaxyscale_override)); - fr.Read(&pskybits_override, sizeof(pskybits_override)); - CheckMagic(fr); - fr.Read(&Numsprites, sizeof(Numsprites)); - fr.Read(spriteext, sizeof(spriteext_t) * MAXSPRITES); - fr.Read(&randomseed, sizeof(randomseed)); - sv_postspriteext(); - CheckMagic(fr); - - fr.Close(); + // Undo the simplification. + for (int i = 0; i < MAXSPRITES; i++) + { + if (nextspritestat[i] == -2) nextspritestat[i] = i + 1; + if (nextspritesect[i] == -2) nextspritesect[i] = i + 1; + if (prevspritestat[i] == -2) prevspritestat[i] = i - 1; + if (prevspritesect[i] == -2) prevspritesect[i] = i - 1; } } diff --git a/source/core/savegamehelp.h b/source/core/savegamehelp.h index ee086fc74..ceb25a28f 100644 --- a/source/core/savegamehelp.h +++ b/source/core/savegamehelp.h @@ -20,8 +20,6 @@ int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu); void G_LoadGame(const char* filename); void G_SaveGame(const char* fn, const char* desc, bool ok4q, bool forceq); -void SaveEngineState(); -void LoadEngineState(); void M_Autosave(); #define SAVEGAME_EXT ".dsave" diff --git a/source/sw/src/sprite.cpp b/source/sw/src/sprite.cpp index 1f7795f64..688ece610 100644 --- a/source/sw/src/sprite.cpp +++ b/source/sw/src/sprite.cpp @@ -4632,7 +4632,7 @@ NewStateGroup(short SpriteNum, STATEp StateGroup[]) // Kind of a goofy check, but it should catch alot of invalid states! // BTW, 6144 is the max tile number allowed in editart. - if (u->State && (u->State->Pic < 0 || u->State->Pic > 6144)) // JBF: verify this! + if (u->State && (u->State->Pic < 0 || u->State->Pic > MAXTILES)) // JBF: verify this! return 0; u->Rot = StateGroup;