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
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ class GlobalData : public SubsystemInterface
// just the "leaf name", read from INI. private because no one is ever allowed
// to look at it directly; they must go thru getPath_UserData(). (srj)
AsciiString m_userDataLeafName;
static AsciiString BuildUserDataPathFromIni();

static GlobalData *m_theOriginal; ///< the original global data instance (no overrides)
GlobalData *m_next; ///< next instance (for overrides)
Expand Down
60 changes: 49 additions & 11 deletions Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,17 +1172,8 @@ void GlobalData::parseGameDataDefinition( INI* ini )
ini->initFromINI( TheWritableGlobalData, s_GlobalDataFieldParseTable );

TheWritableGlobalData->m_userDataDir.clear();

char temp[_MAX_PATH];
if (::SHGetSpecialFolderPath(nullptr, temp, CSIDL_PERSONAL, true))
{
if (temp[strlen(temp)-1] != '\\')
strcat(temp, "\\");
strcat(temp, TheWritableGlobalData->m_userDataLeafName.str());
strcat(temp, "\\");
CreateDirectory(temp, nullptr);
TheWritableGlobalData->m_userDataDir = temp;
}
TheWritableGlobalData->m_userDataDir = BuildUserDataPathFromIni();
CreateDirectory(TheWritableGlobalData->m_userDataDir.str(), nullptr);

// override INI values with user preferences
OptionPreferences optionPref;
Expand Down Expand Up @@ -1313,3 +1304,50 @@ UnsignedInt GlobalData::generateExeCRC()

return exeCRC.get();
}

AsciiString GlobalData::BuildUserDataPathFromIni()
{
// VC6 lacks FOLDERID_Documents and KF_FLAG_DEFAULT
const GUID FOLDERID_Documents = { 0xFDD39AD0, 0x238F, 0x46AF, 0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7 };
const DWORD KF_FLAG_DEFAULT = 0;

typedef HRESULT(WINAPI* PFN_SHGetKnownFolderPath)(const GUID& rfid, DWORD dwFlags, HANDLE hToken, PWSTR* ppszPath);

AsciiString myDocumentsDirectory;
HMODULE shell32module = GetModuleHandleA("shell32.dll");

// TheSuperHackers @bugfix Mauller 20/03/2026 Fix the handling of folder redirection
// OneDrive and Group Policy folder redirection is better supported by SHGetKnownFolderPath()
// SHGetKnownFolderPath() is only supported in windows Vista onwards so we check for it being available
if (shell32module && GetProcAddress(shell32module, "SHGetKnownFolderPath")) {
PFN_SHGetKnownFolderPath pSHGetKnownFolderPath = (PFN_SHGetKnownFolderPath)GetProcAddress(shell32module, "SHGetKnownFolderPath");

PWSTR pszPath = nullptr;
HRESULT hr = pSHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, nullptr, &pszPath);

if (SUCCEEDED(hr) && pszPath) {
myDocumentsDirectory.translate(UnicodeString(pszPath));
CoTaskMemFree(pszPath);
}
}
else {
char temp[_MAX_PATH + 1];
if (SHGetSpecialFolderPath(nullptr, temp, CSIDL_PERSONAL, true)) {
myDocumentsDirectory = temp;
}
}

if (myDocumentsDirectory.isEmpty())
return AsciiString::TheEmptyString;

// Now build the full path string
if (!myDocumentsDirectory.endsWith("\\"))
myDocumentsDirectory.concat('\\');

myDocumentsDirectory.concat(TheWritableGlobalData->m_userDataLeafName.str());

if (!myDocumentsDirectory.endsWith("\\"))
myDocumentsDirectory.concat('\\');

return myDocumentsDirectory;
}
1 change: 1 addition & 0 deletions GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ class GlobalData : public SubsystemInterface
// this is private, since we read the info from Windows and cache it for
// future use. No one is allowed to change it, ever. (srj)
AsciiString m_userDataDir;
AsciiString BuildUserDataPathFromRegistry();

static GlobalData *m_theOriginal; ///< the original global data instance (no overrides)
GlobalData *m_next; ///< next instance (for overrides)
Expand Down
84 changes: 58 additions & 26 deletions GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,32 +1036,10 @@ GlobalData::GlobalData()

