From 688eb0abe867890237255870d063ed146fe9c4b8 Mon Sep 17 00:00:00 2001 From: morfidon <57798071+morfidon@users.noreply.github.com> Date: Wed, 11 Mar 2026 19:23:29 +0100 Subject: [PATCH] Harden hotkey load sanitization Move hotkey loading out of InitPlayer so it runs only after spell sources are rebuilt. Also make LoadHotkeys sanitize the selected spell and hotkeys itself, including when the sidecar hotkeys file is missing, and resync queuedSpell in the player load paths. --- Source/loadsave.cpp | 9 +++++++-- Source/pfile.cpp | 8 +++++++- Source/player.cpp | 2 -- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 838c0c3ce..0c161e3ad 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -2332,8 +2332,10 @@ size_t HotkeysSize(size_t nHotkeys = NumHotkeys) void LoadHotkeys() { LoadHelper file(OpenSaveArchive(gSaveNumber), "hotkeys"); - if (!file.IsValid()) + if (!file.IsValid()) { + SanitizePlayerSpellSelections(*MyPlayer); return; + } Player &myPlayer = *MyPlayer; size_t nHotkeys = 4; // Defaults to old save format number @@ -2369,6 +2371,7 @@ void LoadHotkeys() // Load the selected spell last myPlayer._pRSpell = static_cast(file.NextLE()); myPlayer._pRSplType = static_cast(file.NextLE()); + SanitizePlayerSpellSelections(myPlayer); } void SaveHotkeys(SaveWriter &saveWriter, const Player &player) @@ -2521,7 +2524,9 @@ tl::expected LoadGame(bool firstflag) LoadPlayer(file, myPlayer); ValidatePlayer(); CalcPlrInv(myPlayer, false); - SanitizePlayerSpellSelections(myPlayer); + LoadHotkeys(); + myPlayer.queuedSpell.spellId = myPlayer._pRSpell; + myPlayer.queuedSpell.spellType = myPlayer._pRSplType; if (sgGameInitInfo.nDifficulty < DIFF_NORMAL || sgGameInitInfo.nDifficulty > DIFF_HELL) sgGameInitInfo.nDifficulty = DIFF_NORMAL; diff --git a/Source/pfile.cpp b/Source/pfile.cpp index 2580cee36..7913f3493 100644 --- a/Source/pfile.cpp +++ b/Source/pfile.cpp @@ -779,7 +779,13 @@ void pfile_read_player_from_save(uint32_t saveNum, Player &player) LoadHeroItems(player); RemoveAllInvalidItems(player); CalcPlrInv(player, false); - SanitizePlayerSpellSelections(player); + if (&player == MyPlayer) { + LoadHotkeys(); + player.queuedSpell.spellId = player._pRSpell; + player.queuedSpell.spellType = player._pRSplType; + } else { + SanitizePlayerSpellSelections(player); + } } void pfile_save_level() diff --git a/Source/player.cpp b/Source/player.cpp index 07cf197f9..1a0e2251d 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -2487,8 +2487,6 @@ void InitPlayer(Player &player, bool firstTime) if (firstTime) { player._pRSplType = SpellType::Invalid; player._pRSpell = SpellID::Invalid; - if (&player == MyPlayer) - LoadHotkeys(); player._pSBkSpell = SpellID::Invalid; player.queuedSpell.spellId = player._pRSpell; player.queuedSpell.spellType = player._pRSplType;