diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index 7a955895827..9f0d9f74cfc 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -1149,16 +1149,21 @@ Bool RecorderClass::playbackFile(AsciiString filename) } #endif - Bool isMultiplayer = m_gameInfo.getSlot(header.localPlayerIndex)->getIP() != 0; - m_crcInfo = CRCInfo(header.localPlayerIndex, isMultiplayer); REPLAY_CRC_INTERVAL = m_gameInfo.getCRCInterval(); - DEBUG_LOG(("Player index is %d, replay CRC interval is %d", m_crcInfo.getLocalPlayer(), REPLAY_CRC_INTERVAL)); Int difficulty = 0; m_file->read(&difficulty, sizeof(difficulty)); m_file->read(&m_originalGameMode, sizeof(m_originalGameMode)); + // TheSuperHackers @bugfix bobtista 30/08/2026 A replay header is allowed to carry a local player + // index of -1 and getSlot returns NULL for it, so reading the slot to tell a network game from a + // local one dereferenced NULL. The recorded game mode answers the same question directly, so the + // crc queue is now primed from the mode and the local slot is no longer read here. + const Bool isMultiplayer = m_originalGameMode == GAME_LAN || m_originalGameMode == GAME_INTERNET; + m_crcInfo = CRCInfo(header.localPlayerIndex, isMultiplayer); + DEBUG_LOG(("Player index is %d, replay CRC interval is %d", m_crcInfo.getLocalPlayer(), REPLAY_CRC_INTERVAL)); + Int rankPoints = 0; m_file->read(&rankPoints, sizeof(rankPoints)); diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 932caa24f82..ac71eb4247c 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1315,7 +1315,11 @@ void GameLogic::tryStartNewGame( Bool loadingSaveGame ) d.setInt(TheKey_multiplayerStartIndex, slot->getStartPos()); // d.setBool(TheKey_multiplayerIsLocal, slot->isLocalPlayer()); // d.setBool(TheKey_multiplayerIsLocal, slot->getIP() == game->getLocalIP()); - d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && (slot->getName().compare(TheGameInfo->getSlot(TheGameInfo->getLocalSlotNum())->getName().str()) == 0)); + // TheSuperHackers @bugfix bobtista 30/08/2026 A replay recorded without a local player has no + // local slot number and getSlot returns NULL for it, so no slot can be the local one. + const Int localSlotNum = TheGameInfo->getLocalSlotNum(); + const GameSlot *localGameSlot = localSlotNum >= 0 ? TheGameInfo->getSlot(localSlotNum) : nullptr; + d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && localGameSlot != nullptr && (slot->getName().compare(localGameSlot->getName().str()) == 0)); /* if (slot->getIP() == game->getLocalIP()) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 9d71eb45b1b..68fc12acb0b 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1152,16 +1152,21 @@ Bool RecorderClass::playbackFile(AsciiString filename) } #endif - Bool isMultiplayer = m_gameInfo.getSlot(header.localPlayerIndex)->getIP() != 0; - m_crcInfo = CRCInfo(header.localPlayerIndex, isMultiplayer); REPLAY_CRC_INTERVAL = m_gameInfo.getCRCInterval(); - DEBUG_LOG(("Player index is %d, replay CRC interval is %d", m_crcInfo.getLocalPlayer(), REPLAY_CRC_INTERVAL)); Int difficulty = 0; m_file->read(&difficulty, sizeof(difficulty)); m_file->read(&m_originalGameMode, sizeof(m_originalGameMode)); + // TheSuperHackers @bugfix bobtista 30/08/2026 A replay header is allowed to carry a local player + // index of -1 and getSlot returns NULL for it, so reading the slot to tell a network game from a + // local one dereferenced NULL. The recorded game mode answers the same question directly, so the + // crc queue is now primed from the mode and the local slot is no longer read here. + const Bool isMultiplayer = m_originalGameMode == GAME_LAN || m_originalGameMode == GAME_INTERNET; + m_crcInfo = CRCInfo(header.localPlayerIndex, isMultiplayer); + DEBUG_LOG(("Player index is %d, replay CRC interval is %d", m_crcInfo.getLocalPlayer(), REPLAY_CRC_INTERVAL)); + Int rankPoints = 0; m_file->read(&rankPoints, sizeof(rankPoints)); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index 60bae04f186..71e8f72b235 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -1476,7 +1476,11 @@ void GameLogic::tryStartNewGame( Bool loadingSaveGame ) d.setInt(TheKey_multiplayerStartIndex, slot->getStartPos()); // d.setBool(TheKey_multiplayerIsLocal, slot->isLocalPlayer()); // d.setBool(TheKey_multiplayerIsLocal, slot->getIP() == game->getLocalIP()); - d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && (slot->getName().compare(TheGameInfo->getSlot(TheGameInfo->getLocalSlotNum())->getName().str()) == 0)); + // TheSuperHackers @bugfix bobtista 30/08/2026 A replay recorded without a local player has no + // local slot number and getSlot returns NULL for it, so no slot can be the local one. + const Int localSlotNum = TheGameInfo->getLocalSlotNum(); + const GameSlot *localGameSlot = localSlotNum >= 0 ? TheGameInfo->getSlot(localSlotNum) : nullptr; + d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && localGameSlot != nullptr && (slot->getName().compare(localGameSlot->getName().str()) == 0)); /* if (slot->getIP() == game->getLocalIP())