From 16ef884d6bff020bc1f80cb588950de0b63b5cec Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 24 Feb 2026 04:22:47 +0100 Subject: [PATCH 1/5] Fixed dummy string logic. --- .../Include/Common/NameKeyGenerator.h | 8 +-- .../GameEngine/Source/Common/GameEngine.cpp | 4 ++ .../Source/Common/NameKeyGenerator.cpp | 51 +++++-------------- 3 files changed, 20 insertions(+), 43 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h index c948ff09c1b..1f0a1b4d74a 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h @@ -106,6 +106,10 @@ class NameKeyGenerator : public SubsystemInterface // Get a string out of the INI. Store it into a NameKeyType static void parseStringAsNameKeyType( INI *ini, void *instance, void *store, const void* userData ); +#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC + void syncNameKeyID(); +#endif + private: enum @@ -114,10 +118,6 @@ class NameKeyGenerator : public SubsystemInterface SOCKET_COUNT = 45007 }; -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC - Bool addReservedKey(); -#endif - NameKeyType nameToKeyImpl(const AsciiString& name); NameKeyType nameToLowercaseKeyImpl(const AsciiString& name); NameKeyType nameToKeyImpl(const char* name); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 830b9d6f1bb..9939252fdde 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -524,6 +524,10 @@ void GameEngine::init() if (!TheAudio->isMusicAlreadyLoaded()) setQuitting(TRUE); +#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC + TheNameKeyGenerator->syncNameKeyID(); +#endif + #ifdef DUMP_PERF_STATS/////////////////////////////////////////////////////////////////////////// GetPrecisionTimer(&endTime64);////////////////////////////////////////////////////////////////// sprintf(Buf,"----------------------------------------------------------------------------After TheAudio = %f seconds",((double)(endTime64-startTime64)/(double)(freq64))); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp index 7f700e779d9..fd911242e90 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp @@ -124,67 +124,40 @@ AsciiString NameKeyGenerator::keyToName(NameKeyType key) //------------------------------------------------------------------------------------------------- #if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC -// TheSuperHackers @info xezon 04/09/2025 This key reservation is required for CRC compatibility, -// because the name keys are somehow CRC relevant. It was originally used by the file exist cache -// of the file system in Zero Hour. -Bool NameKeyGenerator::addReservedKey() +// TheSuperHackers @bugfix Caball009 24/02/2026 Originally the game would hash three files +// for the file exist cache of the file system in Zero Hour. +// TheScienceStore and TheUpgradeCenter rely on having the exact same name key IDs across all clients. +// That means that we still need to hash 3 dummy strings to keep the name key IDs synchronized with retail. +void NameKeyGenerator::syncNameKeyID() { - switch (m_nextID) - { - case 97: nameToLowercaseKeyImpl("Data\\English\\Language9x.ini"); return true; - case 98: nameToLowercaseKeyImpl("Data\\Audio\\Tracks\\English\\GLA_02.mp3"); return true; - case 99: nameToLowercaseKeyImpl("Data\\Audio\\Tracks\\GLA_02.mp3"); return true; - } - return false; + nameToKey("TSH_dummy_string_1"); + nameToKey("TSH_dummy_string_2"); + nameToKey("TSH_dummy_string_3"); } #endif //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToKey(const AsciiString& name) { - const NameKeyType key = nameToKeyImpl(name); - -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC - while (addReservedKey()); -#endif - - return key; + return nameToKeyImpl(name); } //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToLowercaseKey(const AsciiString& name) { - const NameKeyType key = nameToLowercaseKeyImpl(name); - -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC - while (addReservedKey()); -#endif - - return key; + return nameToLowercaseKeyImpl(name); } //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToKey(const char* name) { - const NameKeyType key = nameToKeyImpl(name); - -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC - while (addReservedKey()); -#endif - - return key; + return nameToKeyImpl(name); } //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToLowercaseKey(const char *name) { - const NameKeyType key = nameToLowercaseKeyImpl(name); - -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC - while (addReservedKey()); -#endif - - return key; + return nameToLowercaseKeyImpl(name); } //------------------------------------------------------------------------------------------------- From b8b15dbf3f02bc2d001666ccfe9927a95e86b988 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:51:07 +0100 Subject: [PATCH 2/5] Added assertions for name key IDs. --- .../Code/GameEngine/Include/Common/NameKeyGenerator.h | 5 ++++- .../Code/GameEngine/Source/Common/GameEngine.cpp | 8 ++++++++ .../Code/GameEngine/Source/Common/NameKeyGenerator.cpp | 10 +++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h index 1f0a1b4d74a..47a73269003 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h @@ -106,9 +106,12 @@ class NameKeyGenerator : public SubsystemInterface // Get a string out of the INI. Store it into a NameKeyType static void parseStringAsNameKeyType( INI *ini, void *instance, void *store, const void* userData ); -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC +#if RETAIL_COMPATIBLE_CRC +#if RTS_ZEROHOUR void syncNameKeyID(); #endif + void verifyNameKeyID(UnsignedInt expectedNextID) const; +#endif private: diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 9939252fdde..33b5abd1dff 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -508,6 +508,10 @@ void GameEngine::init() #endif///////////////////////////////////////////////////////////////////////////////////////////// +#if RETAIL_COMPATIBLE_CRC + if (xferCRC.getCRC() == 0xA1E7F8E6) TheNameKeyGenerator->verifyNameKeyID(1); +#endif + initSubsystem(TheScienceStore,"TheScienceStore", MSGNEW("GameEngineSubsystem") ScienceStore(), &xferCRC, "Data\\INI\\Default\\Science", "Data\\INI\\Science"); initSubsystem(TheMultiplayerSettings,"TheMultiplayerSettings", MSGNEW("GameEngineSubsystem") MultiplayerSettings(), &xferCRC, "Data\\INI\\Default\\Multiplayer", "Data\\INI\\Multiplayer"); initSubsystem(TheTerrainTypes,"TheTerrainTypes", MSGNEW("GameEngineSubsystem") TerrainTypeCollection(), &xferCRC, "Data\\INI\\Default\\Terrain", "Data\\INI\\Terrain"); @@ -582,6 +586,10 @@ void GameEngine::init() #endif///////////////////////////////////////////////////////////////////////////////////////////// +#if RETAIL_COMPATIBLE_CRC + if (xferCRC.getCRC() == 0x6209AF6E) TheNameKeyGenerator->verifyNameKeyID(2265); +#endif + initSubsystem(TheUpgradeCenter,"TheUpgradeCenter", MSGNEW("GameEngineSubsystem") UpgradeCenter, &xferCRC, "Data\\INI\\Default\\Upgrade", "Data\\INI\\Upgrade"); initSubsystem(TheGameClient,"TheGameClient", createGameClient(), nullptr); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp index fd911242e90..63aecdff3b0 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp @@ -123,7 +123,8 @@ AsciiString NameKeyGenerator::keyToName(NameKeyType key) } //------------------------------------------------------------------------------------------------- -#if RTS_ZEROHOUR && RETAIL_COMPATIBLE_CRC +#if RETAIL_COMPATIBLE_CRC +#if RTS_ZEROHOUR // TheSuperHackers @bugfix Caball009 24/02/2026 Originally the game would hash three files // for the file exist cache of the file system in Zero Hour. // TheScienceStore and TheUpgradeCenter rely on having the exact same name key IDs across all clients. @@ -135,6 +136,13 @@ void NameKeyGenerator::syncNameKeyID() nameToKey("TSH_dummy_string_3"); } #endif +void NameKeyGenerator::verifyNameKeyID(UnsignedInt expectedNextID) const +{ + // this should only be called before the initialization of TheScienceStore and TheUpgradeCenter in GameEngine::init + DEBUG_ASSERTCRASH(expectedNextID == m_nextID, + ("Retail client expects items to start with name key ID %d for unmodded files, but starts with %d", expectedNextID, m_nextID)); +} +#endif //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToKey(const AsciiString& name) From dc13156aaab8fb1c039f5956d994d6602e5fb1cc Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:02:13 +0100 Subject: [PATCH 3/5] Addressed feedback (part 1). --- GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp | 6 ++++-- .../Code/GameEngine/Source/Common/NameKeyGenerator.cpp | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 33b5abd1dff..99842d93198 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -509,7 +509,8 @@ void GameEngine::init() #if RETAIL_COMPATIBLE_CRC - if (xferCRC.getCRC() == 0xA1E7F8E6) TheNameKeyGenerator->verifyNameKeyID(1); + if (xferCRC.getCRC() == 0xA1E7F8E6) + TheNameKeyGenerator->verifyNameKeyID(1); #endif initSubsystem(TheScienceStore,"TheScienceStore", MSGNEW("GameEngineSubsystem") ScienceStore(), &xferCRC, "Data\\INI\\Default\\Science", "Data\\INI\\Science"); @@ -587,7 +588,8 @@ void GameEngine::init() #if RETAIL_COMPATIBLE_CRC - if (xferCRC.getCRC() == 0x6209AF6E) TheNameKeyGenerator->verifyNameKeyID(2265); + if (xferCRC.getCRC() == 0x6209AF6E) + TheNameKeyGenerator->verifyNameKeyID(2265); #endif initSubsystem(TheUpgradeCenter,"TheUpgradeCenter", MSGNEW("GameEngineSubsystem") UpgradeCenter, &xferCRC, "Data\\INI\\Default\\Upgrade", "Data\\INI\\Upgrade"); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp index 63aecdff3b0..31da42dc6c0 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp @@ -131,9 +131,9 @@ AsciiString NameKeyGenerator::keyToName(NameKeyType key) // That means that we still need to hash 3 dummy strings to keep the name key IDs synchronized with retail. void NameKeyGenerator::syncNameKeyID() { - nameToKey("TSH_dummy_string_1"); - nameToKey("TSH_dummy_string_2"); - nameToKey("TSH_dummy_string_3"); + nameToKey("Data\\English\\Language9x.ini"); + nameToKey("Data\\Audio\\Tracks\\English\\GLA_02.mp3"); + nameToKey("Data\\Audio\\Tracks\\GLA_02.mp3"); } #endif void NameKeyGenerator::verifyNameKeyID(UnsignedInt expectedNextID) const From 41f2423f03db274c7dfbb3ecc0348fd2edbff8e0 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:49:16 +0100 Subject: [PATCH 4/5] Restored use of 'nameToLowercase' for dummy strings. --- .../Code/GameEngine/Source/Common/NameKeyGenerator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp index 31da42dc6c0..2b12a98cb65 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp @@ -131,9 +131,9 @@ AsciiString NameKeyGenerator::keyToName(NameKeyType key) // That means that we still need to hash 3 dummy strings to keep the name key IDs synchronized with retail. void NameKeyGenerator::syncNameKeyID() { - nameToKey("Data\\English\\Language9x.ini"); - nameToKey("Data\\Audio\\Tracks\\English\\GLA_02.mp3"); - nameToKey("Data\\Audio\\Tracks\\GLA_02.mp3"); + nameToLowercaseKey("Data\\English\\Language9x.ini"); + nameToLowercaseKey("Data\\Audio\\Tracks\\English\\GLA_02.mp3"); + nameToLowercaseKey("Data\\Audio\\Tracks\\GLA_02.mp3"); } #endif void NameKeyGenerator::verifyNameKeyID(UnsignedInt expectedNextID) const From 1159e664259de27d9aa1ccd91eb0c679345bbc10 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:49:45 +0100 Subject: [PATCH 5/5] Removed implementation functions. --- .../Include/Common/NameKeyGenerator.h | 4 --- .../Source/Common/NameKeyGenerator.cpp | 30 ++----------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h index 47a73269003..28a0e950667 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/NameKeyGenerator.h @@ -121,10 +121,6 @@ class NameKeyGenerator : public SubsystemInterface SOCKET_COUNT = 45007 }; - NameKeyType nameToKeyImpl(const AsciiString& name); - NameKeyType nameToLowercaseKeyImpl(const AsciiString& name); - NameKeyType nameToKeyImpl(const char* name); - NameKeyType nameToLowercaseKeyImpl(const char *name); NameKeyType createNameKey(UnsignedInt hash, const AsciiString& name); void freeSockets(); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp index 2b12a98cb65..a439ae7fac7 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/NameKeyGenerator.cpp @@ -146,30 +146,6 @@ void NameKeyGenerator::verifyNameKeyID(UnsignedInt expectedNextID) const //------------------------------------------------------------------------------------------------- NameKeyType NameKeyGenerator::nameToKey(const AsciiString& name) -{ - return nameToKeyImpl(name); -} - -//------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToLowercaseKey(const AsciiString& name) -{ - return nameToLowercaseKeyImpl(name); -} - -//------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToKey(const char* name) -{ - return nameToKeyImpl(name); -} - -//------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToLowercaseKey(const char *name) -{ - return nameToLowercaseKeyImpl(name); -} - -//------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToKeyImpl(const AsciiString& name) { const UnsignedInt hash = calcHashForString(name.str()) % SOCKET_COUNT; @@ -186,7 +162,7 @@ NameKeyType NameKeyGenerator::nameToKeyImpl(const AsciiString& name) } //------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToLowercaseKeyImpl(const AsciiString& name) +NameKeyType NameKeyGenerator::nameToLowercaseKey(const AsciiString& name) { const UnsignedInt hash = calcHashForLowercaseString(name.str()) % SOCKET_COUNT; @@ -203,7 +179,7 @@ NameKeyType NameKeyGenerator::nameToLowercaseKeyImpl(const AsciiString& name) } //------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToKeyImpl(const char* name) +NameKeyType NameKeyGenerator::nameToKey(const char* name) { const UnsignedInt hash = calcHashForString(name) % SOCKET_COUNT; @@ -220,7 +196,7 @@ NameKeyType NameKeyGenerator::nameToKeyImpl(const char* name) } //------------------------------------------------------------------------------------------------- -NameKeyType NameKeyGenerator::nameToLowercaseKeyImpl(const char* name) +NameKeyType NameKeyGenerator::nameToLowercaseKey(const char *name) { const UnsignedInt hash = calcHashForLowercaseString(name) % SOCKET_COUNT;