feat: add config persistence with INI load/save and debug mode

This commit is contained in:
kedou 2026-08-08 18:09:32 +08:00
parent baabc7878e
commit 974442b78c
6 changed files with 304 additions and 6 deletions

5
.gitignore vendored
View File

@ -2,4 +2,7 @@
x86
x64
Debug
Release
Release
CFramework.ini
CFramework_imgui.ini
.serena/

View File

@ -97,6 +97,9 @@ namespace
void CFramework::RenderInfo()
{
if (!g.g_DebugMode)
return;
const auto snapshot = GetDynamicSnapshot();
const bool snapshotFresh = snapshot && IsSnapshotFresh(snapshot->capturedAt);

View File

@ -1,3 +1,282 @@
#include "Config.h"
#include <algorithm>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <string>
#include <unordered_map>
Globals g;
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::string, std::string>;
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

View File

@ -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;
extern Globals g;
namespace Config
{
bool Load();
bool Save();
const char* FileName();
}

View File

@ -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();

View File

@ -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;
}
}