399 lines
11 KiB
C++
399 lines
11 KiB
C++
#define _CRT_SECURE_NO_WARNINGS
|
|
#include "Config.h"
|
|
#include "../Utilities/Utilities.h"
|
|
#include "../../Backend/Globalincludes.h"
|
|
#include "../Main/Main.h"
|
|
#include <filesystem>
|
|
#include <array>
|
|
|
|
namespace {
|
|
std::string color_to_string(const int col[4]) {
|
|
return std::to_string(col[0]) + "," + std::to_string(col[1]) + "," + std::to_string(col[2]) + "," + std::to_string(col[3]);
|
|
}
|
|
|
|
std::array<int, 4> string_to_color(const std::string& s) {
|
|
static auto split = [](const std::string& str, const char* del) -> std::vector<std::string>
|
|
{
|
|
char* pTempStr = _strdup(str.c_str());
|
|
char* pWord = strtok(pTempStr, del);
|
|
std::vector<std::string> dest;
|
|
|
|
while (pWord != NULL)
|
|
{
|
|
dest.push_back(pWord);
|
|
pWord = strtok(NULL, del);
|
|
}
|
|
|
|
free(pTempStr);
|
|
|
|
return dest;
|
|
};
|
|
|
|
std::vector<std::string> col = split(s, ",");
|
|
return { std::stoi(col.at(0)), std::stoi(col.at(1)), std::stoi(col.at(2)), std::stoi(col.at(3)) };
|
|
}
|
|
}
|
|
|
|
void CConfig::GetFileName(char* _buffer, bool db) {
|
|
strcpy_s(_buffer, 255, this->s["config_name"]);
|
|
}
|
|
|
|
void CConfig::GetFilePath(char* _buffer, bool db) {
|
|
char buffer[MAX_PATH];
|
|
char filename[255];
|
|
GetFileName(filename);
|
|
GetCurrentDirectoryA(MAX_PATH, buffer);
|
|
|
|
if (!db)
|
|
sprintf_s(buffer, "%s\\configs\\%s.cfg", buffer, filename);
|
|
else
|
|
sprintf_s(buffer, "%s\\configs", buffer);
|
|
|
|
strcpy_s(_buffer, 260, buffer);
|
|
}
|
|
|
|
void CConfig::LoadDefaults() {
|
|
Refresh();
|
|
|
|
CConfig* cfg = CConfig::get();
|
|
|
|
{
|
|
cfg->b["isAdmin"] = IsRunningAsAdmin();
|
|
}
|
|
|
|
{
|
|
cfg->b["time"] = false;
|
|
cfg->i["time_format"] = 0;
|
|
cfg->b["text_border"] = false;
|
|
cfg->i["time_text_size"] = 45;
|
|
cfg->i["time_text_character_spacing"] = 0;
|
|
cfg->c["time_text_Color"][0] = 255;
|
|
cfg->c["time_text_Color"][1] = 255;
|
|
cfg->c["time_text_Color"][2] = 255;
|
|
cfg->c["time_text_Color"][3] = 255;
|
|
cfg->f["time_x_pos"] = 0.0f;
|
|
cfg->f["time_y_pos"] = 0.0f;
|
|
cfg->c["text_border_Color"][0] = 0;
|
|
cfg->c["text_border_Color"][1] = 0;
|
|
cfg->c["text_border_Color"][2] = 0;
|
|
cfg->c["text_border_Color"][3] = 255;
|
|
}
|
|
|
|
{
|
|
cfg->b["keyboard_control"] = false;
|
|
cfg->i["Caps_control"] = 0;
|
|
cfg->i["Num_control"] = 0;
|
|
strncpy_s(cfg->s["keyboard_whitelist"], 255, "", 254);
|
|
}
|
|
|
|
{
|
|
cfg->c["MenuColor"][0] = 163;
|
|
cfg->c["MenuColor"][1] = 212;
|
|
cfg->c["MenuColor"][2] = 31;
|
|
cfg->c["MenuColor"][3] = 255;
|
|
strncpy_s(cfg->s["menu_key_bind"], 255, "163,97", 254); // Ctrl + 1 (top row, NumLock-independent)
|
|
GamesenseMenuFramework::Globals::MultiKeyCache("menu_key_bind") = "163,97";
|
|
cfg->i["menu_keystyle"] = 4;
|
|
cfg->i["menu_key"] = VK_INSERT; // fallback single-key
|
|
cfg->i["menu_scale"] = 0;
|
|
cfg->b["lock_menu_layout"] = true;
|
|
cfg->b["show_console"] = true;
|
|
}
|
|
}
|
|
|
|
// --- menu.cfg tracking helpers (must be before Load/Save) ---
|
|
|
|
static void GetMenuCfgPath(char* buf, int bufSize) {
|
|
GetCurrentDirectoryA(bufSize, buf);
|
|
sprintf_s(buf, bufSize, "%s\\configs\\menu.configs", buf);
|
|
}
|
|
|
|
static void WriteMenuCfg(const char* configName, bool hasManual) {
|
|
char path[MAX_PATH];
|
|
GetMenuCfgPath(path, MAX_PATH);
|
|
WritePrivateProfileStringA("Config", "LastConfig", configName, path);
|
|
WritePrivateProfileStringA("Config", "HasManualSave", hasManual ? "1" : "0", path);
|
|
}
|
|
|
|
static void ReadMenuCfg(char* outName, int outSize, bool* outHasManual) {
|
|
char path[MAX_PATH];
|
|
GetMenuCfgPath(path, MAX_PATH);
|
|
GetPrivateProfileStringA("Config", "LastConfig", "", outName, outSize, path);
|
|
*outHasManual = GetPrivateProfileIntA("Config", "HasManualSave", 0, path) != 0;
|
|
}
|
|
|
|
// ---
|
|
|
|
void CConfig::AutoLoad() {
|
|
char lastConfig[255] = "";
|
|
bool hasManual;
|
|
ReadMenuCfg(lastConfig, sizeof(lastConfig), &hasManual);
|
|
this->hasManualSave = hasManual;
|
|
|
|
// Try the last known config first
|
|
if (lastConfig[0] != 0) {
|
|
strcpy_s(this->s["config_name"], lastConfig);
|
|
char filepath[MAX_PATH];
|
|
GetFilePath(filepath);
|
|
if (GetFileAttributesA(filepath) != INVALID_FILE_ATTRIBUTES) {
|
|
Load();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Fallback: find an existing profile-*.cfg
|
|
char configsPath[MAX_PATH];
|
|
GetFilePath(configsPath, true);
|
|
if (std::filesystem::exists(configsPath)) {
|
|
std::string bestDefault;
|
|
for (auto& entry : std::filesystem::directory_iterator(configsPath)) {
|
|
if (entry.path().extension() == ".cfg") {
|
|
std::string name = entry.path().stem().string();
|
|
if (name.find("profile-") == 0 && name > bestDefault)
|
|
bestDefault = name;
|
|
}
|
|
}
|
|
if (!bestDefault.empty()) {
|
|
strcpy_s(this->s["config_name"], bestDefault.c_str());
|
|
Load();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Nothing found — use in-memory defaults (already set by LoadDefaults)
|
|
this->s["config_name"][0] = 0;
|
|
}
|
|
|
|
void CConfig::AutoSave() {
|
|
if (this->hasManualSave) {
|
|
if (this->s["config_name"][0] != 0)
|
|
Save();
|
|
} else {
|
|
if (this->s["config_name"][0] == 0)
|
|
strcpy_s(this->s["config_name"], "profile-auto");
|
|
Save();
|
|
}
|
|
WriteMenuCfg(this->s["config_name"], this->hasManualSave);
|
|
}
|
|
|
|
void CConfig::Load() {
|
|
if (this->s["config_name"][0] == 0)
|
|
return;
|
|
this->Current = this->s["config_name"];
|
|
Refresh();
|
|
|
|
char file_path[MAX_PATH] = { 0 };
|
|
GetFilePath(file_path);
|
|
|
|
// Read b (bool) entries from INI
|
|
for (auto& e : b) {
|
|
if (e.first.find("_") == 0) continue;
|
|
e.second = GetPrivateProfileIntA("b", e.first.c_str(), e.second, file_path) != 0;
|
|
}
|
|
|
|
// Read i (int) entries from INI
|
|
for (auto& e : i) {
|
|
if (e.first.find("_") == 0) continue;
|
|
e.second = GetPrivateProfileIntA("i", e.first.c_str(), e.second, file_path);
|
|
}
|
|
|
|
// Read f (float) entries from INI
|
|
for (auto& e : f) {
|
|
if (e.first.find("_") == 0) continue;
|
|
char buffer[64] = { 0 };
|
|
GetPrivateProfileStringA("f", e.first.c_str(), "", buffer, 63, file_path);
|
|
if (buffer[0] != 0)
|
|
e.second = (float)atof(buffer);
|
|
}
|
|
|
|
// Read c (color) entries from INI
|
|
for (auto& e : c) {
|
|
if (e.first.find("_") == 0) continue;
|
|
char buffer[64] = { 0 };
|
|
GetPrivateProfileStringA("c", e.first.c_str(), "", buffer, 63, file_path);
|
|
if (buffer[0] != 0) {
|
|
auto col = string_to_color(buffer);
|
|
e.second[0] = col[0];
|
|
e.second[1] = col[1];
|
|
e.second[2] = col[2];
|
|
e.second[3] = col[3];
|
|
}
|
|
}
|
|
|
|
// Read m (map) entries from INI
|
|
for (auto& e : m) {
|
|
if (e.first.find("_") == 0) continue;
|
|
char buffer[4096] = { 0 };
|
|
GetPrivateProfileStringA("m", e.first.c_str(), "", buffer, 4095, file_path);
|
|
if (buffer[0] != 0) {
|
|
e.second.clear();
|
|
char* ctx = nullptr;
|
|
char* token = strtok_s(buffer, "|", &ctx);
|
|
while (token) {
|
|
int key = 0, val = 0;
|
|
if (sscanf_s(token, "%d:%d", &key, &val) == 2)
|
|
e.second[key] = val != 0;
|
|
token = strtok_s(nullptr, "|", &ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read s (string) entries from INI
|
|
for (auto& e : s) {
|
|
if (e.first == "config_name") continue;
|
|
GetPrivateProfileStringA("s", e.first.c_str(), e.second, e.second, 255, file_path);
|
|
}
|
|
|
|
// Sync MultiKeyCache with loaded s values so IsBindActive uses the correct bindings
|
|
for (auto& e : s) {
|
|
if (e.first.size() > 5 && e.first.compare(e.first.size() - 5, 5, "_bind") == 0) {
|
|
GamesenseMenuFramework::Globals::MultiKeyCache(e.first) = e.second;
|
|
}
|
|
}
|
|
|
|
WriteMenuCfg(this->s["config_name"], this->hasManualSave);
|
|
}
|
|
|
|
void CConfig::Save() {
|
|
if (this->s["config_name"][0] == 0)
|
|
return;
|
|
|
|
char configs_path[260];
|
|
GetFilePath(configs_path, true);
|
|
std::filesystem::create_directories(configs_path);
|
|
|
|
char file_path[MAX_PATH] = { 0 };
|
|
GetFilePath(file_path);
|
|
Misc::Utilities->Console_Log("Saving config: %s", file_path);
|
|
|
|
for (auto e : b) {
|
|
if (!std::string(e.first).find("_")) continue;
|
|
char buffer[8] = { 0 }; _itoa(e.second, buffer, 10);
|
|
WritePrivateProfileStringA("b", e.first.c_str(), std::string(buffer).c_str(), file_path);
|
|
}
|
|
|
|
for (auto e : i) {
|
|
if (!std::string(e.first).find("_")) continue;
|
|
char buffer[32] = { 0 }; _itoa(e.second, buffer, 10);
|
|
WritePrivateProfileStringA("i", e.first.c_str(), std::string(buffer).c_str(), file_path);
|
|
}
|
|
|
|
for (auto e : f) {
|
|
if (!std::string(e.first).find("_")) continue;
|
|
char buffer[64] = { 0 }; sprintf(buffer, "%f", e.second);
|
|
WritePrivateProfileStringA("f", e.first.c_str(), std::string(buffer).c_str(), file_path);
|
|
}
|
|
|
|
for (auto e : c) {
|
|
if (!std::string(e.first).find("_")) continue;
|
|
WritePrivateProfileStringA("c", e.first.c_str(), color_to_string(e.second).c_str(), file_path);
|
|
}
|
|
|
|
for (auto e : m) {
|
|
if (!std::string(e.first).find("_")) continue;
|
|
|
|
std::string vs = "";
|
|
for (auto v : e.second)
|
|
vs += std::to_string(v.first) + ":" + std::to_string(v.second) + "|";
|
|
|
|
WritePrivateProfileStringA("m", e.first.c_str(), vs.c_str(), file_path);
|
|
}
|
|
|
|
for (auto e : s) {
|
|
if (e.first == "config_name") continue;
|
|
if (e.second[0] == 0) continue;
|
|
WritePrivateProfileStringA("s", e.first.c_str(), e.second, file_path);
|
|
}
|
|
|
|
this->hasManualSave = true;
|
|
WriteMenuCfg(this->s["config_name"], true);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void CConfig::Delete() {
|
|
if (this->s["config_name"][0] == 0)
|
|
return;
|
|
char path[260];
|
|
GetFilePath(path);
|
|
std::remove(path);
|
|
Refresh();
|
|
}
|
|
|
|
void CConfig::Refresh() {
|
|
this->List.clear();
|
|
char path[260];
|
|
GetFilePath(path, true);
|
|
|
|
if (!std::filesystem::exists(path))
|
|
return;
|
|
|
|
for (auto& entry : std::filesystem::directory_iterator(path)) {
|
|
if (entry.path().extension() == (".cfg")) {
|
|
auto path = entry.path();
|
|
auto path_text = entry.path().string();
|
|
|
|
auto filename = path.filename().string();
|
|
|
|
if (filename != "tempbuffer")
|
|
this->List.push_back(filename.substr(0, filename.length() - 4));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CConfig::IsBindActive(std::string key) {
|
|
auto g = GamesenseMenuFramework::Globals::Gui_Ctx;
|
|
if (!g) return false;
|
|
|
|
switch (this->i[key + "style"]) {
|
|
case 0:
|
|
// Always on
|
|
return true;
|
|
case 1:
|
|
// On hotkey (held)
|
|
return g->KeyState[this->i[key]];
|
|
case 2:
|
|
// Toggle (rising edge)
|
|
return g->KeyState[this->i[key]] && !g->PrevKeyState[this->i[key]];
|
|
case 3:
|
|
// Off hotkey (not held)
|
|
return !g->KeyState[this->i[key]];
|
|
case 4:
|
|
{
|
|
// Multi-key: all stored keys must be held simultaneously
|
|
std::string multiStr = GamesenseMenuFramework::Globals::MultiKeyCache(key + "_bind");
|
|
if (multiStr.empty()) {
|
|
multiStr = this->s[key + "_bind"];
|
|
if (!multiStr.empty() && multiStr[0] != 0)
|
|
GamesenseMenuFramework::Globals::MultiKeyCache(key + "_bind") = multiStr;
|
|
}
|
|
if (multiStr.empty() || multiStr[0] == 0) return false;
|
|
const char* p = multiStr.c_str();
|
|
while (*p) {
|
|
int vk = atoi(p);
|
|
if (vk > 0 && vk < 256) {
|
|
bool held = g->KeyState[vk];
|
|
if (!held) {
|
|
// Accept either left/right modifier
|
|
int pair = 0;
|
|
if (vk == 162) pair = 163;
|
|
else if (vk == 163) pair = 162;
|
|
else if (vk == 160) pair = 161;
|
|
else if (vk == 161) pair = 160;
|
|
else if (vk == 164) pair = 165;
|
|
else if (vk == 165) pair = 164;
|
|
if (pair) held = g->KeyState[pair];
|
|
}
|
|
if (!held) return false;
|
|
}
|
|
const char* comma = strchr(p, ',');
|
|
if (!comma) break;
|
|
p = comma + 1;
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
return true;
|
|
}
|
|
}
|