Added registry spoofing option & quirk. Thanks to @artur-graniszewski

Added Black Myth quirk
This commit is contained in:
cdozdil
2026-02-16 00:59:33 +03:00
parent 7fb068b223
commit 2ad8557595
5 changed files with 272 additions and 1 deletions
+2
View File
@@ -583,6 +583,7 @@ bool Config::Reload(std::filesystem::path iniPath)
TargetVendorId.set_from_config(readUInt("Spoofing", "TargetVendorId"));
TargetDeviceId.set_from_config(readUInt("Spoofing", "TargetDeviceId"));
UESpoofIntelAtomics64.set_from_config(readBool("Spoofing", "UEIntelAtomics"));
SpoofRegistry.set_from_config(readBool("Spoofing", "Registry"));
}
// Inputs
@@ -1239,6 +1240,7 @@ bool Config::SaveIni()
GetIntValue(Instance()->TargetVendorId.value_for_config(), true).c_str());
ini.SetValue("Spoofing", "TargetDeviceId",
GetIntValue(Instance()->TargetDeviceId.value_for_config(), true).c_str());
ini.SetValue("Spoofing", "Registry", GetBoolValue(Instance()->SpoofRegistry.value_for_config()).c_str());
}
// Plugins
+1
View File
@@ -405,6 +405,7 @@ class Config
CustomOptional<uint32_t, NoDefault> TargetDeviceId;
CustomOptional<std::wstring> SpoofedGPUName { L"NVIDIA GeForce RTX 4090" };
CustomOptional<bool> UESpoofIntelAtomics64 { false };
CustomOptional<bool> SpoofRegistry { false };
// Plugins
CustomOptional<std::wstring> PluginPath { L"plugins" };
+11
View File
@@ -1306,6 +1306,12 @@ static void printQuirks(flag_set<GameQuirk>& quirks)
state->detectedQuirks.push_back("Disable Resize Skip");
}
if (quirks & GameQuirk::SpoofRegistry)
{
spdlog::info("Quirk: Spoof Registry");
state->detectedQuirks.push_back("Spoof Registry");
}
return;
}
@@ -1459,6 +1465,11 @@ static void CheckQuirks()
Config::Instance()->FGXeFGSkipResizeBuffers.set_volatile_value(false);
}
if (quirks & GameQuirk::SpoofRegistry && !Config::Instance()->SpoofRegistry.has_value())
{
Config::Instance()->SpoofRegistry.set_volatile_value(true);
}
// For Luma, we assume if Luma addon in game folder it's used
const auto dir = Util::ExePath().parent_path();
bool lumaDetected = false;
+253 -1
View File
@@ -8,10 +8,14 @@ const HKEY signatureMark = (HKEY) 0xFFFFFFFF13372137;
typedef decltype(&RegOpenKeyExW) PFN_RegOpenKeyExW;
typedef decltype(&RegEnumValueW) PFN_RegEnumValueW;
typedef decltype(&RegCloseKey) PFN_RegCloseKey;
typedef decltype(&RegQueryValueExW) PFN_RegQueryValueExW;
typedef decltype(&RegQueryValueExA) PFN_RegQueryValueExA;
static PFN_RegOpenKeyExW o_RegOpenKeyExW = nullptr;
static PFN_RegEnumValueW o_RegEnumValueW = nullptr;
static PFN_RegCloseKey o_RegCloseKey = nullptr;
static PFN_RegQueryValueExW o_RegQueryValueExW = nullptr;
static PFN_RegQueryValueExA o_RegQueryValueExA = nullptr;
static LSTATUS hkRegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
@@ -81,7 +85,241 @@ static LSTATUS hkRegCloseKey(HKEY hKey)
return o_RegCloseKey(hKey);
}
// Replaces all values returned for SOFTWARE\\NVIDIA Corporation\\Global so could potentially cause issues on Nvidia
// Original implementation:
// https://github.com/artur-graniszewski/dlss-enabler-main/blob/1f8b24722f1b526ffb896ae62b6aa3ca766b0728/Utils/RegistryProxy.cpp#L137
static LONG hkRegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData,
LPDWORD lpcbData)
{
std::wstring valueName = L"";
if (lpValueName != NULL)
{
valueName = std::wstring(lpValueName);
}
if (valueName == L"HwSchMode")
{
// Check if lpcbData is not NULL
if (lpcbData != nullptr)
{
// If lpData is NULL, we're being asked for the required size
if (lpData == nullptr)
{
*lpcbData = sizeof(DWORD); // Indicate the required size
// Set the type to REG_DWORD
if (lpType)
{
*lpType = REG_DWORD;
}
return ERROR_SUCCESS;
}
// Check if the buffer is large enough
if (*lpcbData >= sizeof(DWORD))
{
*(DWORD*) lpData = 2;
// Set the type to REG_DWORD
if (lpType)
{
*lpType = REG_DWORD;
}
// Set the size of the data returned
*lpcbData = sizeof(DWORD);
// Return success
return ERROR_SUCCESS;
}
else
{
// Buffer is too small, return required size
*lpcbData = sizeof(DWORD);
return ERROR_MORE_DATA;
}
}
// If lpcbData is NULL, return an error
return ERROR_INVALID_PARAMETER;
}
// Call the original function
auto result = o_RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
// Check the result of the query and if the valueName matches
if (result == ERROR_SUCCESS && !State::Instance().isRunningOnNvidia)
{
if (valueName == L"DriverVersion")
{
const std::wstring spoofedValue = L"31.0.15.5244";
size_t spoofedValueSize =
(spoofedValue.size() + 1) * sizeof(wchar_t); // Size in bytes including null terminator
if (lpData != nullptr && lpcbData != nullptr)
{
// Check if buffer size is sufficient
if (*lpcbData >= spoofedValueSize)
{
// Copy the spoofed value into lpData
std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize);
// Update lpcbData with the size of the spoofed value
*lpcbData = static_cast<DWORD>(spoofedValueSize);
}
else
{
// If buffer is too small, set lpcbData to the required size
*lpcbData = static_cast<DWORD>(spoofedValueSize);
result = ERROR_MORE_DATA; // Indicate that buffer was too small
}
}
}
if (valueName == L"HardwareID")
{
// Handle REG_SZ type
std::wstring data(reinterpret_cast<wchar_t*>(lpData), *lpcbData / sizeof(wchar_t));
std::wstring newData = data;
size_t pos = 0;
// Replace VEN_1002 and VEN_8086 with VEN_10DE in REG_SZ
while ((pos = newData.find(L"VEN_1002", pos)) != std::wstring::npos)
{
newData.replace(pos, 8, L"VEN_10DE");
pos += 8; // Move past the replacement
}
pos = 0;
while ((pos = newData.find(L"VEN_8086", pos)) != std::wstring::npos)
{
newData.replace(pos, 8, L"VEN_10DE");
pos += 8; // Move past the replacement
}
LOG_ERROR(L"NEW PAYLOAD: " + newData);
// Copy the new data back
wcscpy_s(reinterpret_cast<wchar_t*>(lpData), *lpcbData / sizeof(wchar_t), newData.c_str());
}
}
return result;
}
LONG WINAPI hkRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData,
LPDWORD lpcbData)
{
std::string valueName = "";
if (lpValueName != NULL)
{
valueName = std::string(lpValueName);
}
if (valueName == "HwSchMode")
{
// Check if lpcbData is not NULL
if (lpcbData != nullptr)
{
// If lpData is NULL, we're being asked for the required size
if (lpData == nullptr)
{
*lpcbData = sizeof(DWORD); // Indicate the required size
// Set the type to REG_DWORD
if (lpType)
{
*lpType = REG_DWORD;
}
return ERROR_SUCCESS;
}
// Check if the buffer is large enough
if (*lpcbData >= sizeof(DWORD))
{
*(DWORD*) lpData = 2;
// Set the type to REG_DWORD
if (lpType)
{
*lpType = REG_DWORD;
}
// Set the size of the data returned
*lpcbData = sizeof(DWORD);
// Return success
return ERROR_SUCCESS;
}
else
{
// Buffer is too small, return required size
*lpcbData = sizeof(DWORD);
return ERROR_MORE_DATA;
}
}
// If lpcbData is NULL, return an error
return ERROR_INVALID_PARAMETER;
}
// Call the original function
auto result = o_RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);
// Check the result of the query and if the valueName matches
if (result == ERROR_SUCCESS && !State::Instance().isRunningOnNvidia)
{
if (valueName == "DriverVersion")
{
const std::wstring spoofedValue = L"31.0.15.5244";
size_t spoofedValueSize =
(spoofedValue.size() + 1) * sizeof(wchar_t); // Size in bytes including null terminator
if (lpData != nullptr && lpcbData != nullptr)
{
// Check if buffer size is sufficient
if (*lpcbData >= spoofedValueSize)
{
// Copy the spoofed value into lpData
std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize);
// Update lpcbData with the size of the spoofed value
*lpcbData = static_cast<DWORD>(spoofedValueSize);
}
else
{
// If buffer is too small, set lpcbData to the required size
*lpcbData = static_cast<DWORD>(spoofedValueSize);
result = ERROR_MORE_DATA; // Indicate that buffer was too small
}
}
}
if (valueName == "HardwareID")
{
// Handle REG_SZ type
std::string data(reinterpret_cast<char*>(lpData), *lpcbData / sizeof(char));
std::string newData = data;
size_t pos = 0;
// Replace VEN_1002 and VEN_8086 with VEN_10DE in REG_SZ
while ((pos = newData.find("VEN_1002", pos)) != std::wstring::npos)
{
newData.replace(pos, 8, "VEN_10DE");
pos += 8; // Move past the replacement
}
pos = 0;
while ((pos = newData.find("VEN_8086", pos)) != std::wstring::npos)
{
newData.replace(pos, 8, "VEN_10DE");
pos += 8; // Move past the replacement
}
// Copy the new data back
std::memcpy(lpData, newData.c_str(), *lpcbData);
}
}
return result;
}
static void hookAdvapi32()
{
LOG_FUNC();
@@ -90,6 +328,14 @@ static void hookAdvapi32()
o_RegEnumValueW = reinterpret_cast<PFN_RegEnumValueW>(DetourFindFunction("Advapi32.dll", "RegEnumValueW"));
o_RegCloseKey = reinterpret_cast<PFN_RegCloseKey>(DetourFindFunction("Advapi32.dll", "RegCloseKey"));
if (Config::Instance()->SpoofRegistry.value_or_default())
{
o_RegQueryValueExW =
reinterpret_cast<PFN_RegQueryValueExW>(DetourFindFunction("Advapi32.dll", "RegQueryValueExW"));
o_RegQueryValueExA =
reinterpret_cast<PFN_RegQueryValueExA>(DetourFindFunction("Advapi32.dll", "RegQueryValueExA"));
}
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
@@ -102,6 +348,12 @@ static void hookAdvapi32()
if (o_RegCloseKey)
DetourAttach(&(PVOID&) o_RegCloseKey, hkRegCloseKey);
if (o_RegQueryValueExW)
DetourAttach(&(PVOID&) o_RegQueryValueExW, hkRegQueryValueExW);
if (o_RegQueryValueExA)
DetourAttach(&(PVOID&) o_RegQueryValueExA, hkRegQueryValueExA);
DetourTransactionCommit();
}
+5
View File
@@ -37,6 +37,7 @@ enum class GameQuirk : uint64_t
SetVelocityValidNow,
SetHudlessValidNow,
DisableResizeSkip,
SpoofRegistry,
// Quirks that are applied deeper in code
CyberpunkHudlessFixes,
@@ -197,6 +198,10 @@ static const QuirkEntry quirkTable[] = {
QUIRK_ENTRY("prey.exe", GameQuirk::DontUseNTShared, GameQuirk::DisableOptiXessPipelineCreation,
GameQuirk::DisableDxgiSpoofing),
// Black Myth: Wukong
// To enable DLSS-FG option
QUIRK_ENTRY_UE(b1, GameQuirk::SpoofRegistry),
// Avowed
// NoBarriers needed to avoid post-loading crash with DLSS
QUIRK_ENTRY_UE(avowed, GameQuirk::ForceAutoExposure, GameQuirk::DontUseUnrealBarriers, GameQuirk::DisableFSR2Inputs,