From 974442b78cd65e9859d464b9005db3f0f5ad0655 Mon Sep 17 00:00:00 2001 From: kedou Date: Sat, 8 Aug 2026 18:09:32 +0800 Subject: [PATCH] feat: add config persistence with INI load/save and debug mode --- .gitignore | 5 +- CFramework/Cheat/Visual.cpp | 3 + CFramework/Framework/Config/Config.cpp | 281 ++++++++++++++++++++++- CFramework/Framework/Config/Config.h | 10 +- CFramework/Framework/Overlay/Overlay.cpp | 2 +- CFramework/main.cpp | 9 +- 6 files changed, 304 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 362790a..7c0e1a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ x86 x64 Debug -Release \ No newline at end of file +Release +CFramework.ini +CFramework_imgui.ini +.serena/ \ No newline at end of file diff --git a/CFramework/Cheat/Visual.cpp b/CFramework/Cheat/Visual.cpp index bebdf41..4930412 100644 --- a/CFramework/Cheat/Visual.cpp +++ b/CFramework/Cheat/Visual.cpp @@ -97,6 +97,9 @@ namespace void CFramework::RenderInfo() { + if (!g.g_DebugMode) + return; + const auto snapshot = GetDynamicSnapshot(); const bool snapshotFresh = snapshot && IsSnapshotFresh(snapshot->capturedAt); diff --git a/CFramework/Framework/Config/Config.cpp b/CFramework/Framework/Config/Config.cpp index ccce795..4c84b3b 100644 --- a/CFramework/Framework/Config/Config.cpp +++ b/CFramework/Framework/Config/Config.cpp @@ -1,3 +1,282 @@ #include "Config.h" +#include +#include +#include +#include +#include +#include +#include -Globals g; \ No newline at end of file +Globals g; + +#define CFW_CONFIG_BOOL_FIELDS(X) \ + X(g_DebugMode) \ + X(g_ESP) \ + X(g_ESP_Item) \ + X(g_ESP_Corpse) \ + X(g_ESP_Container) \ + X(g_ESP_Exfil) \ + X(g_ESP_Grenade) \ + X(g_ESP_Line) \ + X(g_ESP_Box) \ + X(g_ESP_BoxFilled) \ + X(g_ESP_Skeleton) \ + X(g_ESP_Distance) \ + X(g_ESP_Name) \ + X(g_ESP_HealthBar) \ + X(g_InfStamina) \ + X(g_NoFallDamage) \ + X(g_NoSway) \ + X(g_NoRecoil) \ + X(g_NoMalfunction) \ + X(g_Aim) \ + X(g_AimTargetPMC) \ + X(g_AimTargetScav) \ + X(g_AimTargetBoss) \ + X(g_AimIgnoreHealth) \ + X(g_AimForce) \ + X(g_AimDrawFov) \ + X(g_AimDrawTarget) \ + X(g_Recoil) \ + X(g_StreamProof) \ + X(g_Crosshair) + +#define CFW_CONFIG_INT_FIELDS(X) \ + X(g_ESP_BoxStyle) \ + X(g_AimMethod) \ + X(g_AimBone) \ + X(g_RecoilInterval) \ + X(g_AimMcOffset) \ + X(g_AimRotOffset) \ + X(g_CrosshairSize) \ + X(g_CrosshairType) \ + X(g_MenuKey) \ + X(g_AimKey0) \ + X(g_AimKey1) \ + X(g_AimKey2) + +#define CFW_CONFIG_FLOAT_FIELDS(X) \ + X(g_ESP_MaxDistance) \ + X(g_ESP_MaxItemDistance) \ + X(g_ESP_MaxContainerDistance) \ + X(g_AimFov) \ + X(g_AimSmooth) \ + X(g_AimSens) \ + X(g_AimMaxSpeed) \ + X(g_AimMaxDist) \ + X(g_RecoilStrength) + +namespace +{ + using ConfigValues = std::unordered_map; + + std::filesystem::path GetConfigPath() + { + wchar_t modulePath[MAX_PATH]{}; + const DWORD length = GetModuleFileNameW(nullptr, modulePath, MAX_PATH); + if (length == 0 || length >= MAX_PATH) + return std::filesystem::path(L"CFramework.ini"); + + return std::filesystem::path(modulePath).parent_path() / L"CFramework.ini"; + } + + void Trim(std::string& value) + { + const std::size_t first = value.find_first_not_of(" \t\r\n"); + if (first == std::string::npos) + { + value.clear(); + return; + } + + const std::size_t last = value.find_last_not_of(" \t\r\n"); + value = value.substr(first, last - first + 1); + } + + ConfigValues ReadValues(std::ifstream& input) + { + ConfigValues values; + std::string line; + while (std::getline(input, line)) + { + Trim(line); + if (line.empty() || line.front() == '#' || line.front() == ';') + continue; + + const std::size_t separator = line.find('='); + if (separator == std::string::npos) + continue; + + std::string key = line.substr(0, separator); + std::string value = line.substr(separator + 1); + Trim(key); + Trim(value); + if (!key.empty()) + values[key] = value; + } + return values; + } + + bool ReadBool(const ConfigValues& values, const char* key, bool& output) + { + const auto it = values.find(key); + if (it == values.end()) + return false; + + if (it->second == "1" || it->second == "true" || it->second == "TRUE") + { + output = true; + return true; + } + if (it->second == "0" || it->second == "false" || it->second == "FALSE") + { + output = false; + return true; + } + return false; + } + + bool ReadInt(const ConfigValues& values, const char* key, int& output) + { + const auto it = values.find(key); + if (it == values.end()) + return false; + + try + { + std::size_t consumed = 0; + const int parsed = std::stoi(it->second, &consumed, 0); + if (consumed != it->second.size()) + return false; + output = parsed; + return true; + } + catch (...) + { + return false; + } + } + + bool ReadFloat(const ConfigValues& values, const char* key, float& output) + { + const auto it = values.find(key); + if (it == values.end()) + return false; + + try + { + std::size_t consumed = 0; + const float parsed = std::stof(it->second, &consumed); + if (consumed != it->second.size() || !std::isfinite(parsed)) + return false; + output = parsed; + return true; + } + catch (...) + { + return false; + } + } + + void WriteBool(std::ofstream& output, const char* key, bool value) + { + output << key << '=' << (value ? 1 : 0) << '\n'; + } + + void WriteInt(std::ofstream& output, const char* key, int value) + { + output << key << '=' << value << '\n'; + } + + void WriteFloat(std::ofstream& output, const char* key, float value) + { + output << key << '=' << std::setprecision(9) << value << '\n'; + } + + void ClampLoadedConfig() + { + g.g_ESP_BoxStyle = (std::clamp)(g.g_ESP_BoxStyle, 0, 1); + g.g_AimMethod = (std::clamp)(g.g_AimMethod, 0, 1); + g.g_AimBone = (std::clamp)(g.g_AimBone, 0, 2); + g.g_RecoilInterval = (std::clamp)(g.g_RecoilInterval, 1, 1000); + g.g_AimMcOffset = (std::clamp)(g.g_AimMcOffset, 0, 0x1000); + g.g_AimRotOffset = (std::clamp)(g.g_AimRotOffset, 0, 0x1000); + g.g_CrosshairSize = (std::clamp)(g.g_CrosshairSize, 1, 32); + g.g_CrosshairType = (std::clamp)(g.g_CrosshairType, 0, 1); + g.g_MenuKey = (std::clamp)(g.g_MenuKey, 1, 255); + g.g_AimKey0 = (std::clamp)(g.g_AimKey0, 1, 255); + g.g_AimKey1 = (std::clamp)(g.g_AimKey1, 1, 255); + g.g_AimKey2 = (std::clamp)(g.g_AimKey2, 1, 255); + + g.g_ESP_MaxDistance = (std::clamp)(g.g_ESP_MaxDistance, 0.f, 5000.f); + g.g_ESP_MaxItemDistance = (std::clamp)(g.g_ESP_MaxItemDistance, 0.f, 5000.f); + g.g_ESP_MaxContainerDistance = (std::clamp)(g.g_ESP_MaxContainerDistance, 0.f, 5000.f); + g.g_AimFov = (std::clamp)(g.g_AimFov, 1.f, 2000.f); + g.g_AimSmooth = (std::clamp)(g.g_AimSmooth, 0.f, 100.f); + g.g_AimSens = (std::clamp)(g.g_AimSens, 0.01f, 20.f); + g.g_AimMaxSpeed = (std::clamp)(g.g_AimMaxSpeed, 0.f, 2000.f); + g.g_AimMaxDist = (std::clamp)(g.g_AimMaxDist, 0.f, 5000.f); + g.g_RecoilStrength = (std::clamp)(g.g_RecoilStrength, 0.f, 100.f); + } +} + +namespace Config +{ + bool Load() + { + std::ifstream input(GetConfigPath(), std::ios::in); + if (!input.is_open()) + return false; + + const ConfigValues values = ReadValues(input); + +#define READ_BOOL(field) ReadBool(values, #field, g.field); + CFW_CONFIG_BOOL_FIELDS(READ_BOOL) +#undef READ_BOOL + +#define READ_INT(field) ReadInt(values, #field, g.field); + CFW_CONFIG_INT_FIELDS(READ_INT) +#undef READ_INT + +#define READ_FLOAT(field) ReadFloat(values, #field, g.field); + CFW_CONFIG_FLOAT_FIELDS(READ_FLOAT) +#undef READ_FLOAT + + ClampLoadedConfig(); + return true; + } + + bool Save() + { + std::ofstream output(GetConfigPath(), std::ios::out | std::ios::trunc); + if (!output.is_open()) + return false; + + output << "# CFramework configuration v1\n"; + output << "version=1\n"; + +#define WRITE_BOOL(field) WriteBool(output, #field, g.field); + CFW_CONFIG_BOOL_FIELDS(WRITE_BOOL) +#undef WRITE_BOOL + +#define WRITE_INT(field) WriteInt(output, #field, g.field); + CFW_CONFIG_INT_FIELDS(WRITE_INT) +#undef WRITE_INT + +#define WRITE_FLOAT(field) WriteFloat(output, #field, g.field); + CFW_CONFIG_FLOAT_FIELDS(WRITE_FLOAT) +#undef WRITE_FLOAT + + output.flush(); + return output.good(); + } + + const char* FileName() + { + return "CFramework.ini"; + } +} + +#undef CFW_CONFIG_BOOL_FIELDS +#undef CFW_CONFIG_INT_FIELDS +#undef CFW_CONFIG_FLOAT_FIELDS diff --git a/CFramework/Framework/Config/Config.h b/CFramework/Framework/Config/Config.h index 1d55e52..e442daf 100644 --- a/CFramework/Framework/Config/Config.h +++ b/CFramework/Framework/Config/Config.h @@ -70,6 +70,7 @@ struct Globals // System bool g_StreamProof = false; + bool g_DebugMode = false; bool g_Crosshair = true; int g_CrosshairSize = 3; int g_CrosshairType = 0; @@ -81,4 +82,11 @@ struct Globals int g_AimKey2 = VK_F9; // 键盘触发键 (鼠标键检测异常时的替代, 按住触发) }; -extern Globals g; \ No newline at end of file +extern Globals g; + +namespace Config +{ + bool Load(); + bool Save(); + const char* FileName(); +} \ No newline at end of file diff --git a/CFramework/Framework/Overlay/Overlay.cpp b/CFramework/Framework/Overlay/Overlay.cpp index 3a5ca2a..bf0e0cf 100644 --- a/CFramework/Framework/Overlay/Overlay.cpp +++ b/CFramework/Framework/Overlay/Overlay.cpp @@ -43,7 +43,7 @@ bool Overlay::CreateOverlay() IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.IniFilename = nullptr; + io.IniFilename = "CFramework_imgui.ini"; io.LogFilename = nullptr; LoadStyle(); diff --git a/CFramework/main.cpp b/CFramework/main.cpp index a089d50..3f3a4e3 100644 --- a/CFramework/main.cpp +++ b/CFramework/main.cpp @@ -1,6 +1,7 @@ #include "Cheat/Framework.h" #include "Framework/Overlay/Overlay.h" #include "Framework/Input/Input.h" +#include "Framework/Config/Config.h" #pragma comment(lib, "freetype.lib") /* @@ -47,6 +48,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // 使用每监视器 DPI,保证 overlay、GameRect 和 W2S 使用同一套物理像素坐标。 SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + // 先读取用户配置,再创建 overlay 和启动 worker。 + Config::Load(); + // ウィンドウをベースにして初期化を行う if (!m.AttachProcess("EscapeFromTarkov", InitMode::WINDOW_TITLE)) // 詳細は Memory/Memory.h を参照 return 1; @@ -60,7 +64,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // ImGui io setting ImGuiIO& io = ImGui::GetIO(); (void)io; - io.IniFilename = nullptr; + io.IniFilename = "CFramework_imgui.ini"; io.LogFilename = nullptr; // Load Font @@ -104,8 +108,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine staticSnapshotThread.join(); cheat->ResetFeatureOverrides(); + Config::Save(); overlay->DestroyOverlay(); m.DetachProcess(); return 0; -} \ No newline at end of file +}