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"),
+ ]),
]),
]