From 3e900d7f57877af23df466bdb9425703a8f0f46f Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 17 Mar 2023 14:34:39 +0000 Subject: [PATCH] G_DirtyGameData: Dirty bit only applied in I_Error and signal handlers, nowhere else - Unfortunately, the way this system previously worked, the unlock was given to you for free if you accidentially opened two copies of the game at once. - Instead, open the file in r+ mode, shimmy along 5 bytes, and write a `true` to be read later. - Far more memory safe than rewriting the entire gamedata out on crash. ALSO: - crashflags has been split into boolean evercrashed and UINT8 musicflags. - We don't need to track if the LAST session was a crash, at least not right now. - Opens the floor up to other music like Loser Club happening on the Challenges menu. --- src/d_main.c | 2 +- src/deh_soc.c | 2 +- src/f_finale.c | 2 +- src/g_demo.c | 2 +- src/g_game.c | 61 +++++++++++++++++++++++++---------- src/g_game.h | 3 +- src/k_menufunc.c | 2 +- src/k_podium.c | 4 +-- src/k_pwrlv.c | 4 +-- src/m_cond.c | 9 +++--- src/m_cond.h | 7 ++-- src/menus/extras-challenges.c | 2 +- src/p_setup.c | 4 +-- src/p_tick.c | 2 +- src/sdl/i_system.c | 9 ++++-- src/sdl12/i_system.c | 9 ++++-- src/w_wad.c | 2 +- src/win32ce/win_sys.c | 9 ++++-- 18 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 508b42712..027cd2461 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1051,7 +1051,7 @@ void D_ClearState(void) cursongcredit.def = NULL; if (gamedata && gamedata->deferredsave) - G_SaveGameData(true); + G_SaveGameData(); G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; diff --git a/src/deh_soc.c b/src/deh_soc.c index 13c5b2703..8a6c2955d 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2931,7 +2931,7 @@ void readmaincfg(MYFILE *f, boolean mainfile) if (!GoodDataFileName(word2)) I_Error("Maincfg: bad data file name '%s'\n", word2); - G_SaveGameData(false); // undirty your old gamedata + G_SaveGameData(); strlcpy(gamedatafilename, word2, sizeof (gamedatafilename)); strlwr(gamedatafilename); savemoddata = true; diff --git a/src/f_finale.c b/src/f_finale.c index 0a802459d..4e17c8785 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1084,7 +1084,7 @@ void F_GameEvaluationTicker(void) ++gamedata->timesBeaten; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } else { diff --git a/src/g_demo.c b/src/g_demo.c index 312d0f3ca..a8632e126 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -4196,7 +4196,7 @@ void G_SaveDemo(void) { gamedata->eversavedreplay = true; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } } else diff --git a/src/g_game.c b/src/g_game.c index 26c0bc2af..c3eac0990 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3834,7 +3834,7 @@ static void G_UpdateVisited(void) CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : ""); M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } static boolean CanSaveLevel(INT32 mapnum) @@ -3926,7 +3926,7 @@ static void G_GetNextMap(void) { gamedata->everseenspecial = true; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } } } @@ -4161,7 +4161,7 @@ static void G_DoCompleted(void) } if (gamedata->deferredsave) - G_SaveGameData(true); + G_SaveGameData(); legitimateexit = false; @@ -4567,6 +4567,11 @@ void G_LoadGameData(void) gridunusable = true; } + if (versionMinor > 1) + { + gamedata->evercrashed = (boolean)READUINT8(save.p); + } + gamedata->totalplaytime = READUINT32(save.p); if (versionMinor > 1) @@ -4583,10 +4588,6 @@ void G_LoadGameData(void) gamedata->keyspending = READUINT8(save.p); gamedata->chaokeys = READUINT16(save.p); - gamedata->crashflags = READUINT8(save.p); - if (gamedata->crashflags & GDCRASH_LAST) - gamedata->crashflags |= GDCRASH_ANY; - gamedata->everloadedaddon = (boolean)READUINT8(save.p); gamedata->eversavedreplay = (boolean)READUINT8(save.p); gamedata->everseenspecial = (boolean)READUINT8(save.p); @@ -4782,9 +4783,34 @@ void G_LoadGameData(void) } } +// G_DirtyGameData +// Modifies the gamedata as little as possible to maintain safety in a crash event, while still recording it. +void G_DirtyGameData(void) +{ + FILE *handle = NULL; + const UINT8 writebytesource = true; + + if (gamedata) + gamedata->evercrashed = true; + + //if (FIL_WriteFileOK(name)) + handle = fopen(va(pandf, srb2home, gamedatafilename), "r+"); + + if (!handle) + return; + + // Write a dirty byte immediately after the gamedata check + minor version. + if (fseek(handle, 5, SEEK_SET) != -1) + fwrite(&writebytesource, 1, 1, handle); + + fclose(handle); + + return; +} + // G_SaveGameData // Saves the main data file, which stores information such as emblems found, etc. -void G_SaveGameData(boolean dirty) +void G_SaveGameData(void) { size_t length; INT32 i, j, numcups; @@ -4805,10 +4831,11 @@ void G_SaveGameData(boolean dirty) return; } - length = (4+1+4+4+ + length = (4+1+1+ + 4+4+ (4*GDGT_MAX)+ 4+1+1+2+ - 1+1+1+1+ + 1+1+1+ 4+ (MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+ 4+2); @@ -4836,6 +4863,13 @@ void G_SaveGameData(boolean dirty) WRITEUINT32(save.p, GD_VERSIONCHECK); // 4 WRITEUINT8(save.p, GD_VERSIONMINOR); // 1 + + // Crash dirtiness + // cannot move, see G_DirtyGameData + WRITEUINT8(save.p, gamedata->evercrashed); // 1 + + // Statistics + WRITEUINT32(save.p, gamedata->totalplaytime); // 4 WRITEUINT32(save.p, gamedata->totalrings); // 4 @@ -4849,13 +4883,6 @@ void G_SaveGameData(boolean dirty) WRITEUINT8(save.p, gamedata->keyspending); // 1 WRITEUINT16(save.p, gamedata->chaokeys); // 2 - { - UINT8 crashflags = (gamedata->crashflags & GDCRASH_ANY); - if (dirty) - crashflags |= GDCRASH_LAST; - WRITEUINT8(save.p, crashflags); // 1 - } - WRITEUINT8(save.p, gamedata->everloadedaddon); // 1 WRITEUINT8(save.p, gamedata->eversavedreplay); // 1 WRITEUINT8(save.p, gamedata->everseenspecial); // 1 diff --git a/src/g_game.h b/src/g_game.h index d85b98187..3f122f6c4 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -175,7 +175,8 @@ boolean G_IsTitleCardAvailable(void); // Can be called by the startup code or M_Responder, calls P_SetupLevel. void G_LoadGame(UINT32 slot, INT16 mapoverride); -void G_SaveGameData(boolean dirty); +void G_SaveGameData(void); +void G_DirtyGameData(void); void G_SaveGame(UINT32 slot, INT16 mapnum); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6602f74cd..2561238f2 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -373,7 +373,7 @@ void M_PlayMenuJam(void) { menu_t *refMenu = (menuactive ? currentMenu : restoreMenu); static boolean loserclubpermitted = false; - boolean loserclub = (loserclubpermitted && (gamedata->crashflags & GDCRASH_LOSERCLUB)); + boolean loserclub = (loserclubpermitted && (gamedata->musicflags & GDMUSIC_LOSERCLUB)); if (challengesmenu.pending) { diff --git a/src/k_podium.c b/src/k_podium.c index b9c6ca831..519c654e3 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -291,7 +291,7 @@ void K_FinishCeremony(void) // Play the noise now M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } /*-------------------------------------------------- @@ -339,7 +339,7 @@ void K_ResetCeremony(void) grandprixinfo.cup->windata[i].got_emerald = true; // Save before playing the noise - G_SaveGameData(true); + G_SaveGameData(); } /*-------------------------------------------------- diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index f4b307cfd..aa98cbcc3 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -428,7 +428,7 @@ void K_CashInPowerLevels(void) if (gamedataupdate) { M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } //CONS_Printf("========\n"); @@ -644,6 +644,6 @@ void K_PlayerForfeit(UINT8 playerNum, boolean pointLoss) pr->powerlevels[powerType] = yourPower + inc; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } } diff --git a/src/m_cond.c b/src/m_cond.c index 518c7780e..55eeeb300 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -529,7 +529,8 @@ void M_ClearStats(void) gamedata->everloadedaddon = false; gamedata->eversavedreplay = false; gamedata->everseenspecial = false; - gamedata->crashflags = 0; + gamedata->evercrashed = false; + gamedata->musicflags = 0; } void M_ClearSecrets(void) @@ -764,9 +765,9 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_REPLAY: return (gamedata->eversavedreplay == true); case UC_CRASH: - if (gamedata->crashflags & (GDCRASH_LAST|GDCRASH_ANY)) + if (gamedata->evercrashed) { - gamedata->crashflags |= GDCRASH_LOSERCLUB; + gamedata->musicflags |= GDMUSIC_LOSERCLUB; return true; } return false; @@ -1224,7 +1225,7 @@ static const char *M_GetConditionString(condition_t *cn) case UC_REPLAY: return "save a replay after finishing a round"; case UC_CRASH: - if (gamedata->crashflags & (GDCRASH_LAST|GDCRASH_ANY)) + if (gamedata->evercrashed) return "launch \"Dr. Robotnik's Ring Racers\" again after a game crash"; return NULL; diff --git a/src/m_cond.h b/src/m_cond.h index 968d5c38e..f65bba15b 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -217,9 +217,7 @@ typedef enum #endif #define challengegridloops (gamedata->challengegridwidth >= CHALLENGEGRIDLOOPWIDTH) -#define GDCRASH_LAST 0x01 -#define GDCRASH_ANY 0x02 -#define GDCRASH_LOSERCLUB 0x04 +#define GDMUSIC_LOSERCLUB 0x01 // This is the largest number of 9s that will fit in UINT32 and UINT16 respectively. #define GDMAX_RINGS 999999999 @@ -281,7 +279,8 @@ struct gamedata_t boolean everloadedaddon; boolean eversavedreplay; boolean everseenspecial; - UINT8 crashflags; + boolean evercrashed; + UINT8 musicflags; }; extern gamedata_t *gamedata; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 5e462287b..c5cda4bc8 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -399,7 +399,7 @@ void M_ChallengesTick(void) { // All done! Let's save the unlocks we've busted open. challengesmenu.pending = challengesmenu.chaokeyadd = false; - G_SaveGameData(true); + G_SaveGameData(); } } else if (challengesmenu.pending) diff --git a/src/p_setup.c b/src/p_setup.c index 939e8cd22..d2cd02ede 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7501,7 +7501,7 @@ static void P_InitGametype(void) // Started a game? Move on to the next jam when you go back to the title screen CV_SetValue(&cv_menujam_update, 1); - gamedata->crashflags &= ~GDCRASH_LOSERCLUB; + gamedata->musicflags = 0; } struct minimapinfo minimapinfo; @@ -7995,7 +7995,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } G_AddMapToBuffer(gamemap-1); diff --git a/src/p_tick.c b/src/p_tick.c index 1a5ae508c..26741e8a5 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -620,7 +620,7 @@ void P_Ticker(boolean run) // TODO would this be laggy with more conditions in play... if (((!demo.playback && leveltime > introtime && M_UpdateUnlockablesAndExtraEmblems(true, false)) || (gamedata && gamedata->deferredsave))) - G_SaveGameData(true); + G_SaveGameData(); } // Keep track of how long they've been playing! diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index b60e30269..9d593115b 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -328,6 +328,7 @@ FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num) { D_QuitNetGame(); // Fix server freezes CL_AbortDownloadResume(); + G_DirtyGameData(); #ifdef UNIXBACKTRACE write_backtrace(num); #endif @@ -1448,7 +1449,7 @@ void I_Quit(void) if (Playing()) K_PlayerForfeit(consoleplayer, true); - G_SaveGameData(false); // Tails 12-08-2002 -- undirty your save + G_SaveGameData(); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. @@ -1532,7 +1533,8 @@ void I_Error(const char *error, ...) if (errorcount == 8) { M_SaveConfig(NULL); - G_SaveGameData(true); + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); } if (errorcount > 20) { @@ -1563,7 +1565,8 @@ void I_Error(const char *error, ...) M_SaveConfig(NULL); // save game config, cvars.. D_SaveBan(); // save the ban list - G_SaveGameData(true); // Tails 12-08-2002 + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); // Tails 12-08-2002 // Shutdown. Here might be other errors. diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index 032c535d6..2af8f4cf9 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -357,6 +357,7 @@ static void signal_handler(INT32 num) sigmsg = sigdef; } + G_DirtyGameData(); I_OutputMsg("signal_handler() error: %s\n", sigmsg); signal(num, SIG_DFL); //default signal action raise(num); @@ -2983,7 +2984,7 @@ void I_Quit(void) if (Playing()) K_PlayerForfeit(consoleplayer, true); - G_SaveGameData(false); // Tails 12-08-2002 -- undirty your save + G_SaveGameData(); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. @@ -3078,7 +3079,8 @@ void I_Error(const char *error, ...) if (errorcount == 9) { M_SaveConfig(NULL); - G_SaveGameData(true); + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); } if (errorcount > 20) { @@ -3142,7 +3144,8 @@ void I_Error(const char *error, ...) #ifndef NONET D_SaveBan(); // save the ban list #endif - G_SaveGameData(true); // Tails 12-08-2002 + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); // Tails 12-08-2002 // Shutdown. Here might be other errors. if (demorecording) diff --git a/src/w_wad.c b/src/w_wad.c index 481ef672e..bf2a3f640 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -819,7 +819,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) { gamedata->everloadedaddon = true; M_UpdateUnlockablesAndExtraEmblems(true, true); - G_SaveGameData(true); + G_SaveGameData(); } switch(type = ResourceFileDetect(filename)) diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c index c4f4f4859..2d2a64f02 100644 --- a/src/win32ce/win_sys.c +++ b/src/win32ce/win_sys.c @@ -469,6 +469,7 @@ static void signal_handler(int num) char sigdef[64]; D_QuitNetGame(); // Fix server freezes + G_DirtyGameData(); I_ShutdownSystem(); switch (num) @@ -607,7 +608,8 @@ void I_Error(const char *error, ...) if (errorcount == 7) { M_SaveConfig(NULL); - G_SaveGameData(true); + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); } if (errorcount > 20) { @@ -636,7 +638,8 @@ void I_Error(const char *error, ...) if (!errorcount) { M_SaveConfig(NULL); // save game config, cvars.. - G_SaveGameData(true); + G_DirtyGameData(); // done first in case an error is in G_SaveGameData + G_SaveGameData(); } // save demo, could be useful for debug @@ -726,7 +729,7 @@ void I_Quit(void) G_CheckDemoStatus(); M_SaveConfig(NULL); // save game config, cvars.. - G_SaveGameData(false); // undirty your save + G_SaveGameData(); // undirty your save // maybe it needs that the ticcount continues, // or something else that will be finished by I_ShutdownSystem(),