diff --git a/Generals/Code/GameEngine/Include/Common/Recorder.h b/Generals/Code/GameEngine/Include/Common/Recorder.h index c49b71dda2f..b52f29a4552 100644 --- a/Generals/Code/GameEngine/Include/Common/Recorder.h +++ b/Generals/Code/GameEngine/Include/Common/Recorder.h @@ -192,7 +192,7 @@ class RecorderClass : public SubsystemInterface AsciiString readAsciiString(); ///< Read the next string from m_file using ascii characters. UnicodeString readUnicodeString(); ///< Read the next string from m_file using unicode characters. - void readNextFrame(); ///< Read the next frame number to execute a command on. + void readNextFrame(); ///< Read and validate the next frame number to execute a command on. void appendNextCommand(); ///< Read the next GameMessage and append it to TheCommandList. void writeArgument(GameMessageArgumentDataType type, const GameMessageArgumentType arg); void readArgument(GameMessageArgumentDataType type, GameMessage *msg); diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index e9c55ee350b..00945712ff1 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -438,7 +438,7 @@ void RecorderClass::updatePlayback() { // While there are commands to be queued up for this frame, do it. while (m_nextFrame == curFrame) { appendNextCommand(); // append the next command to TheCommandQueue - readNextFrame(); // Read the next command's frame number for playback. + readNextFrame(); // Read and validate the next command's frame number for playback. } } @@ -1228,6 +1228,7 @@ Bool RecorderClass::playbackFile(AsciiString filename) // Otherwise a crc message remains and messes up the crc calculation on the restarted replay. TheCommandList->reset(); + m_nextFrame = 0; readNextFrame(); // readNextFrame() closes m_file via stopPlayback() if the first frame cannot be read. if(m_file == nullptr) @@ -1324,9 +1325,29 @@ AsciiString RecorderClass::readAsciiString() { * Read the frame number for the next command in the playback file. If the end of the file is reached, the playback * is stopped and the next frame is said to be -1. */ -void RecorderClass::readNextFrame() { +void RecorderClass::readNextFrame() +{ + const UnsignedInt curFrame = m_nextFrame; + Int bytesRead = m_file->read(&m_nextFrame, sizeof(m_nextFrame)); - if (bytesRead != sizeof(m_nextFrame)) { + if (bytesRead == sizeof(m_nextFrame)) + { + // TheSuperHackers @bugfix Check whether the next frame value is within a reasonable range + // to avoid prolonging playback due to potentially corrupted data. + const bool isInValidRange = m_nextFrame >= curFrame && m_nextFrame < curFrame + 3600 * LOGICFRAMES_PER_SECOND; + if (!isInValidRange) + { + if (m_doingAnalysis) + return; + + DEBUG_LOG(("RecorderClass::readNextFrame - current frame %d, next frame %d is in unexpected distance", + TheGameLogic->getFrame(), m_nextFrame)); + m_nextFrame = -1; + stopPlayback(); + } + } + else + { DEBUG_LOG(("RecorderClass::readNextFrame - read failed on frame %d", TheGameLogic->getFrame())); m_nextFrame = -1; stopPlayback(); diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h b/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h index f49b50495ae..f9e5e6ee1e4 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Recorder.h @@ -192,7 +192,7 @@ class RecorderClass : public SubsystemInterface AsciiString readAsciiString(); ///< Read the next string from m_file using ascii characters. UnicodeString readUnicodeString(); ///< Read the next string from m_file using unicode characters. - void readNextFrame(); ///< Read the next frame number to execute a command on. + void readNextFrame(); ///< Read and validate the next frame number to execute a command on. void appendNextCommand(); ///< Read the next GameMessage and append it to TheCommandList. void writeArgument(GameMessageArgumentDataType type, const GameMessageArgumentType arg); void readArgument(GameMessageArgumentDataType type, GameMessage *msg); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 1b1bb00f6d2..9b6c6ad0b51 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -438,7 +438,7 @@ void RecorderClass::updatePlayback() { // While there are commands to be queued up for this frame, do it. while (m_nextFrame == curFrame) { appendNextCommand(); // append the next command to TheCommandQueue - readNextFrame(); // Read the next command's frame number for playback. + readNextFrame(); // Read and validate the next command's frame number for playback. } } @@ -1231,6 +1231,7 @@ Bool RecorderClass::playbackFile(AsciiString filename) // Otherwise a crc message remains and messes up the crc calculation on the restarted replay. TheCommandList->reset(); + m_nextFrame = 0; readNextFrame(); // readNextFrame() closes m_file via stopPlayback() if the first frame cannot be read. if(m_file == nullptr) @@ -1327,9 +1328,29 @@ AsciiString RecorderClass::readAsciiString() { * Read the frame number for the next command in the playback file. If the end of the file is reached, the playback * is stopped and the next frame is said to be -1. */ -void RecorderClass::readNextFrame() { +void RecorderClass::readNextFrame() +{ + const UnsignedInt curFrame = m_nextFrame; + Int bytesRead = m_file->read(&m_nextFrame, sizeof(m_nextFrame)); - if (bytesRead != sizeof(m_nextFrame)) { + if (bytesRead == sizeof(m_nextFrame)) + { + // TheSuperHackers @bugfix Check whether the next frame value is within a reasonable range + // to avoid prolonging playback due to potentially corrupted data. + const bool isInValidRange = m_nextFrame >= curFrame && m_nextFrame < curFrame + 3600 * LOGICFRAMES_PER_SECOND; + if (!isInValidRange) + { + if (m_doingAnalysis) + return; + + DEBUG_LOG(("RecorderClass::readNextFrame - current frame %d, next frame %d is in unexpected distance", + TheGameLogic->getFrame(), m_nextFrame)); + m_nextFrame = -1; + stopPlayback(); + } + } + else + { DEBUG_LOG(("RecorderClass::readNextFrame - read failed on frame %d", TheGameLogic->getFrame())); m_nextFrame = -1; stopPlayback();