feat: add config persistence with INI load/save and debug mode
This commit is contained in:
parent
baabc7878e
commit
974442b78c
|
|
@ -3,3 +3,6 @@ x86
|
|||
x64
|
||||
Debug
|
||||
Release
|
||||
CFramework.ini
|
||||
CFramework_imgui.ini
|
||||
.serena/
|
||||
|
|
@ -97,6 +97,9 @@ namespace
|
|||
|
||||
void CFramework::RenderInfo()
|
||||
{
|
||||
if (!g.g_DebugMode)
|
||||
return;
|
||||
|
||||
const auto snapshot = GetDynamicSnapshot();
|
||||
const bool snapshotFresh = snapshot && IsSnapshotFresh(snapshot->capturedAt);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,282 @@
|
|||
#include "Config.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -82,3 +83,10 @@ struct Globals
|
|||
};
|
||||
|
||||
extern Globals g;
|
||||
|
||||
namespace Config
|
||||
{
|
||||
bool Load();
|
||||
bool Save();
|
||||
const char* FileName();
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,6 +108,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||
staticSnapshotThread.join();
|
||||
|
||||
cheat->ResetFeatureOverrides();
|
||||
Config::Save();
|
||||
overlay->DestroyOverlay();
|
||||
m.DetachProcess();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue