From c51ca7a9d605da8d4d8e3b5eac616e2c1b23dc17 Mon Sep 17 00:00:00 2001 From: pv Date: Fri, 4 Aug 2023 20:11:23 +0300 Subject: [PATCH] Register stat types --- src/App/Application.cpp | 2 + src/App/Stats/StatService.cpp | 97 +++++++++++++++++++++++++++++++++ src/App/Stats/StatService.hpp | 22 ++++++++ src/App/Tweaks/TweakService.cpp | 15 +++++ src/App/Tweaks/TweakService.hpp | 4 ++ src/App/Version.rc | 4 +- src/Red/Addresses.hpp | 7 ++- src/Red/StatsDataSystem.hpp | 45 +++++++++++++++ src/pch.hpp | 2 + tools/ida/scan.py | 10 ++++ 10 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 src/App/Stats/StatService.cpp create mode 100644 src/App/Stats/StatService.hpp create mode 100644 src/Red/StatsDataSystem.hpp diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 5628881..c003b31 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1,5 +1,6 @@ #include "Application.hpp" #include "App/Environment.hpp" +#include "App/Stats/StatService.hpp" #include "App/Tweaks/TweakService.hpp" #include "Core/Foundation/RuntimeProvider.hpp" #include "Support/MinHook/MinHookProvider.hpp" @@ -15,4 +16,5 @@ App::Application::Application(HMODULE aHandle, const RED4ext::Sdk*) Register(); Register(Env::GameDir(), Env::TweaksDir()); + Register(); } diff --git a/src/App/Stats/StatService.cpp b/src/App/Stats/StatService.cpp new file mode 100644 index 0000000..d686345 --- /dev/null +++ b/src/App/Stats/StatService.cpp @@ -0,0 +1,97 @@ +#include "StatService.hpp" +#include "App/Tweaks/TweakService.hpp" +#include "Core/Facades/Container.hpp" + +namespace +{ +constexpr auto BaseStatPrefix = "BaseStats."; +constexpr auto BaseStatPrefixLength = std::char_traits::length(BaseStatPrefix); +constexpr auto BaseStatCount = static_cast(Red::game::data::StatType::Count); +constexpr auto InvalidStat = static_cast(Red::game::data::StatType::Invalid); + +bool s_statTypesModified = false; +} + +void App::StatService::OnBootstrap() +{ + HookAfter(&OnInitializeStats); +} + +void App::StatService::OnInitializeStats(void* aSystem) +{ + auto statRecords = Raw::StatsDataSystem::StatRecords::Get(aSystem); + auto statTypeEnum = Red::GetDescriptor(); + + auto& tweakManager = Core::Resolve()->GetManager(); + auto& tweakChangelog = Core::Resolve()->GetChangelog(); + + for (const auto& recordId : tweakChangelog.GetAffectedRecords()) + { + const auto& recordName = tweakManager.GetName(recordId); + if (recordName.starts_with(BaseStatPrefix)) + { + auto enumName = tweakManager.GetFlat({recordId, ".enumName"}).As().c_str(); + if (!statTypeEnum->HasOption(enumName)) + { + if (recordName.substr(BaseStatPrefixLength) != enumName) + { + LogError("{}: Enum name must match the record name.", recordName); + continue; + } + + if (statRecords->size == BaseStatCount) + { + // Add dummy entries for "Count" and "Invalid" + statRecords->EmplaceBack(); + statRecords->EmplaceBack(); + } + + statTypeEnum->AddOption(statRecords->size, enumName); + statRecords->PushBack(recordId); + + if (!s_statTypesModified) + { + s_statTypesModified = true; + Hook(&OnGetStatFlags); + Hook(&OnGetStatRange); + } + } + } + } +} + +uint32_t App::StatService::OnGetStatFlags(void* aSystem, uint32_t aStat) +{ + if (aStat != InvalidStat) + { + auto& statParams = Raw::StatsDataSystem::StatParams::Ref(aSystem); + auto& statLock = Raw::StatsDataSystem::StatLock::Ref(aSystem); + + std::shared_lock _(statLock); + if (aStat < statParams.size) + { + return statParams[aStat].flags; + } + } + + return 0; +} + +uint64_t* App::StatService::OnGetStatRange(void* aSystem, uint64_t* aRange, uint32_t aStat) +{ + if (aStat != InvalidStat) + { + auto& statParams = Raw::StatsDataSystem::StatParams::Ref(aSystem); + auto& statLock = Raw::StatsDataSystem::StatLock::Ref(aSystem); + + std::shared_lock _(statLock); + if (aStat < statParams.size) + { + *aRange = statParams[aStat].range; + return aRange; + } + } + + *aRange = 0; + return aRange; +} diff --git a/src/App/Stats/StatService.hpp b/src/App/Stats/StatService.hpp new file mode 100644 index 0000000..dab70ae --- /dev/null +++ b/src/App/Stats/StatService.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "Core/Foundation/Feature.hpp" +#include "Core/Hooking/HookingAgent.hpp" +#include "Core/Logging/LoggingAgent.hpp" +#include "Red/StatsDataSystem.hpp" + +namespace App +{ +class StatService + : public Core::Feature + , public Core::HookingAgent + , public Core::LoggingAgent +{ +protected: + void OnBootstrap() override; + + static void OnInitializeStats(void* aSystem); + static uint32_t OnGetStatFlags(void* aSystem, uint32_t aStat); + static uint64_t* OnGetStatRange(void* aSystem, uint64_t* aRange, uint32_t aStat); +}; +} diff --git a/src/App/Tweaks/TweakService.cpp b/src/App/Tweaks/TweakService.cpp index 7f54ffa..1b8f058 100644 --- a/src/App/Tweaks/TweakService.cpp +++ b/src/App/Tweaks/TweakService.cpp @@ -114,3 +114,18 @@ bool App::TweakService::RegisterDirectory(std::filesystem::path aPath) m_importPaths.emplace_back(std::move(aPath)); return true; } + +Red::TweakDBManager& App::TweakService::GetManager() +{ + return *m_manager; +} + +Red::TweakDBReflection& App::TweakService::GetReflection() +{ + return *m_reflection; +} + +App::TweakChangelog& App::TweakService::GetChangelog() +{ + return *m_changelog; +} diff --git a/src/App/Tweaks/TweakService.hpp b/src/App/Tweaks/TweakService.hpp index ef01cc4..2621d37 100644 --- a/src/App/Tweaks/TweakService.hpp +++ b/src/App/Tweaks/TweakService.hpp @@ -27,6 +27,10 @@ class TweakService void ExecuteTweaks(); void ExecuteTweak(Red::CName aName); + Red::TweakDBManager& GetManager(); + Red::TweakDBReflection& GetReflection(); + App::TweakChangelog& GetChangelog(); + protected: void OnBootstrap() override; void CreateTweaksDir(); diff --git a/src/App/Version.rc b/src/App/Version.rc index 636cbe9..d2c24da 100644 --- a/src/App/Version.rc +++ b/src/App/Version.rc @@ -1,9 +1,9 @@ #define VER_PRODUCTVERSION 1,2,0,0 -#define VER_FILEVERSION 1,2,0,2308010056 +#define VER_FILEVERSION 1,2,0,2308041748 #define VER_PRODUCTNAME_STR "TweakXL\0" #define VER_PRODUCTVERSION_STR "1.2.0\0" -#define VER_FILEVERSION_STR "1.2.0.2308010056\0" +#define VER_FILEVERSION_STR "1.2.0.2308041748\0" 1 VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/src/Red/Addresses.hpp b/src/Red/Addresses.hpp index 68a807a..9d22e17 100644 --- a/src/Red/Addresses.hpp +++ b/src/Red/Addresses.hpp @@ -1,6 +1,6 @@ #pragma once -// Generated by cp77ida.py on 2023-06-26 for Cyberpunk 2077 v.1.63 +// Generated by cp77ida.py on 2023-08-04 for Cyberpunk 2077 v.1.63 // DO NOT MODIFY. USE tools\ida\scan.py TO GENERATE THIS FILE. #include @@ -11,6 +11,11 @@ constexpr uintptr_t ImageBase = 0x140000000; constexpr uintptr_t Main = 0x1401A0550 - ImageBase; // 40 53 48 81 EC ? ? ? ? FF 15 ? ? ? ? E8 ? ? ? ? E8 ? ? ? ?, expected: 1, index: 0 +constexpr uintptr_t StatsDataSystem_InitializeRecords = 0x141A26120 - ImageBase; // 48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC ? 48 8B F9 E8 ? ? ? ? 48 BA, expected: 1, index: 0 +constexpr uintptr_t StatsDataSystem_InitializeParams = 0x141A262C0 - ImageBase; // 48 8B C4 41 54 41 56 48 81 EC ? ? ? ? 8B 91 ? ? ? ? 4C 8D B1 ? ? ? ? 45 33 E4 48 89 58 ? 48 89 78, expected: 1, index: 0 +constexpr uintptr_t StatsDataSystem_GetStatFlags = 0x141A24810 - ImageBase; // 48 89 74 24 ? 57 48 83 EC ? 8B FA 48 8B F1 81 FA ? ? ? ? 73 ? 48 81 C1 ? ? ? ? 48 89 5C 24, expected: 1, index: 0 +constexpr uintptr_t StatsDataSystem_GetStatRange = 0x141A24880 - ImageBase; // 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 41 8B F0 48 8B FA 48 8B E9 41 81 F8 ? ? ? ? 73, expected: 1, index: 0 + constexpr uintptr_t TweakDB_Load = 0x140BE6B50 - ImageBase; // 48 89 5C 24 18 55 57 41 56 48 8B EC 48 83 EC 70 48 8B D9 45 33 F6 48 8D, expected: 1, index: 0 constexpr uintptr_t TweakDB_CreateRecord = 0x140FD4930 - ImageBase; // 48 89 5C 24 08 ? 89 ? 24 18 57 48 83 EC 30 8B C2, expected: 1, index: 0 diff --git a/src/Red/StatsDataSystem.hpp b/src/Red/StatsDataSystem.hpp new file mode 100644 index 0000000..979e200 --- /dev/null +++ b/src/Red/StatsDataSystem.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "Red/Addresses.hpp" + +namespace Red +{ +struct StatParams +{ +#pragma pack(push, 1) + union + { + uint64_t range; + struct + { + float min; + float max; + }; + }; +#pragma pack(pop) + uint32_t flags; +}; +} + +namespace Raw::StatsDataSystem +{ +using StatRecords = Core::OffsetPtr<0xD8, Red::DynArray>; +using StatParams = Core::OffsetPtr<0xE8, Red::DynArray>; +using StatLock = Core::OffsetPtr<0xFC, Red::SharedMutex>; + +constexpr auto InitializeRecords = Core::RawFunc< + /* addr = */ Red::Addresses::StatsDataSystem_InitializeRecords, + /* type = */ void (*)(void* aSystem)>(); + +constexpr auto InitializeParams = Core::RawFunc< + /* addr = */ Red::Addresses::StatsDataSystem_InitializeParams, + /* type = */ void (*)(void* aSystem)>(); + +constexpr auto GetStatFlags = Core::RawFunc< + /* addr = */ Red::Addresses::StatsDataSystem_GetStatFlags, + /* type = */ uint32_t (*)(void* aSystem, uint32_t aStat)>(); + +constexpr auto GetStatRange = Core::RawFunc< + /* addr = */ Red::Addresses::StatsDataSystem_GetStatRange, + /* type = */ uint64_t* (*)(void* aSystem, uint64_t*, uint32_t aStat)>(); +} diff --git a/src/pch.hpp b/src/pch.hpp index fc7bfbe..41cdaa5 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/tools/ida/scan.py b/tools/ida/scan.py index 430855b..50e11a3 100644 --- a/tools/ida/scan.py +++ b/tools/ida/scan.py @@ -20,6 +20,16 @@ def patterns(): Item(name="Derive", pattern="40 53 48 83 EC 30 33 C0 4C 89 44 24 20 48 8B DA"), ]), + Group(name="StatsDataSystem", functions=[ + Item(name="InitializeRecords", + pattern="48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC ? 48 8B F9 E8 ? ? ? ? 48 BA"), + Item(name="InitializeParams", + pattern="48 8B C4 41 54 41 56 48 81 EC ? ? ? ? 8B 91 ? ? ? ? 4C 8D B1 ? ? ? ? 45 33 E4 48 89 58 ? 48 89 78"), + Item(name="GetStatFlags", + pattern="48 89 74 24 ? 57 48 83 EC ? 8B FA 48 8B F1 81 FA ? ? ? ? 73 ? 48 81 C1 ? ? ? ? 48 89 5C 24"), + Item(name="GetStatRange", + pattern="48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 41 8B F0 48 8B FA 48 8B E9 41 81 F8 ? ? ? ? 73"), + ]), ]), ]