RecorderClass::playbackFile determines whether the recorded game was multiplayer by reading the local player slot:
Bool isMultiplayer = m_gameInfo.getSlot(header.localPlayerIndex)->getIP() != 0;
readReplayHeader accepts -1 as a valid local player index, and GameInfo::getSlot returns nullptr for a negative slot, so a replay carrying -1 crashes before playback begins.
This dereference was introduced by #545 when replay CRC handling began distinguishing network games from local games. Before #545, the local player index was stored without accessing its slot.
The -1 value remains supported by the replay parser and can occur in legacy or externally generated replays. The LAN and GameSpy recording paths can also preserve the default -1 if they fail to identify the local slot, although those paths have not been reproduced.
The replay stores its original game mode after the header, so GAME_LAN and GAME_INTERNET can identify network replays without reading the local slot. This also preserves the correct replay CRC alignment when a network replay has no local slot.
This affects every replay entry point using RecorderClass::playbackFile, including the Replay menu.
GameLogic::startNewGame dereferences the same missing slot a second time, and this one is what actually ends the process:
d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && (slot->getName().compare(TheGameInfo->getSlot(TheGameInfo->getLocalSlotNum())->getName().str()) == 0));
readReplayHeader only calls setLocalIP when the index is not negative, so a replay carrying -1 leaves the local IP unset, getLocalSlotNum matches no slot and returns -1, and the lookup dereferences nullptr. A replay byte-edited to carry -1 crashes here with EXCEPTION_ACCESS_VIOLATION reading address 0x28, and the log records GameInfo::getSlot - Invalid slot number immediately before the dump.
RecorderClass::playbackFiledetermines whether the recorded game was multiplayer by reading the local player slot:Bool isMultiplayer = m_gameInfo.getSlot(header.localPlayerIndex)->getIP() != 0;readReplayHeaderaccepts-1as a valid local player index, andGameInfo::getSlotreturnsnullptrfor a negative slot, so a replay carrying-1crashes before playback begins.This dereference was introduced by #545 when replay CRC handling began distinguishing network games from local games. Before #545, the local player index was stored without accessing its slot.
The
-1value remains supported by the replay parser and can occur in legacy or externally generated replays. The LAN and GameSpy recording paths can also preserve the default-1if they fail to identify the local slot, although those paths have not been reproduced.The replay stores its original game mode after the header, so
GAME_LANandGAME_INTERNETcan identify network replays without reading the local slot. This also preserves the correct replay CRC alignment when a network replay has no local slot.This affects every replay entry point using
RecorderClass::playbackFile, including the Replay menu.GameLogic::startNewGamedereferences the same missing slot a second time, and this one is what actually ends the process:readReplayHeaderonly callssetLocalIPwhen the index is not negative, so a replay carrying-1leaves the local IP unset,getLocalSlotNummatches no slot and returns-1, and the lookup dereferencesnullptr. A replay byte-edited to carry-1crashes here withEXCEPTION_ACCESS_VIOLATIONreading address0x28, and the log recordsGameInfo::getSlot - Invalid slot numberimmediately before the dump.