From 497a792e6faf1ff4d3cdc8768916622bb1c23a65 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 16 Dec 2024 03:36:30 +0100 Subject: [PATCH 1/7] Blood: Increase kMaxLevels to 64, but only if NOONE_EXTENSIONS are enabled otherwise use the old default of 16 --- source/blood/src/levels.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/blood/src/levels.h b/source/blood/src/levels.h index 0be34e7436..72c6f9c3b6 100644 --- a/source/blood/src/levels.h +++ b/source/blood/src/levels.h @@ -26,7 +26,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define kMaxMessages 32 #define kMaxEpisodes 7 -#define kMaxLevels 16 +#ifdef NOONE_EXTENSIONS + // allow advanced mods to use more than 16 levels. + // for example, Blood: What Lies Beneath has 43 (as of version 1.1) + #define kMaxLevels 64 +#else // original blood only allowed 16 levels per episode + #define kMaxLevels 16 +#endif #pragma pack(push, 1) From b8c296ae87eaed6323e1ef38e725d88071c314aa Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 15 Dec 2024 20:41:03 +0100 Subject: [PATCH 2/7] Blood: Write savegames to temp file first, rename when done If the game crashed while saving, the savegame was corrupted. Prevent that by saving to a temporary file first and renaming that to the proper savegame name when saving is done (without crashing), so at least the previous version of the savegame is preserved in case of crashes. Additionally this creates a backup of the previous version as gameXXX.sav_bk in case I regret doing a quicksave --- source/blood/src/loadsave.cpp | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index a93f2aebdd..64dc778f60 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -232,9 +232,33 @@ void LoadSave::LoadGame(char *pzFile) //sndPlaySong(gGameOptions.zLevelSong, 1); } +// TODO: when starting to use buildvfs in SaveGame(), remove this function and use buildvfs_exists() instead +static int file_exists(char const* path) +{ +#ifdef _WIN32 + return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES; +#else + struct Bstat st; + return !Bstat(path, &st); +#endif +} + void LoadSave::SaveGame(char *pzFile) { - hSFile = fopen(pzFile, "wb"); + // TODO: use buildvfs_open_write() etc (probably in the whole file?) + char fileNameTmp[BMAX_PATH+4]; + const char* saveFileName = pzFile; + bool saveFileExists = file_exists(saveFileName); + if (saveFileExists) + { + // write to a different file first, so in case the game crashes while saving + // (which would result in a corrupted savegame) at least the old savegame is preserved + strcpy(fileNameTmp, pzFile); + strcat(fileNameTmp, "_tmp"); + saveFileName = fileNameTmp; + } + + hSFile = fopen(saveFileName, "wb"); if (hSFile == NULL) ThrowError("File error #%d creating save file.", errno); dword_27AA38 = 0; @@ -250,6 +274,18 @@ void LoadSave::SaveGame(char *pzFile) } fclose(hSFile); hSFile = NULL; + if (saveFileExists) + { + // I'd like to have a backup of the old savegame, just in case I regret the last save :-p + char fileNameBk[BMAX_PATH+3]; + strcpy(fileNameBk, pzFile); + strcat(fileNameBk, "_bk"); + rename(pzFile, fileNameBk); + + // the savegame was written successfully, so we can rename the saved file + // to the requested name (from gameXXX.sav_tmp to gameXXX.sav) + rename(saveFileName, pzFile); + } } class MyLoadSave : public LoadSave From c97facc31364f7e71ea13aa25a8ee0571ae72ab2 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 15 Dec 2024 04:42:40 +0100 Subject: [PATCH 3/7] Blood: Bump savegame version (BYTEVERSION) to 106 Commit 92eebf5 "- Fix SEQ playing incorrect animation and/or AI (...)" broke savegame backwards compatibility, so the version should've been bumped then. I'm doing it now instead, better late than never.. --- source/blood/src/common_game.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blood/src/common_game.h b/source/blood/src/common_game.h index a548afbe54..acdb0afab9 100644 --- a/source/blood/src/common_game.h +++ b/source/blood/src/common_game.h @@ -44,7 +44,7 @@ extern int g_useCwd; #define BLOODWIDESCREENDEF "blood_widescreen.def" -#define BYTEVERSION 105 +#define BYTEVERSION 106 void _SetErrorLoc(const char *pzFile, int nLine); void _ThrowError(const char *pzFormat, ...); From 074b0d72a6c35c6bc2c96d78b9460e31ac1cfa36 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 15 Dec 2024 23:52:31 +0100 Subject: [PATCH 4/7] Blood: Silence some compiler warnings about pot. uninit. values --- source/blood/src/aicdud.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/blood/src/aicdud.cpp b/source/blood/src/aicdud.cpp index 7451efa0cd..8bc7bb948a 100644 --- a/source/blood/src/aicdud.cpp +++ b/source/blood/src/aicdud.cpp @@ -629,13 +629,13 @@ static void weaponShot(int, int nXIndex) int nShots, nTime, nCode; int dx1, dy1, dz1; - int dx2, dy2, dz2; - int dx3, dy3, dz3; + int dx2, dy2, dz2=0; + int dx3=0, dy3=0, dz3; int i, j; - int txof; char hxof; - int sang; int hsht; - int tang; char styled; + int txof; char hxof=0; + int sang=0; int hsht; + int tang=0; char styled; nnExtCoSin(pSpr->ang, &dx1, &dy1); From 7b1b726ce2d0bbb20615d7119c3737b1c3574a7d Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 15 Dec 2024 23:57:18 +0100 Subject: [PATCH 5/7] Blood: Fix (work around) crash on loading some savegames .. in nnExtInitSprite() This sometimes happened in WLB, for some reason pSpr->extra was < 0 so `xsprite[pSpr->extra]` returned garbage (or even crashed) and later getSpriteMassBySize() threw an error because pSpr->extra < 0. No idea why bSpr->extra was -1 in the first place, though. --- source/blood/src/nnexts.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/blood/src/nnexts.cpp b/source/blood/src/nnexts.cpp index 0b5bde87fe..62fe559c66 100644 --- a/source/blood/src/nnexts.cpp +++ b/source/blood/src/nnexts.cpp @@ -1127,7 +1127,13 @@ void nnExtInitSprite(int nSpr, bool bSaveLoad) { int i; spritetype* pSpr = &sprite[nSpr]; - if ((pSpr->flags & kHitagFree)) + // FIXME: the extra < 0 check prevents crashes when loading some savegames, for example + // due to `XSPRITE* pXSpr = &xsprite[pSpr->extra]` accessing invalid memory, + // or due to that memory containing a value != 0 at pXspr->physAddr and then + // getSpriteMassBySize() throwing an error because pSpr->extra < 0.. + // However, it maybe hides real issues that should be investigated, like + // when/why is extra -1 here?! + if ((pSpr->flags & kHitagFree) || pSpr->extra < 0) return; XSPRITE* pXSpr = &xsprite[pSpr->extra]; From a9b1eb64febec81f91c1a1b133ec99eb59eb283f Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 16 Dec 2024 00:00:52 +0100 Subject: [PATCH 6/7] Blood: Fix crash when saving with more than 1024 EVENTs in eventQ happened sometimes in WLB, for example in the garden level near the well/fountain, when it was almost (but not completely) empty (and maybe zombies being in there? not sure what exactly caused it, but that's where I ran into this, I think I had > 3000 events) --- source/blood/src/eventq.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/source/blood/src/eventq.cpp b/source/blood/src/eventq.cpp index b0f9e2720a..4a17d4e287 100644 --- a/source/blood/src/eventq.cpp +++ b/source/blood/src/eventq.cpp @@ -642,27 +642,35 @@ void EventQLoadSave::Load() Read(bucketHead, sizeof(bucketHead)); } +struct EventWithTime { + EVENT event; + unsigned int evTime; +}; + void EventQLoadSave::Save() { - EVENT events[1024]; - unsigned int eventstime[1024]; - Write(&eventQ, sizeof(eventQ)); - int nEvents = eventQ.PQueue->Size(); + uint32_t nEvents = eventQ.PQueue->Size(); + // DG: support arbitrary number of events instead of just 1024 (fix crashes when saving in WLB) + EventWithTime* events = new EventWithTime[nEvents]; + + Write(&eventQ, sizeof(eventQ)); // FIXME: what's the point of saving this? this only writes a pointer that is invalid on load anyway Write(&nEvents, sizeof(nEvents)); - for (int i = 0; i < nEvents; i++) + for (uint32_t i = 0; i < nEvents; i++) { - eventstime[i] = eventQ.PQueue->LowestPriority(); - events[i] = eventQ.ERemove(); - Write(&eventstime[i], sizeof(eventstime[i])); - Write(&events[i], sizeof(events[i])); + EventWithTime& evt = events[i]; + evt.evTime = eventQ.PQueue->LowestPriority(); + evt.event = eventQ.ERemove(); + Write(&evt.evTime, sizeof(evt.evTime)); + Write(&evt.event, sizeof(evt.event)); } dassert(eventQ.PQueue->Size() == 0); - for (int i = 0; i < nEvents; i++) + for (uint32_t i = 0; i < nEvents; i++) { - eventQ.PQueue->Insert(eventstime[i], events[i]); + eventQ.PQueue->Insert(events[i].evTime, events[i].event); } Write(rxBucket, sizeof(rxBucket)); Write(bucketHead, sizeof(bucketHead)); + delete[] events; } static EventQLoadSave *myLoadSave; From a53bd06e2e4cf33afd29093d34a250ba580e729f Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Mon, 16 Dec 2024 00:01:25 +0100 Subject: [PATCH 7/7] Blood: Make dassert() and ThrowError() break into debugger --- source/blood/src/globals.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blood/src/globals.cpp b/source/blood/src/globals.cpp index b6adad1ef8..ec7369e4a3 100644 --- a/source/blood/src/globals.cpp +++ b/source/blood/src/globals.cpp @@ -59,6 +59,9 @@ void _ThrowError(const char *pzFormat, ...) vsprintf(buffer, pzFormat, args); LOG_F(ERROR, "%s(%i): %s", _module, _line, buffer); + // NOTE: if NDEBUG is defined, this is a no-op (handled in build/include/compat.h) + debug_break(); + #ifdef WM_MSGBOX_WINDOW char titlebuf[256]; Bsprintf(titlebuf, APPNAME " %s", s_buildRev); @@ -79,11 +82,13 @@ void _consoleSysMsg(const char* pzFormat, ...) { OSD_Printf(OSDTEXT_RED "%s(%i): %s\n", _module, _line, buffer); } - void __dassert(const char * pzExpr, const char * pzFile, int nLine) { LOG_F(ERROR, "Assertion failed: %s in file %s at line %i", pzExpr, pzFile, nLine); + // NOTE: if NDEBUG is defined, this is a no-op (handled in build/include/compat.h) + debug_break(); + #ifdef WM_MSGBOX_WINDOW char titlebuf[256]; Bsprintf(titlebuf, APPNAME " %s", s_buildRev);