Skip to content
10 changes: 7 additions & 3 deletions Generals/Code/GameEngine/Include/Common/Recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ class RecorderClass : public SubsystemInterface {
Bool isPlaybackMode() const { return m_mode == RECORDERMODETYPE_PLAYBACK || m_mode == RECORDERMODETYPE_SIMULATION_PLAYBACK; }
void initControls(); ///< Show or Hide the Replay controls

AsciiString getReplayDir(); ///< Returns the directory that holds the replay files.
static AsciiString getReplayExtention(); ///< Returns the file extention for replay files.
AsciiString getLastReplayFileName(); ///< Returns the filename used for the default replay.
static AsciiString getReplayDir(); ///< Returns the directory that holds the replay files.
static AsciiString getReplayArchiveDir(); ///< Returns the directory that holds the archived replay files.
static AsciiString getReplayExtention(); ///< Returns the file extention for replay files.
static AsciiString getLastReplayFileName(); ///< Returns the filename used for the default replay.

GameInfo *getGameInfo( void ) { return &m_gameInfo; } ///< Returns the slot list for playback game start

Expand All @@ -133,10 +134,12 @@ class RecorderClass : public SubsystemInterface {
Bool sawCRCMismatch() const;
void cleanUpReplayFile( void ); ///< after a crash, send replay/debug info to a central repository

void setArchiveEnabled(Bool enable) { m_archiveReplays = enable; } ///< Enable or disable replay archiving.
void stopRecording(); ///< Stop recording and close m_file.
protected:
void startRecording(GameDifficulty diff, Int originalGameMode, Int rankPoints, Int maxFPS); ///< Start recording to m_file.
void writeToFile(GameMessage *msg); ///< Write this GameMessage to m_file.
void archiveReplay(AsciiString fileName); ///< Move the specified replay file to the archive directory.

void logGameStart(AsciiString options);
void logGameEnd( void );
Expand Down Expand Up @@ -167,6 +170,7 @@ class RecorderClass : public SubsystemInterface {
Bool m_wasDesync;

Bool m_doingAnalysis;
Bool m_archiveReplays; ///< if true, each replay is archived to the replay archive folder after recording

Int m_originalGameMode; // valid in replays

Expand Down
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/Common/UserPreferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class OptionPreferences : public UserPreferences
void setOnlineIPAddress(AsciiString IP); // convenience function
void setLANIPAddress(UnsignedInt IP); // convenience function
void setOnlineIPAddress(UnsignedInt IP); // convenience function
Bool getArchiveReplaysEnabled() const; // convenience function
Bool getAlternateMouseModeEnabled(void); // convenience function
Real getScrollFactor(void); // convenience function
Bool getDrawScrollAnchor(void);
Expand Down
49 changes: 47 additions & 2 deletions Generals/Code/GameEngine/Source/Common/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "GameLogic/GameLogic.h"
#include "Common/RandomValue.h"
#include "Common/CRCDebug.h"
#include "Common/UserPreferences.h"
#include "Common/version.h"

constexpr const char s_genrep[] = "GENREP";
Expand Down Expand Up @@ -370,6 +371,7 @@ RecorderClass::RecorderClass()
//Added By Sadullah Nader
//Initializtion(s) inserted
m_doingAnalysis = FALSE;
m_archiveReplays = FALSE;
m_nextFrame = 0;
m_wasDesync = FALSE;
//
Expand Down Expand Up @@ -406,6 +408,9 @@ void RecorderClass::init() {
m_wasDesync = FALSE;
m_doingAnalysis = FALSE;
m_playbackFrameCount = 0;

OptionPreferences optionPref;
m_archiveReplays = optionPref.getArchiveReplaysEnabled();
}

/**
Expand Down Expand Up @@ -726,10 +731,42 @@ void RecorderClass::stopRecording() {
if (m_file != NULL) {
m_file->close();
m_file = NULL;

if (m_archiveReplays)
archiveReplay(m_fileName);
}
m_fileName.clear();
}

/**
* TheSuperHackers @feature Stubbjax 17/10/2025 Copy the replay file to the archive directory and rename it using the current timestamp.
*/
void RecorderClass::archiveReplay(AsciiString fileName)
{
SYSTEMTIME st;
GetLocalTime(&st);

AsciiString archiveFileName;
// Use a standard YYYYMMDD_HHMMSS format for simplicity and to avoid conflicts.
archiveFileName.format("%04d%02d%02d_%02d%02d%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

AsciiString extension = getReplayExtention();
AsciiString sourcePath = getReplayDir();
sourcePath.concat(fileName);

if (!sourcePath.endsWith(extension))
sourcePath.concat(extension);

AsciiString destPath = getReplayArchiveDir();
TheFileSystem->createDirectory(destPath.str());

destPath.concat(archiveFileName);
destPath.concat(extension);

if (!CopyFile(sourcePath.str(), destPath.str(), FALSE))
DEBUG_LOG(("RecorderClass::archiveReplay: Failed to copy %s to %s", sourcePath.str(), destPath.str()));
}

/**
* Write this game message to the record file. This also writes the game message's execution frame.
*/
Expand Down Expand Up @@ -1595,10 +1632,18 @@ RecorderClass::CullBadCommandsResult RecorderClass::cullBadCommands() {
*/
AsciiString RecorderClass::getReplayDir()
{
const char* replayDir = "Replays\\";
AsciiString tmp = TheGlobalData->getPath_UserData();
tmp.concat("Replays\\");
return tmp;
}

/**
* returns the directory that holds the archived replay files.
*/
AsciiString RecorderClass::getReplayArchiveDir()
{
AsciiString tmp = TheGlobalData->getPath_UserData();
tmp.concat(replayDir);
tmp.concat("ArchivedReplays\\");
return tmp;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "Common/GameEngine.h"
#include "Common/UserPreferences.h"
#include "Common/GameLOD.h"
#include "Common/Recorder.h"
#include "Common/Registry.h"
#include "Common/version.h"

Expand Down Expand Up @@ -310,6 +311,18 @@ void OptionPreferences::setOnlineIPAddress( UnsignedInt IP )
(*this)["GameSpyIPAddress"] = tmp;
}

Bool OptionPreferences::getArchiveReplaysEnabled() const
{
OptionPreferences::const_iterator it = find("ArchiveReplays");
if (it == end())
return FALSE;

if (stricmp(it->second.str(), "yes") == 0) {
return TRUE;
}
return FALSE;
}

Bool OptionPreferences::getAlternateMouseModeEnabled(void)
{
OptionPreferences::const_iterator it = find("UseAlternateMouse");
Expand Down Expand Up @@ -1296,6 +1309,13 @@ static void saveOptions( void )
TheWritableGlobalData->m_enablePlayerObserver = enabled;
}

// TheSuperHackers @todo Add checkbox ?
{
Bool enabled = pref->getArchiveReplaysEnabled();
(*pref)["ArchiveReplays"] = enabled ? "yes" : "no";
TheRecorder->setArchiveEnabled(enabled);
}

//-------------------------------------------------------------------------------------------------
// scroll speed val
val = GadgetSliderGetPosition(sliderScrollSpeed);
Expand Down
10 changes: 7 additions & 3 deletions GeneralsMD/Code/GameEngine/Include/Common/Recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ class RecorderClass : public SubsystemInterface {
Bool isPlaybackMode() const { return m_mode == RECORDERMODETYPE_PLAYBACK || m_mode == RECORDERMODETYPE_SIMULATION_PLAYBACK; }
void initControls(); ///< Show or Hide the Replay controls

AsciiString getReplayDir(); ///< Returns the directory that holds the replay files.
static AsciiString getReplayExtention(); ///< Returns the file extention for replay files.
AsciiString getLastReplayFileName(); ///< Returns the filename used for the default replay.
static AsciiString getReplayDir(); ///< Returns the directory that holds the replay files.
static AsciiString getReplayArchiveDir(); ///< Returns the directory that holds the archived replay files.
static AsciiString getReplayExtention(); ///< Returns the file extention for replay files.
static AsciiString getLastReplayFileName(); ///< Returns the filename used for the default replay.

GameInfo *getGameInfo( void ) { return &m_gameInfo; } ///< Returns the slot list for playback game start

Expand All @@ -133,10 +134,12 @@ class RecorderClass : public SubsystemInterface {
Bool sawCRCMismatch() const;
void cleanUpReplayFile( void ); ///< after a crash, send replay/debug info to a central repository

void setArchiveEnabled(Bool enable) { m_archiveReplays = enable; } ///< Enable or disable replay archiving.
void stopRecording(); ///< Stop recording and close m_file.
protected:
void startRecording(GameDifficulty diff, Int originalGameMode, Int rankPoints, Int maxFPS); ///< Start recording to m_file.
void writeToFile(GameMessage *msg); ///< Write this GameMessage to m_file.
void archiveReplay(AsciiString fileName); ///< Move the specified replay file to the archive directory.

void logGameStart(AsciiString options);
void logGameEnd( void );
Expand Down Expand Up @@ -167,6 +170,7 @@ class RecorderClass : public SubsystemInterface {
Bool m_wasDesync;

Bool m_doingAnalysis;
Bool m_archiveReplays; ///< if true, each replay is archived to the replay archive folder after recording

Int m_originalGameMode; // valid in replays

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class OptionPreferences : public UserPreferences
void setOnlineIPAddress(AsciiString IP); // convenience function
void setLANIPAddress(UnsignedInt IP); // convenience function
void setOnlineIPAddress(UnsignedInt IP); // convenience function
Bool getArchiveReplaysEnabled() const; // convenience function
Bool getAlternateMouseModeEnabled(void); // convenience function
Bool getRetaliationModeEnabled(); // convenience function
Bool getDoubleClickAttackMoveEnabled(void); // convenience function
Expand Down
49 changes: 47 additions & 2 deletions GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "GameLogic/GameLogic.h"
#include "Common/RandomValue.h"
#include "Common/CRCDebug.h"
#include "Common/UserPreferences.h"
#include "Common/version.h"

constexpr const char s_genrep[] = "GENREP";
Expand Down Expand Up @@ -370,6 +371,7 @@ RecorderClass::RecorderClass()
//Added By Sadullah Nader
//Initializtion(s) inserted
m_doingAnalysis = FALSE;
m_archiveReplays = FALSE;
m_nextFrame = 0;
m_wasDesync = FALSE;
//
Expand Down Expand Up @@ -406,6 +408,9 @@ void RecorderClass::init() {
m_wasDesync = FALSE;
m_doingAnalysis = FALSE;
m_playbackFrameCount = 0;

OptionPreferences optionPref;
m_archiveReplays = optionPref.getArchiveReplaysEnabled();
}

/**
Expand Down Expand Up @@ -728,10 +733,42 @@ void RecorderClass::stopRecording() {
if (m_file != NULL) {
m_file->close();
m_file = NULL;

if (m_archiveReplays)
archiveReplay(m_fileName);
}
m_fileName.clear();
}

/**
* TheSuperHackers @feature Stubbjax 17/10/2025 Copy the replay file to the archive directory and rename it using the current timestamp.
*/
void RecorderClass::archiveReplay(AsciiString fileName)
{
SYSTEMTIME st;
GetLocalTime(&st);

AsciiString archiveFileName;
// Use a standard YYYYMMDD_HHMMSS format for simplicity and to avoid conflicts.
archiveFileName.format("%04d%02d%02d_%02d%02d%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);

AsciiString extension = getReplayExtention();
AsciiString sourcePath = getReplayDir();
sourcePath.concat(fileName);

if (!sourcePath.endsWith(extension))
sourcePath.concat(extension);

AsciiString destPath = getReplayArchiveDir();
TheFileSystem->createDirectory(destPath.str());

destPath.concat(archiveFileName);
destPath.concat(extension);

if (!CopyFile(sourcePath.str(), destPath.str(), FALSE))
DEBUG_LOG(("RecorderClass::archiveReplay: Failed to copy %s to %s", sourcePath.str(), destPath.str()));
}

/**
* Write this game message to the record file. This also writes the game message's execution frame.
*/
Expand Down Expand Up @@ -1598,10 +1635,18 @@ RecorderClass::CullBadCommandsResult RecorderClass::cullBadCommands() {
*/
AsciiString RecorderClass::getReplayDir()
{
const char* replayDir = "Replays\\";
AsciiString tmp = TheGlobalData->getPath_UserData();
tmp.concat("Replays\\");
return tmp;
}

/**
* returns the directory that holds the archived replay files.
*/
AsciiString RecorderClass::getReplayArchiveDir()
{
AsciiString tmp = TheGlobalData->getPath_UserData();
tmp.concat(replayDir);
tmp.concat("ArchivedReplays\\");
return tmp;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "Common/GameEngine.h"
#include "Common/UserPreferences.h"
#include "Common/GameLOD.h"
#include "Common/Recorder.h"
#include "Common/Registry.h"
#include "Common/version.h"

Expand Down Expand Up @@ -319,6 +320,18 @@ void OptionPreferences::setOnlineIPAddress( UnsignedInt IP )
(*this)["GameSpyIPAddress"] = tmp;
}

Bool OptionPreferences::getArchiveReplaysEnabled() const
{
OptionPreferences::const_iterator it = find("ArchiveReplays");
if (it == end())
return FALSE;

if (stricmp(it->second.str(), "yes") == 0) {
return TRUE;
}
return FALSE;
}

Bool OptionPreferences::getAlternateMouseModeEnabled(void)
{
OptionPreferences::const_iterator it = find("UseAlternateMouse");
Expand Down Expand Up @@ -1356,6 +1369,13 @@ static void saveOptions( void )
TheWritableGlobalData->m_enablePlayerObserver = enabled;
}

// TheSuperHackers @todo Add checkbox ?
{
Bool enabled = pref->getArchiveReplaysEnabled();
(*pref)["ArchiveReplays"] = enabled ? "yes" : "no";
TheRecorder->setArchiveEnabled(enabled);
}

//-------------------------------------------------------------------------------------------------
// scroll speed val
val = GadgetSliderGetPosition(sliderScrollSpeed);
Expand Down
Loading