From f6557b1a5385bb5f7368e04b3eebe9e11497c71e Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Fri, 25 Apr 2025 01:15:55 +0200 Subject: [PATCH 1/3] Fix replay header memory corruption on VS22 --- .../GameEngine/Source/Common/Recorder.cpp | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 46ebcaa78e..d22811567d 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -56,10 +56,17 @@ Int REPLAY_CRC_INTERVAL = 100; const char *replayExtention = ".rep"; const char *lastReplayFileName = "00000000"; // a name the user is unlikely to ever type, but won't cause panic & confusion +// TheSuperHackers @tweak helmutbuhler 25/04/2025 +// The replay header contains two time elements startTime and endTime of type time_t. +// On VC6 this was 32-bit, but on newer compilers it's 64-bit. In order to remain compatible +// we need to load and save them as 32-bit and use this type for that. +// Note that this will overflow on January 18, 2038. +typedef int32_t replay_time_t; + static time_t startTime; static const UnsignedInt startTimeOffset = 6; -static const UnsignedInt endTimeOffset = startTimeOffset + sizeof(time_t); -static const UnsignedInt framesOffset = endTimeOffset + sizeof(time_t); +static const UnsignedInt endTimeOffset = startTimeOffset + sizeof(replay_time_t); +static const UnsignedInt framesOffset = endTimeOffset + sizeof(replay_time_t); static const UnsignedInt desyncOffset = framesOffset + sizeof(UnsignedInt); static const UnsignedInt quitEarlyOffset = desyncOffset + sizeof(Bool); static const UnsignedInt disconOffset = quitEarlyOffset + sizeof(Bool); @@ -75,7 +82,8 @@ void RecorderClass::logGameStart(AsciiString options) if (!fseek(m_file, startTimeOffset, SEEK_SET)) { // save off start time - fwrite(&startTime, sizeof(time_t), 1, m_file); + replay_time_t tmp = (replay_time_t)startTime; + fwrite(&tmp, sizeof(replay_time_t), 1, m_file); } // move back to end of stream #ifdef DEBUG_CRASHING @@ -229,7 +237,8 @@ void RecorderClass::logGameEnd( void ) if (!fseek(m_file, endTimeOffset, SEEK_SET)) { // save off end time - fwrite(&t, sizeof(time_t), 1, m_file); + replay_time_t tmp = (replay_time_t)t; + fwrite(&tmp, sizeof(replay_time_t), 1, m_file); } // move to appropriate offset if (!fseek(m_file, framesOffset, SEEK_SET)) @@ -557,9 +566,9 @@ void RecorderClass::startRecording(GameDifficulty diff, Int originalGameMode, In // // **** if this changes, change the LAN code above **** // - time_t t = 0; - fwrite(&t, sizeof(time_t), 1, m_file); // reserve space for start time - fwrite(&t, sizeof(time_t), 1, m_file); // reserve space for end time + replay_time_t t = 0; + fwrite(&t, sizeof(replay_time_t), 1, m_file); // reserve space for start time + fwrite(&t, sizeof(replay_time_t), 1, m_file); // reserve space for end time UnsignedInt frames = 0; fwrite(&frames, sizeof(UnsignedInt), 1, m_file); // reserve space for duration in frames @@ -836,8 +845,11 @@ Bool RecorderClass::readReplayHeader(ReplayHeader& header) } // read in some stats - fread(&header.startTime, sizeof(time_t), 1, m_file); - fread(&header.endTime, sizeof(time_t), 1, m_file); + replay_time_t tmp; + fread(&tmp, sizeof(replay_time_t), 1, m_file); + header.startTime = tmp; + fread(&tmp, sizeof(replay_time_t), 1, m_file); + header.endTime = tmp; fread(&header.frameDuration, sizeof(UnsignedInt), 1, m_file); From c84c6c969f3b620d7dcc4d91bfc0b2502c25ddf4 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:59:57 +0200 Subject: [PATCH 2/3] Replicate change in Generals --- .../GameEngine/Source/Common/Recorder.cpp | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index e7e2c12f0c..a5037b72be 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -56,10 +56,17 @@ Int REPLAY_CRC_INTERVAL = 100; const char *replayExtention = ".rep"; const char *lastReplayFileName = "00000000"; // a name the user is unlikely to ever type, but won't cause panic & confusion +// TheSuperHackers @tweak helmutbuhler 25/04/2025 +// The replay header contains two time elements startTime and endTime of type time_t. +// On VC6 this was 32-bit, but on newer compilers it's 64-bit. In order to remain compatible +// we need to load and save them as 32-bit and use this type for that. +// Note that this will overflow on January 18, 2038. +typedef int32_t replay_time_t; + static time_t startTime; static const UnsignedInt startTimeOffset = 6; -static const UnsignedInt endTimeOffset = startTimeOffset + sizeof(time_t); -static const UnsignedInt framesOffset = endTimeOffset + sizeof(time_t); +static const UnsignedInt endTimeOffset = startTimeOffset + sizeof(replay_time_t); +static const UnsignedInt framesOffset = endTimeOffset + sizeof(replay_time_t); static const UnsignedInt desyncOffset = framesOffset + sizeof(UnsignedInt); static const UnsignedInt quitEarlyOffset = desyncOffset + sizeof(Bool); static const UnsignedInt disconOffset = quitEarlyOffset + sizeof(Bool); @@ -75,7 +82,8 @@ void RecorderClass::logGameStart(AsciiString options) if (!fseek(m_file, startTimeOffset, SEEK_SET)) { // save off start time - fwrite(&startTime, sizeof(time_t), 1, m_file); + replay_time_t tmp = (replay_time_t)startTime; + fwrite(&tmp, sizeof(replay_time_t), 1, m_file); } // move back to end of stream #ifdef DEBUG_CRASHING @@ -229,7 +237,8 @@ void RecorderClass::logGameEnd( void ) if (!fseek(m_file, endTimeOffset, SEEK_SET)) { // save off end time - fwrite(&t, sizeof(time_t), 1, m_file); + replay_time_t tmp = (replay_time_t)t; + fwrite(&tmp, sizeof(replay_time_t), 1, m_file); } // move to appropriate offset if (!fseek(m_file, framesOffset, SEEK_SET)) @@ -555,9 +564,9 @@ void RecorderClass::startRecording(GameDifficulty diff, Int originalGameMode, In // // **** if this changes, change the LAN Playtest code above **** // - time_t t = 0; - fwrite(&t, sizeof(time_t), 1, m_file); // reserve space for start time - fwrite(&t, sizeof(time_t), 1, m_file); // reserve space for end time + replay_time_t t = 0; + fwrite(&t, sizeof(replay_time_t), 1, m_file); // reserve space for start time + fwrite(&t, sizeof(replay_time_t), 1, m_file); // reserve space for end time UnsignedInt frames = 0; fwrite(&frames, sizeof(UnsignedInt), 1, m_file); // reserve space for duration in frames @@ -834,8 +843,11 @@ Bool RecorderClass::readReplayHeader(ReplayHeader& header) } // read in some stats - fread(&header.startTime, sizeof(time_t), 1, m_file); - fread(&header.endTime, sizeof(time_t), 1, m_file); + replay_time_t tmp; + fread(&tmp, sizeof(replay_time_t), 1, m_file); + header.startTime = tmp; + fread(&tmp, sizeof(replay_time_t), 1, m_file); + header.endTime = tmp; fread(&header.frameDuration, sizeof(UnsignedInt), 1, m_file); From c3409682d98f6a4beabd1b331cb9092474a7f531 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:05:06 +0200 Subject: [PATCH 3/3] Improve comment text --- Generals/Code/GameEngine/Source/Common/Recorder.cpp | 8 ++++---- GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index a5037b72be..5edb810833 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -57,10 +57,10 @@ const char *replayExtention = ".rep"; const char *lastReplayFileName = "00000000"; // a name the user is unlikely to ever type, but won't cause panic & confusion // TheSuperHackers @tweak helmutbuhler 25/04/2025 -// The replay header contains two time elements startTime and endTime of type time_t. -// On VC6 this was 32-bit, but on newer compilers it's 64-bit. In order to remain compatible -// we need to load and save them as 32-bit and use this type for that. -// Note that this will overflow on January 18, 2038. +// The replay header contains two time fields; startTime and endTime of type time_t. +// time_t is 32 bit wide on VC6, but on newer compilers it is 64 bit wide. +// In order to remain compatible we need to load and save time values with 32 bits. +// Note that this will overflow on January 18, 2038. @todo Upgrade to 64 bits when we break compatibility. typedef int32_t replay_time_t; static time_t startTime; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index d22811567d..bc8e7adc9a 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -57,10 +57,10 @@ const char *replayExtention = ".rep"; const char *lastReplayFileName = "00000000"; // a name the user is unlikely to ever type, but won't cause panic & confusion // TheSuperHackers @tweak helmutbuhler 25/04/2025 -// The replay header contains two time elements startTime and endTime of type time_t. -// On VC6 this was 32-bit, but on newer compilers it's 64-bit. In order to remain compatible -// we need to load and save them as 32-bit and use this type for that. -// Note that this will overflow on January 18, 2038. +// The replay header contains two time fields; startTime and endTime of type time_t. +// time_t is 32 bit wide on VC6, but on newer compilers it is 64 bit wide. +// In order to remain compatible we need to load and save time values with 32 bits. +// Note that this will overflow on January 18, 2038. @todo Upgrade to 64 bits when we break compatibility. typedef int32_t replay_time_t; static time_t startTime;