m_keyboardCameraRotateSpeed = 0.1f;

// Set user data directory based on registry settings instead of INI parameters. This allows us to
// localize the leaf name.
char temp[_MAX_PATH + 1];
if (::SHGetSpecialFolderPath(nullptr, temp, CSIDL_PERSONAL, true))
{
AsciiString myDocumentsDirectory = temp;

if (myDocumentsDirectory.getCharAt(myDocumentsDirectory.getLength() -1) != '\\')
myDocumentsDirectory.concat( '\\' );

AsciiString leafName;

if ( !GetStringFromRegistry( "", "UserDataLeafName", leafName ) )
{
// Use something, anything
// [MH] had to remove this, otherwise mapcache build step won't run... DEBUG_CRASH( ( "Could not find registry key UserDataLeafName; defaulting to \"Command and Conquer Generals Zero Hour Data\" " ) );
leafName = "Command and Conquer Generals Zero Hour Data";
}

myDocumentsDirectory.concat( leafName );
if (myDocumentsDirectory.getCharAt( myDocumentsDirectory.getLength() - 1) != '\\')
myDocumentsDirectory.concat( '\\' );

CreateDirectory(myDocumentsDirectory.str(), nullptr);
m_userDataDir = myDocumentsDirectory;
}
// Set user data directory based on registry settings instead of INI parameters.
// This allows us to localize the leaf name.
m_userDataDir = BuildUserDataPathFromRegistry();
CreateDirectory(m_userDataDir.str(), nullptr);

//-allAdvice feature
//m_allAdvice = FALSE;
Expand Down Expand Up @@ -1333,3 +1311,57 @@ UnsignedInt GlobalData::generateExeCRC()

return exeCRC.get();
}

AsciiString GlobalData::BuildUserDataPathFromRegistry()
{
// VC6 lacks FOLDERID_Documents and KF_FLAG_DEFAULT
const GUID FOLDERID_Documents = { 0xFDD39AD0, 0x238F, 0x46AF, 0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7 };
const DWORD KF_FLAG_DEFAULT = 0;

typedef HRESULT(WINAPI* PFN_SHGetKnownFolderPath)(const GUID& rfid, DWORD dwFlags, HANDLE hToken, PWSTR* ppszPath);

AsciiString myDocumentsDirectory;
HMODULE shell32module = GetModuleHandleA("shell32.dll");

// TheSuperHackers @bugfix Mauller 20/03/2026 Fix the handling of folder redirection
// OneDrive and Group Policy folder redirection is better supported by SHGetKnownFolderPath()
// SHGetKnownFolderPath() is only supported in windows Vista onwards so we check for it being available
if (shell32module && GetProcAddress(shell32module, "SHGetKnownFolderPath")) {
PFN_SHGetKnownFolderPath pSHGetKnownFolderPath = (PFN_SHGetKnownFolderPath)GetProcAddress(shell32module, "SHGetKnownFolderPath");

PWSTR pszPath = nullptr;
HRESULT hr = pSHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, nullptr, &pszPath);

if (SUCCEEDED(hr) && pszPath) {
myDocumentsDirectory.translate(UnicodeString(pszPath));
CoTaskMemFree(pszPath);
}
}
else {
char temp[_MAX_PATH + 1];
if (SHGetSpecialFolderPath(nullptr, temp, CSIDL_PERSONAL, true)) {
myDocumentsDirectory = temp;
}
}

if (myDocumentsDirectory.isEmpty())
return AsciiString::TheEmptyString;

// Now build the full path string
if (!myDocumentsDirectory.endsWith("\\"))
myDocumentsDirectory.concat('\\');

AsciiString leafName;
if (!GetStringFromRegistry("", "UserDataLeafName", leafName))
{
// Use something, anything
// [MH] had to remove this, otherwise mapcache build step won't run... DEBUG_CRASH( ( "Could not find registry key UserDataLeafName; defaulting to \"Command and Conquer Generals Zero Hour Data\" " ) );
leafName = "Command and Conquer Generals Zero Hour Data";
}

myDocumentsDirectory.concat(leafName);
if (!myDocumentsDirectory.endsWith("\\"))
myDocumentsDirectory.concat('\\');

return myDocumentsDirectory;
}
Loading