diff --git a/src/d_main.c b/src/d_main.c index 3566e7f3d..b1f09aaa8 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1473,6 +1473,12 @@ void D_SRB2Main(void) //--------------------------------------------------------- CONFIG.CFG M_FirstLoadConfig(); // WARNING : this do a "COM_BufExecute()" + if (M_CheckParm("-gamedata") && M_IsNextParm()) + { + // Moved from G_LoadGameData itself, as it would cause some crazy + // confusion issues when loading mods. + strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename); + } G_LoadGameData(); #if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4e90db0dc..73bfe9e17 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4613,11 +4613,11 @@ static void Fishcake_OnChange(void) static void Command_Isgamemodified_f(void) { if (savemoddata) - CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n")); + CONS_Printf(M_GetText("modifiedgame is true, but you can save time data in this mod.\n")); else if (modifiedgame) - CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n")); + CONS_Printf(M_GetText("modifiedgame is true, time data can't be saved\n")); else - CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n")); + CONS_Printf(M_GetText("modifiedgame is false, you can save time data\n")); } static void Command_Cheats_f(void) diff --git a/src/f_finale.c b/src/f_finale.c index bca8e3ba6..73f6281a1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1575,7 +1575,9 @@ void F_GameEvaluationDrawer(void) { V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:"); - if (!(netgame) && (!modifiedgame || savemoddata)) + if (netgame) + V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!"); + else { INT32 startcoord = 32; @@ -1590,10 +1592,6 @@ void F_GameEvaluationDrawer(void) } } } - else if (netgame) - V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!"); - else - V_DrawString(8, 96, V_YELLOWMAP, "Modified games\ncan't unlock\nextras!"); } #endif @@ -1657,7 +1655,7 @@ void F_GameEvaluationTicker(void) HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!"); S_StartSound(NULL, sfx_s3k68); } - else if (!modifiedgame || savemoddata) + else { ++timesBeaten; @@ -1672,13 +1670,6 @@ void F_GameEvaluationTicker(void) G_SaveGameData(); } - else - { - HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8); - HU_SetCEchoDuration(6); - HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Modified games can't unlock extras!"); - S_StartSound(NULL, sfx_s3k68); - } } } diff --git a/src/g_game.c b/src/g_game.c index 349d90558..edddcc050 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -755,7 +755,7 @@ void G_SetGameModified(boolean silent) savemoddata = false; if (!silent) - CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to record statistics.\n")); + CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to play Record Attack.\n")); // If in record attack recording, cancel it. if (modeattacking) @@ -3826,8 +3826,7 @@ static void G_UpdateVisited(void) { boolean spec = G_IsSpecialStage(gamemap); // Update visitation flags? - if ((!modifiedgame || savemoddata) // Not modified - && !multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode + if (!multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode && !stagefailed) // Did not fail the stage { UINT8 earnedEmblems; @@ -3895,14 +3894,18 @@ static void G_HandleSaveLevel(void) remove(liveeventbackup); cursaveslot = 0; } - else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking)) + else if (!(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking)) + { G_SaveGame((UINT32)cursaveslot, spstage_start); + } } } // and doing THIS here means you don't lose your progress if you close the game mid-intermission else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) - && (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(lastmap+1)) + && cursaveslot > 0 && CanSaveLevel(lastmap+1)) + { G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages + } } // @@ -4178,8 +4181,10 @@ static void G_DoContinued(void) tokenlist = 0; token = 0; - if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0) + if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && cursaveslot > 0) + { G_SaveGameOver((UINT32)cursaveslot, true); + } // Reset # of lives pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers]; @@ -4244,13 +4249,17 @@ void G_LoadGameSettings(void) S_InitRuntimeSounds(); } +#define GAMEDATA_ID 0x86E4A27C // Change every major version, as usual +#define COMPAT_GAMEDATA_ID 0xFCAFE211 // Can be removed entirely for 2.3 + // G_LoadGameData // Loads the main data file, which stores information such as emblems found, etc. void G_LoadGameData(void) { size_t length; INT32 i, j; - UINT8 modded = false; + + UINT32 versionID; UINT8 rtemp; //For records @@ -4261,6 +4270,9 @@ void G_LoadGameData(void) UINT8 recmares; INT32 curmare; + // Stop saving, until we successfully load it again. + gamedataloaded = false; + // Clear things so previously read gamedata doesn't transfer // to new gamedata G_ClearRecords(); // main and nights records @@ -4268,27 +4280,35 @@ void G_LoadGameData(void) totalplaytime = 0; // total play time (separate from all) if (M_CheckParm("-nodata")) - return; // Don't load. - - // Allow saving of gamedata beyond this point - gamedataloaded = true; - - if (M_CheckParm("-gamedata") && M_IsNextParm()) { - strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename); + // Don't load at all. + return; } if (M_CheckParm("-resetdata")) - return; // Don't load (essentially, reset). + { + // Don't load, but do save. (essentially, reset) + gamedataloaded = true; + return; + } length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer); - if (!length) // Aw, no game data. Their loss! + if (!length) + { + // No gamedata. We can save a new one. + gamedataloaded = true; return; + } save_p = savebuffer; // Version check - if (READUINT32(save_p) != 0xFCAFE211) + versionID = READUINT32(save_p); + if (versionID != GAMEDATA_ID +#ifdef COMPAT_GAMEDATA_ID // backwards compat behavior + && versionID != COMPAT_GAMEDATA_ID +#endif + ) { const char *gdfolder = "the SRB2 folder"; if (strcmp(srb2home,".")) @@ -4301,13 +4321,26 @@ void G_LoadGameData(void) totalplaytime = READUINT32(save_p); - modded = READUINT8(save_p); +#ifdef COMPAT_GAMEDATA_ID + // Ignore for backwards compat, it'll get fixed when saving. + if (versionID == COMPAT_GAMEDATA_ID) + { + // Old files use a UINT8 here. + READUINT8(save_p); + } + else +#endif + { + // Quick & dirty hash for what mod this save file is for. + UINT32 modID = READUINT32(save_p); + UINT32 expectedID = quickncasehash(timeattackfolder, sizeof timeattackfolder); - // Aha! Someone's been screwing with the save file! - if ((modded && !savemoddata)) - goto datacorrupt; - else if (modded != true && modded != false) - goto datacorrupt; + if (modID != expectedID) + { + // Aha! Someone's been screwing with the save file! + goto datacorrupt; + } + } // TODO put another cipher on these things? meh, I don't care... for (i = 0; i < NUMMAPS; i++) @@ -4393,6 +4426,12 @@ void G_LoadGameData(void) Z_Free(savebuffer); save_p = NULL; + // Don't consider loaded until it's a success! + // It used to do this much earlier, but this would cause the gamedata to + // save over itself when it I_Errors from the corruption landing point below, + // which can accidentally delete players' legitimate data if the code ever has any tiny mistakes! + gamedataloaded = true; + // Silent update unlockables in case they're out of sync with conditions M_SilentUpdateUnlockablesAndEmblems(); @@ -4432,20 +4471,12 @@ void G_SaveGameData(void) return; } - if (modifiedgame && !savemoddata) - { - free(savebuffer); - save_p = savebuffer = NULL; - return; - } - // Version test - WRITEUINT32(save_p, 0xFCAFE211); + WRITEUINT32(save_p, GAMEDATA_ID); WRITEUINT32(save_p, totalplaytime); - btemp = (UINT8)(savemoddata || modifiedgame); - WRITEUINT8(save_p, btemp); + WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder)); // TODO put another cipher on these things? meh, I don't care... for (i = 0; i < NUMMAPS; i++) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index c037abcd7..ebef02319 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2994,7 +2994,7 @@ static void HU_DrawCoopOverlay(void) V_DrawSmallScaledPatch(148, 172, 0, tokenicon); } - if (LUA_HudEnabled(hud_tabemblems) && (!modifiedgame || savemoddata)) + if (LUA_HudEnabled(hud_tabemblems)) { V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems)); V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon); diff --git a/src/m_cond.c b/src/m_cond.c index 1406317c5..58ee71fec 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -200,9 +200,6 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void) char cechoText[992] = ""; UINT8 cechoLines = 0; - if (modifiedgame && !savemoddata) - return false; - M_CheckUnlockConditions(); // Go through extra emblems diff --git a/src/m_menu.c b/src/m_menu.c index 83b788fd5..55733e552 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -757,12 +757,12 @@ static menuitem_t SR_EmblemHintMenu[] = static menuitem_t SP_MainMenu[] = { // Note: If changing the positions here, also change them in M_SinglePlayerMenu() - {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 76}, - {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 84}, - {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 92}, - {IT_SECRET, NULL, "Marathon Run", M_Marathon, 100}, - {IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 108}, - {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116} + {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 76}, + {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 84}, + {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 92}, + {IT_SECRET, NULL, "Marathon Run", M_Marathon, 100}, + {IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 108}, + {IT_CALL | IT_STRING, NULL, "Statistics", M_Statistics, 116} }; enum @@ -3514,9 +3514,11 @@ boolean M_Responder(event_t *ev) { if (((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_CALL || (currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SUBMENU) - && (currentMenu->menuitems[itemOn].status & IT_CALLTYPE)) + && (currentMenu->menuitems[itemOn].status & IT_CALLTYPE)) { #ifndef DEVELOP + // TODO: Replays are scary, so I left the remaining instances of this alone. + // It'd be nice to get rid of this once and for all though! if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata) { S_StartSound(NULL, sfx_skid); @@ -8701,12 +8703,6 @@ static void M_DrawLoad(void) loadgameoffset = 0; M_DrawLoadGameData(); - - if (modifiedgame && !savemoddata) - { - V_DrawCenteredThinString(BASEVIDWIDTH/2, 184, 0, "\x85WARNING: \x80The game is modified."); - V_DrawCenteredThinString(BASEVIDWIDTH/2, 192, 0, "Progress will not be saved."); - } } // @@ -9008,18 +9004,12 @@ static void M_HandleLoadSave(INT32 choice) break; case KEY_ENTER: - if (ultimate_selectable && saveSlotSelected == NOSAVESLOT && !savemoddata && !modifiedgame) + if (ultimate_selectable && saveSlotSelected == NOSAVESLOT) { loadgamescroll = 0; S_StartSound(NULL, sfx_skid); M_StartMessage("Are you sure you want to play\n\x85ultimate mode\x80? It isn't remotely fair,\nand you don't even get an emblem for it.\n\n(Press 'Y' to confirm)\n",M_SaveGameUltimateResponse,MM_YESNO); } - else if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives == -42 && !(!modifiedgame || savemoddata)) - { - loadgamescroll = 0; - S_StartSound(NULL, sfx_skid); - M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING); - } else if (saveSlotSelected == NOSAVESLOT || savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves" { loadgamescroll = 0; diff --git a/src/p_inter.c b/src/p_inter.c index dd3e0f9c2..1094c3045 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2583,7 +2583,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers) { numgameovers++; - if ((!modifiedgame || savemoddata) && cursaveslot > 0) + if (cursaveslot > 0) G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0)); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 4533a2ce8..ac91e0f7f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11972,11 +11972,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) break; case MT_EMBLEM: if (netgame || multiplayer) - return false; // Single player - - if (modifiedgame && !savemoddata) - return false; // No cheating!! - + return false; // Single player (You're next on my shit list) break; default: break; diff --git a/src/p_setup.c b/src/p_setup.c index deb308da2..c41a5d94e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7746,7 +7746,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) nextmapoverride = 0; skipstats = 0; - if (!(netgame || multiplayer || demoplayback) && (!modifiedgame || savemoddata)) + if (!(netgame || multiplayer || demoplayback)) mapvisited[gamemap-1] |= MV_VISITED; else if (netgame || multiplayer) mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently @@ -7764,8 +7764,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) { // I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020 if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode) - && (!modifiedgame || savemoddata) && cursaveslot > 0) + && cursaveslot > 0) + { G_SaveGame((UINT32)cursaveslot, gamemap); + } // If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted. } lastmaploaded = gamemap; // HAS to be set after saving!! diff --git a/src/p_spec.c b/src/p_spec.c index 82337d2f6..94659e0d7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2937,7 +2937,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 441: // Trigger unlockable - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer)) + if (!(netgame || multiplayer)) { INT32 trigid = line->args[0]; diff --git a/src/y_inter.c b/src/y_inter.c index 7faceff50..e0f246eee 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1092,7 +1092,7 @@ void Y_Ticker(void) S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching! // Update when done with tally - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback) + if (!(netgame || multiplayer) && !demoplayback) { if (M_UpdateUnlockablesAndExtraEmblems()) S_StartSound(NULL, sfx_s3k68); @@ -1223,7 +1223,7 @@ void Y_Ticker(void) S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching! // Update when done with tally - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && !demoplayback) + if (!(netgame || multiplayer) && !demoplayback) { if (M_UpdateUnlockablesAndExtraEmblems()) S_StartSound(NULL, sfx_s3k68);