Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Generals/Code/GameEngine/Include/GameLogic/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ class Object : public Thing, public Snapshot

Object* getNextObject() { return m_next; }
const Object* getNextObject() const { return m_next; }
Object* getPrevObject() { return m_prev; }
const Object* getPrevObject() const { return m_prev; }
void friend_setNextObject(Object* obj) { m_next = obj; }
void friend_setPrevObject(Object* obj) { m_prev = obj; }

void updateObjValuesFromMapProperties(Dict* properties); ///< Brings in properties set in the editor.

Expand Down
28 changes: 27 additions & 1 deletion Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4241,13 +4241,14 @@ void GameLogic::prepareLogicForObjectLoad( void )
* this version breaks compatibility with previous versions. (CBD)
* 5: Added xfering the BuildAssistant's sell list.
* 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp
* 10: TheSuperHackers @tweak Save objects in reverse order so they load in correct order. Reverse object list for old saves.
*/
// ------------------------------------------------------------------------------------------------
void GameLogic::xfer( Xfer *xfer )
{

// version
const XferVersion currentVersion = 9;
const XferVersion currentVersion = 10;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume RETAIL_COMPATIBLE_XFER_SAVE is not strictly necessary? Retail game can still load these save games correctly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if an old game tries to load a new save it will just fail gracefully. I don't think this will be an issue - if someone asks a friend with TSH to create a .sav and share it with them maybe? But why would that ever happen? If we need to be able to load new saves on old game, we could use RETAIL_COMPATIBLE_XFER_SAVE

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think the way it would work is that version 10 isn't known by retail and isn't checked so it will load the list the same way it always did which will be the new correct order for saves saved after this version, the old incorrect order for save from before this. This new version checks on load so the order loaded will be correct for builds after this PR is merged no matter what version wrote the save.

XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );

Expand Down Expand Up @@ -4281,7 +4282,13 @@ void GameLogic::xfer( Xfer *xfer )
if( xfer->getXferMode() == XFER_SAVE )
{

// TheSuperHackers @bugfix bobtista 27/01/2026 Save objects in reverse order (newest first)
// so they load in the correct order (oldest objects at head of list).
Object *lastObj = nullptr;
for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
lastObj = obj;

for( obj = lastObj; obj; obj = obj->getPrevObject() )
{

// get the object TOC entry for this template
Expand Down Expand Up @@ -4364,6 +4371,25 @@ void GameLogic::xfer( Xfer *xfer )

}

// TheSuperHackers @fix bobtista 27/01/2026 Reverse object list for old saves.
// Old saves stored objects oldest-first, which results in reversed order when loaded
// since objects are prepended during creation. Version 10+ saves in reverse order.
if ( version <= 9 )
{
Object *prev = nullptr;
Object *current = m_objList;
Object *next = nullptr;
while ( current != nullptr )
{
next = current->getNextObject();
current->friend_setNextObject( prev );
current->friend_setPrevObject( next );
prev = current;
current = next;
}
m_objList = prev;
}

}

// campaign info
Expand Down
4 changes: 4 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ class Object : public Thing, public Snapshot

Object* getNextObject() { return m_next; }
const Object* getNextObject() const { return m_next; }
Object* getPrevObject() { return m_prev; }
const Object* getPrevObject() const { return m_prev; }
void friend_setNextObject(Object* obj) { m_next = obj; }
void friend_setPrevObject(Object* obj) { m_prev = obj; }

void updateObjValuesFromMapProperties(Dict* properties); ///< Brings in properties set in the editor.

Expand Down
29 changes: 27 additions & 2 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4801,13 +4801,14 @@ void GameLogic::prepareLogicForObjectLoad( void )
* 5: Added xfering the BuildAssistant's sell list.
* 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp
* 10: xfer m_superweaponRestriction
* 11: TheSuperHackers @tweak Save objects in reverse order so they load in correct order
*/
// ------------------------------------------------------------------------------------------------
void GameLogic::xfer( Xfer *xfer )
{

// version
const XferVersion currentVersion = 10;
const XferVersion currentVersion = 11;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );

Expand Down Expand Up @@ -4840,8 +4841,13 @@ void GameLogic::xfer( Xfer *xfer )
ObjectTOCEntry *tocEntry;
if( xfer->getXferMode() == XFER_SAVE )
{

// TheSuperHackers @bugfix bobtista 27/01/2026 Save objects in reverse order (newest first)
// so they load in the correct order (oldest objects at head of list).
Object *lastObj = nullptr;
for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
lastObj = obj;

for( obj = lastObj; obj; obj = obj->getPrevObject() )
{

// get the object TOC entry for this template
Expand Down Expand Up @@ -4924,6 +4930,25 @@ void GameLogic::xfer( Xfer *xfer )

}

// TheSuperHackers @fix bobtista 27/01/2026 Reverse object list for old saves.
// Old saves stored objects oldest-first, which results in reversed order when loaded
// since objects are prepended during creation. Version 11+ saves in reverse order.
if ( version <= 10 )
{
Object *prev = nullptr;
Object *current = m_objList;
Object *next = nullptr;
while ( current != nullptr )
{
next = current->getNextObject();
current->friend_setNextObject( prev );
current->friend_setPrevObject( next );
prev = current;
current = next;
}
m_objList = prev;
}

}

// campaign info
Expand Down
Loading