Finish switch to toml, fix hotkey saving
This commit is contained in:
parent
9613354939
commit
6dfadd3470
25447
depend/json.hpp
25447
depend/json.hpp
File diff suppressed because it is too large
Load Diff
102
depend/toml_addon.hpp
Normal file
102
depend/toml_addon.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "toml.hpp"
|
||||
|
||||
template <typename T, typename Path>
|
||||
auto build_from_path(T&& value, Path&& path_component)
|
||||
{
|
||||
using component_type = std::remove_cv_t<std::remove_reference_t<Path>>;
|
||||
static_assert(std::is_integral_v<component_type> || toml::is_key_or_convertible<Path&&>,
|
||||
"path components must be integers or strings");
|
||||
|
||||
// making an array
|
||||
if constexpr (std::is_integral_v<component_type>)
|
||||
{
|
||||
toml::array arr;
|
||||
const auto index = static_cast<std::size_t>(path_component);
|
||||
arr.reserve(index + 1u);
|
||||
|
||||
// backfill with integers
|
||||
while (arr.size() < index)
|
||||
arr.push_back(0);
|
||||
|
||||
// add the actual value
|
||||
arr.push_back(static_cast<T&&>(value));
|
||||
|
||||
return arr;
|
||||
}
|
||||
else // making a table
|
||||
{
|
||||
toml::table tbl;
|
||||
tbl.insert_or_assign(path_component, value);
|
||||
return tbl;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Path, typename... Paths>
|
||||
auto build_from_path(T&& value, Path&& path_component, Paths&&... path_components)
|
||||
{
|
||||
static_assert(sizeof...(Paths));
|
||||
|
||||
return build_from_path(build_from_path(value, path_components...), path_component);
|
||||
}
|
||||
|
||||
static void merge_left(toml::table& lhs, toml::table&& rhs);
|
||||
|
||||
static void merge_left(toml::array& lhs, toml::array&& rhs)
|
||||
{
|
||||
rhs.for_each(
|
||||
[&](std::size_t index, auto&& rhs_val)
|
||||
{
|
||||
// rhs index not found in lhs - direct move
|
||||
if (lhs.size() <= index)
|
||||
{
|
||||
lhs.push_back(std::move(rhs_val));
|
||||
return;
|
||||
}
|
||||
|
||||
// both elements were the same container type - recurse into them
|
||||
if constexpr (toml::is_container<decltype(rhs_val)>)
|
||||
{
|
||||
using rhs_type = std::remove_cv_t<std::remove_reference_t<decltype(rhs_val)>>;
|
||||
if (auto lhs_child = lhs[index].as<rhs_type>())
|
||||
{
|
||||
merge_left(*lhs_child, std::move(rhs_val));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// replace lhs element with rhs
|
||||
lhs.replace(lhs.cbegin() + index, std::move(rhs_val));
|
||||
});
|
||||
}
|
||||
|
||||
static void merge_left(toml::table& lhs, toml::table&& rhs)
|
||||
{
|
||||
rhs.for_each(
|
||||
[&](const toml::key& rhs_key, auto&& rhs_val)
|
||||
{
|
||||
auto lhs_it = lhs.lower_bound(rhs_key);
|
||||
|
||||
// rhs key not found in lhs - direct move
|
||||
if (lhs_it == lhs.cend() || lhs_it->first != rhs_key)
|
||||
{
|
||||
using rhs_type = std::remove_cv_t<std::remove_reference_t<decltype(rhs_val)>>;
|
||||
lhs.emplace_hint<rhs_type>(lhs_it, rhs_key, std::move(rhs_val));
|
||||
return;
|
||||
}
|
||||
|
||||
// both children were the same container type - recurse into them
|
||||
if constexpr (toml::is_container<decltype(rhs_val)>)
|
||||
{
|
||||
using rhs_type = std::remove_cv_t<std::remove_reference_t<decltype(rhs_val)>>;
|
||||
if (auto lhs_child = lhs_it->second.as<rhs_type>())
|
||||
{
|
||||
merge_left(*lhs_child, std::move(rhs_val));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// replace lhs value with rhs
|
||||
lhs.insert_or_assign(rhs_key, std::move(rhs_val));
|
||||
});
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# [Category Name]
|
||||
# location_name = "interior_id, position_x, position_y, position_z"
|
||||
#########
|
||||
##############################################################################
|
||||
|
||||
[Custom]
|
||||
|
||||
|
@ -1,90 +1,93 @@
|
||||
##############################################################################
|
||||
# Particle Names
|
||||
# Example: Category = ["name1", "name2"]
|
||||
# Example,
|
||||
# [Category Name]
|
||||
# particle_name = ""
|
||||
##############################################################################
|
||||
|
||||
Custom = []
|
||||
Main = [
|
||||
"prt_blood",
|
||||
"prt_boatsplash",
|
||||
"prt_bubbles",
|
||||
"prt_cardebris",
|
||||
"prt_collisionsmoke",
|
||||
"prt_glass",
|
||||
"prt_gunshell",
|
||||
"prt_sand",
|
||||
"prt_sand2",
|
||||
"prt_smokeII_3_expand",
|
||||
"prt_smoke_huge",
|
||||
"prt_spark",
|
||||
"prt_spark2",
|
||||
"prt_splash",
|
||||
"prt_wake",
|
||||
"prt_watersplash",
|
||||
"prt_wheeldirt",
|
||||
"boat_prop",
|
||||
"camflash",
|
||||
"exhale",
|
||||
"explosion_fuel_car",
|
||||
"explosion_large",
|
||||
"explosion_medium",
|
||||
"explosion_molotov",
|
||||
"explosion_small",
|
||||
"explosion_tiny",
|
||||
"extinguisher",
|
||||
"fire",
|
||||
"fire_bike",
|
||||
"fire_car",
|
||||
"fire_large",
|
||||
"fire_med",
|
||||
"flamethrower",
|
||||
"gunflash",
|
||||
"gunsmoke",
|
||||
"heli_dust",
|
||||
"jetpack",
|
||||
"jetthrust",
|
||||
"molotov_flame",
|
||||
"nitro",
|
||||
"overheat_car",
|
||||
"overheat_car_electric",
|
||||
"riot_smoke",
|
||||
"spraycan",
|
||||
"tank_fire",
|
||||
"teargas",
|
||||
"teargasAD",
|
||||
"water_hydrant",
|
||||
"water_ripples",
|
||||
"water_speed",
|
||||
"water_splash",
|
||||
"water_splsh_big",
|
||||
"water_splsh_sml",
|
||||
"water_swim",
|
||||
"cigarette_smoke",
|
||||
"flame",
|
||||
"insects",
|
||||
"smoke30lit",
|
||||
"smoke30m",
|
||||
"smoke50lit",
|
||||
"vent",
|
||||
"vent2",
|
||||
"waterfall_end",
|
||||
"water_fnt_tme",
|
||||
"water_fountain",
|
||||
"tree_hit_fir",
|
||||
"tree_hit_palm",
|
||||
"blood_heli",
|
||||
"carwashspray",
|
||||
"cement",
|
||||
"cloudfast",
|
||||
"coke_puff",
|
||||
"coke_trail",
|
||||
"explosion_barrel",
|
||||
"explosion_crate",
|
||||
"explosion_door",
|
||||
"petrolcan",
|
||||
"puke",
|
||||
"shootlight",
|
||||
"smoke_flare",
|
||||
"wallbust",
|
||||
"ws_factorysmoke"
|
||||
]
|
||||
[Custom]
|
||||
|
||||
[Main]
|
||||
prt_blood = ""
|
||||
prt_boatsplash = ""
|
||||
prt_bubbles = ""
|
||||
prt_cardebris = ""
|
||||
prt_collisionsmoke = ""
|
||||
prt_glass = ""
|
||||
prt_gunshell = ""
|
||||
prt_sand = ""
|
||||
prt_sand2 = ""
|
||||
prt_smokeII_3_expand = ""
|
||||
prt_smoke_huge = ""
|
||||
prt_spark = ""
|
||||
prt_spark2 = ""
|
||||
prt_splash = ""
|
||||
prt_wake = ""
|
||||
prt_watersplash = ""
|
||||
prt_wheeldirt = ""
|
||||
boat_prop = ""
|
||||
camflash = ""
|
||||
exhale = ""
|
||||
explosion_fuel_car = ""
|
||||
explosion_large = ""
|
||||
explosion_medium = ""
|
||||
explosion_molotov = ""
|
||||
explosion_small = ""
|
||||
explosion_tiny = ""
|
||||
extinguisher = ""
|
||||
fire = ""
|
||||
fire_bike = ""
|
||||
fire_car = ""
|
||||
fire_large = ""
|
||||
fire_med = ""
|
||||
flamethrower = ""
|
||||
gunflash = ""
|
||||
gunsmoke = ""
|
||||
heli_dust = ""
|
||||
jetpack = ""
|
||||
jetthrust = ""
|
||||
molotov_flame = ""
|
||||
nitro = ""
|
||||
overheat_car = ""
|
||||
overheat_car_electric = ""
|
||||
riot_smoke = ""
|
||||
spraycan = ""
|
||||
tank_fire = ""
|
||||
teargas = ""
|
||||
teargasAD = ""
|
||||
water_hydrant = ""
|
||||
water_ripples = ""
|
||||
water_speed = ""
|
||||
water_splash = ""
|
||||
water_splsh_big = ""
|
||||
water_splsh_sml = ""
|
||||
water_swim = ""
|
||||
cigarette_smoke = ""
|
||||
flame = ""
|
||||
insects = ""
|
||||
smoke30lit = ""
|
||||
smoke30m = ""
|
||||
smoke50lit = ""
|
||||
vent = ""
|
||||
vent2 = ""
|
||||
waterfall_end = ""
|
||||
water_fnt_tme = ""
|
||||
water_fountain = ""
|
||||
tree_hit_fir = ""
|
||||
tree_hit_palm = ""
|
||||
blood_heli = ""
|
||||
carwashspray = ""
|
||||
cement = ""
|
||||
cloudfast = ""
|
||||
coke_puff = ""
|
||||
coke_trail = ""
|
||||
explosion_barrel = ""
|
||||
explosion_crate = ""
|
||||
explosion_door = ""
|
||||
petrolcan = ""
|
||||
puke = ""
|
||||
shootlight = ""
|
||||
smoke_flare = ""
|
||||
wallbust = ""
|
||||
ws_factorysmoke = ""
|
||||
|
||||
|
@ -143,7 +143,7 @@
|
||||
"InvalidValue": "Invalid value",
|
||||
"Language": "Language",
|
||||
"LanguageChangeFailed": "Failed to change language!",
|
||||
"LatestVersion" : "Latest version",
|
||||
"LatestVersion" : "Latest version: ",
|
||||
"Location": "Location: %s",
|
||||
"Name": "Name",
|
||||
"NewVersion" : "A new version of the menu is available.",
|
||||
|
@ -53,8 +53,8 @@ void CheatMenu::DrawWindow()
|
||||
else
|
||||
m_fMenuSize = ImGui::GetWindowSize();
|
||||
|
||||
gConfig.SetValue("window.sizeX", m_fMenuSize.x);
|
||||
gConfig.SetValue("window.sizeY", m_fMenuSize.y);
|
||||
gConfig.Set("Window.SizeX", m_fMenuSize.x);
|
||||
gConfig.Set("Window.SizeY", m_fMenuSize.y);
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::End();
|
||||
@ -94,11 +94,11 @@ void CheatMenu::ProcessPages()
|
||||
* We don't want to be annoying and
|
||||
* show anniversary screen on every game start
|
||||
*/
|
||||
bool flag = gConfig.GetValue("window.anniversaryShown", false);
|
||||
bool flag = gConfig.Get("Window.AnniversaryShown", false);
|
||||
|
||||
if (!flag)
|
||||
{
|
||||
gConfig.SetValue("window.anniversaryShown", true);
|
||||
gConfig.Set("Window.AnniversaryShown", true);
|
||||
m_nMenuPage = eMenuPages::ANNIVERSARY;
|
||||
}
|
||||
}
|
||||
@ -137,7 +137,7 @@ void CheatMenu::ProcessPages()
|
||||
{
|
||||
m_nMenuPage = m_headerList[i].page;
|
||||
size_t curPage = static_cast<size_t>(m_headerList[i].page);
|
||||
gConfig.SetValue("window.page", curPage);
|
||||
gConfig.Set("Window.CurrentPage", curPage);
|
||||
pCallback = m_headerList[i].pFunc;
|
||||
Updater::ResetUpdaterState();
|
||||
}
|
||||
@ -210,9 +210,9 @@ void CheatMenu::Init()
|
||||
}
|
||||
|
||||
// Load menu settings
|
||||
m_nMenuPage = (eMenuPages)gConfig.GetValue("window.page", (size_t)eMenuPages::WELCOME);
|
||||
m_fMenuSize.x = gConfig.GetValue("window.sizeX", screen::GetScreenWidth() / 4.0f);
|
||||
m_fMenuSize.y = gConfig.GetValue("window.sizeY", screen::GetScreenHeight() / 1.2f);
|
||||
m_nMenuPage = (eMenuPages)gConfig.Get("Window.CurrentPage", (size_t)eMenuPages::WELCOME);
|
||||
m_fMenuSize.x = gConfig.Get("Window.SizeX", screen::GetScreenWidth() / 4.0f);
|
||||
m_fMenuSize.y = gConfig.Get("Window.SizeY", screen::GetScreenHeight() / 1.2f);
|
||||
srand(CTimer::m_snTimeInMilliseconds);
|
||||
|
||||
ApplyStyle();
|
||||
|
@ -1,10 +1,16 @@
|
||||
#include "datastore.h"
|
||||
#include "pch.h"
|
||||
|
||||
DataStore::DataStore(const char* fileName, bool isConfig) noexcept
|
||||
DataStore::DataStore(const char* fileName, bool isPathPredefined) noexcept
|
||||
{
|
||||
// Output config file in the same directory as the asi
|
||||
path = PLUGIN_PATH((char*)(isConfig ? "/" : "/CheatMenu/data/")) + std::string(fileName) + fileExt;
|
||||
if (isPathPredefined)
|
||||
{
|
||||
path = std::string(fileName) + fileExt;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = PLUGIN_PATH((char*)"/CheatMenu/data/") + std::string(fileName) + fileExt;
|
||||
}
|
||||
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
@ -21,7 +27,7 @@ DataStore::DataStore(const char* fileName, bool isConfig) noexcept
|
||||
{
|
||||
pTable = std::make_unique<toml::table>();
|
||||
|
||||
if (isConfig)
|
||||
if (fileName == FILE_NAME)
|
||||
{
|
||||
Log::Print<eLogLevel::Info>("Creating {}{}", fileName, fileExt);
|
||||
}
|
||||
@ -59,7 +65,7 @@ void DataStore::RemoveKey(const char* key, const char* entry) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
(*pTable)[key].as_table()->erase(entry);
|
||||
(*pTable).at_path(key).as_table()->erase(entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#define TOML_EXCEPTIONS 0
|
||||
#include "../depend/toml.hpp"
|
||||
#include "../depend/toml_addon.hpp"
|
||||
#include <memory>
|
||||
|
||||
|
||||
/*
|
||||
DataStore Class
|
||||
Stores & loads data from disk
|
||||
@ -18,44 +19,63 @@ private:
|
||||
public:
|
||||
typedef toml::table Table;
|
||||
|
||||
DataStore(const char* fileName, bool isConfig = false) noexcept;
|
||||
DataStore(const char* fileName, bool isPathPredefined = false) noexcept;
|
||||
|
||||
// Returns data from store structure
|
||||
std::string Get(const char* key, const char* defaultVal) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
return (*pTable).at_path(key).value_or(defaultVal);
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Get(const char* key, const T& defaultVal) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
return (*pTable)[key].value_or(defaultVal);
|
||||
return (*pTable).at_path(key).value_or(defaultVal);
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
|
||||
std::string Get(const char* key, std::string&& defaultVal) noexcept
|
||||
// Adds data to the structure
|
||||
template <typename T>
|
||||
void Set(const char* key, T&& value)
|
||||
{
|
||||
if (pTable)
|
||||
{
|
||||
return (*pTable)[key].value_or(defaultVal);
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
std::stringstream ss(key);
|
||||
std::vector<std::string> paths;
|
||||
|
||||
// Sets data in store structure
|
||||
template<typename T>
|
||||
void Set(const char* key, const T& value) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
while(ss.good())
|
||||
{
|
||||
(*pTable)[key] = value;
|
||||
std::string s1 = "";
|
||||
getline(ss, s1, '.');
|
||||
if (s1 != "")
|
||||
{
|
||||
paths.push_back(std::move(s1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Set(const char* key, std::string&& value) noexcept
|
||||
{
|
||||
if (pTable)
|
||||
// assign the value
|
||||
toml::table tbl;
|
||||
int startIndex = paths.size()-1;
|
||||
for (int i = startIndex; i >= 0; --i)
|
||||
{
|
||||
(*pTable)[key].ref<std::string>() = value;
|
||||
if (i == startIndex)
|
||||
{
|
||||
tbl.insert_or_assign(paths[i], std::move(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
toml::table temp;
|
||||
temp.insert_or_assign(paths[i], std::move(tbl));
|
||||
tbl = std::move(temp);
|
||||
}
|
||||
}
|
||||
merge_left(*pTable, std::move(tbl));
|
||||
}
|
||||
|
||||
// If store contains element
|
||||
|
@ -9,7 +9,7 @@
|
||||
#define MENU_NAME "Cheat Menu"
|
||||
#define MENU_VERSION_NUMBER "3.3"
|
||||
#define MENU_VERSION MENU_VERSION_NUMBER"-beta"
|
||||
#define BUILD_NUMBER "20220612"
|
||||
#define BUILD_NUMBER "20220616"
|
||||
#define MENU_TITLE MENU_NAME " v" MENU_VERSION
|
||||
|
||||
#ifdef GTASA
|
||||
|
@ -69,18 +69,18 @@ void MenuThread(void* param)
|
||||
}
|
||||
#endif
|
||||
|
||||
Log::Print<eLogLevel::None>("\nVersion: " MENU_TITLE "\nAuthor: Grinch_\nDiscord: " DISCORD_INVITE "\nMore Info: " GITHUB_LINK "\n");
|
||||
Log::Print<eLogLevel::None>("Version: " MENU_TITLE "\nAuthor: Grinch_\nDiscord: " DISCORD_INVITE "\nMore Info: " GITHUB_LINK "\n");
|
||||
|
||||
CheatMenu::Init();
|
||||
|
||||
// Checking for updates once a day
|
||||
SYSTEMTIME st;
|
||||
GetSystemTime(&st);
|
||||
if (gConfig.GetValue("config.update_date", 0) != st.wDay)
|
||||
if (gConfig.Get("config.update_date", 0) != st.wDay)
|
||||
{
|
||||
Updater::CheckUpdate();
|
||||
Updater::IncrementDailyUsageCounter();
|
||||
gConfig.SetValue("config.update_date", st.wDay);
|
||||
gConfig.Set("config.update_date", st.wDay);
|
||||
}
|
||||
|
||||
while (true)
|
||||
|
@ -4,7 +4,7 @@
|
||||
class Game
|
||||
{
|
||||
private:
|
||||
static inline ResourceStore m_MissionData{ "mission", eResourceType::TYPE_TEXT };
|
||||
static inline ResourceStore m_MissionData{ "missions", eResourceType::TYPE_TEXT };
|
||||
static inline bool m_bDisableCheats;
|
||||
static inline bool m_bDisableReplay;
|
||||
static inline bool m_bMissionTimer;
|
||||
|
@ -179,8 +179,8 @@ Hotkey::Hotkey(int key1, int key2, const std::string& configPath)
|
||||
{
|
||||
if (m_ConfigPath != "")
|
||||
{
|
||||
m_key1 = gConfig.GetValue(m_ConfigPath + ".1", m_key1);
|
||||
m_key2 = gConfig.GetValue(m_ConfigPath + ".2", m_key2);
|
||||
m_key1 = gConfig.Get((m_ConfigPath + ".Key1").c_str(), m_key1);
|
||||
m_key2 = gConfig.Get((m_ConfigPath + ".Key2").c_str(), m_key2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,8 +232,8 @@ bool Hotkey::DrawUI(const char* label)
|
||||
// Save the hotkeys in config file
|
||||
if (m_ConfigPath != "")
|
||||
{
|
||||
gConfig.SetValue(m_ConfigPath + "1", m_key1);
|
||||
gConfig.SetValue(m_ConfigPath + "2", m_key2);
|
||||
gConfig.Set((m_ConfigPath + ".Key1").c_str(), m_key1);
|
||||
gConfig.Set((m_ConfigPath + ".Key2").c_str(), m_key2);
|
||||
}
|
||||
}
|
||||
|
||||
|
54
src/json.cpp
54
src/json.cpp
@ -1,54 +0,0 @@
|
||||
#include "json.h"
|
||||
#include "pch.h"
|
||||
|
||||
CJson::CJson(const char* name, bool pathPredefined)
|
||||
{
|
||||
if (name == "" || !std::filesystem::is_directory(PLUGIN_PATH((char*)"CheatMenu")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathPredefined)
|
||||
{
|
||||
m_FilePath = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FilePath = PLUGIN_PATH((char*)"/CheatMenu/json/") + std::string(name) + ".json";
|
||||
}
|
||||
|
||||
if (std::filesystem::exists(m_FilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream file(m_FilePath);
|
||||
file >> m_Data;
|
||||
file.close();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
//gLog << "Error trying to read " << m_FilePath << std::endl;
|
||||
m_Data = "{}"_json;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Data = "{}"_json;
|
||||
|
||||
if (m_FilePath.find("config"))
|
||||
{
|
||||
//gLog << "Creating config.json file" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
//gLog << "Failed to locate file " << m_FilePath << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CJson::Save()
|
||||
{
|
||||
std::ofstream file(m_FilePath);
|
||||
file << m_Data.dump(4, ' ', false, nlohmann::json::error_handler_t::replace) << std::endl;
|
||||
file.close();
|
||||
}
|
122
src/json.h
122
src/json.h
@ -1,122 +0,0 @@
|
||||
#pragma once
|
||||
#include "../depend/json.hpp"
|
||||
|
||||
/*
|
||||
Wrapper class for nlohmann::json
|
||||
Contains helper methods
|
||||
*/
|
||||
class CJson
|
||||
{
|
||||
private:
|
||||
std::string m_FilePath;
|
||||
|
||||
public:
|
||||
nlohmann::json m_Data;
|
||||
|
||||
/*
|
||||
Returns a value from json structure hierarchy using '.'
|
||||
Example: "Menu.Window.X"
|
||||
*/
|
||||
// specialize since typeid(std::string) doesn't work
|
||||
|
||||
template <typename T>
|
||||
T inline GetValue(std::string&& key, const T& defaultVal)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::stringstream ss(key);
|
||||
std::string line;
|
||||
|
||||
nlohmann::json* json = &m_Data;
|
||||
|
||||
while (getline(ss, line, '.'))
|
||||
{
|
||||
json = &((*json)[line]);
|
||||
}
|
||||
|
||||
// json library bugs with bool, using int instead
|
||||
if (typeid(T) == typeid(bool))
|
||||
{
|
||||
return ((json->get<int>() == 1) ? true : false);
|
||||
}
|
||||
return json->get<T>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
std::string inline GetValueStr(const std::string& key, const std::string& defaultVal)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::stringstream ss(key);
|
||||
std::string line;
|
||||
|
||||
nlohmann::json* json = &m_Data;
|
||||
|
||||
while (getline(ss, line, '.'))
|
||||
{
|
||||
json = &((*json)[line]);
|
||||
}
|
||||
|
||||
return json->get<std::string>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Allows to save values in json hierarchy using '.'
|
||||
Example: "Menu.Window.X"
|
||||
*/
|
||||
template <typename T>
|
||||
void inline SetValue(std::string&& key, const T& val)
|
||||
{
|
||||
std::stringstream ss(key);
|
||||
std::string line;
|
||||
|
||||
nlohmann::json* json = &m_Data;
|
||||
|
||||
while (getline(ss, line, '.'))
|
||||
{
|
||||
json = &((*json)[line]);
|
||||
|
||||
}
|
||||
|
||||
// json library bugs with bool, using int instead
|
||||
if (typeid(T) == typeid(bool))
|
||||
{
|
||||
*json = (val ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
*json = val;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void inline SetValue(std::string&& key, const std::string& val)
|
||||
{
|
||||
std::stringstream ss(key);
|
||||
std::string line;
|
||||
|
||||
nlohmann::json* json = &m_Data;
|
||||
|
||||
while (getline(ss, line, '.'))
|
||||
{
|
||||
json = &((*json)[line]);
|
||||
}
|
||||
|
||||
*json = val;
|
||||
}
|
||||
|
||||
/*
|
||||
Saves json data to disk
|
||||
*/
|
||||
void Save();
|
||||
CJson(const char* text, bool pathPredefined = false);
|
||||
};
|
@ -34,7 +34,7 @@ Locale::eReturnCodes Locale::Init(const char* path, const char* def, const char*
|
||||
#endif
|
||||
for (auto& entry : std::filesystem::directory_iterator(m_path))
|
||||
{
|
||||
if (entry.path().extension() == ".json")
|
||||
if (entry.path().extension() == ".toml")
|
||||
{
|
||||
std::string fileName = entry.path().stem().string();
|
||||
#ifdef _GTA_
|
||||
@ -44,14 +44,14 @@ Locale::eReturnCodes Locale::Init(const char* path, const char* def, const char*
|
||||
|
||||
if (!strcmp(fallback, fileName.c_str()))
|
||||
{
|
||||
std::string localePath = m_path + fileName + ".json";
|
||||
std::string localePath = m_path + fileName;
|
||||
|
||||
if(m_pCallbackJson)
|
||||
if(m_pCallbackData)
|
||||
{
|
||||
delete m_pCallbackJson;
|
||||
m_pCallbackJson = nullptr;
|
||||
delete m_pCallbackData;
|
||||
m_pCallbackData = nullptr;
|
||||
}
|
||||
m_pCallbackJson = new CJson(localePath.c_str(), true);
|
||||
m_pCallbackData = new DataStore(localePath.c_str(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,12 +113,32 @@ Locale::eReturnCodes Locale::SetLocale(size_t index)
|
||||
{
|
||||
return eReturnCodes::INVALID_INDEX;
|
||||
}
|
||||
|
||||
std::string localeFile = m_locales[index];
|
||||
localeFile += ".json";
|
||||
std::string localePath = m_path + localeFile;
|
||||
m_pData = new CJson(localePath.c_str(), true);
|
||||
std::string localePath = m_path + m_locales[index];
|
||||
m_pData = new DataStore(localePath.c_str(), true);
|
||||
localeIndex = index;
|
||||
return eReturnCodes::SUCCESS;
|
||||
}
|
||||
|
||||
std::string Locale::GetText(std::string&& key, std::string&& defaultValue)
|
||||
{
|
||||
if (m_pData == nullptr)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Return keyname if no default value is provided
|
||||
if (defaultValue == "")
|
||||
{
|
||||
defaultValue = "#" + key;
|
||||
}
|
||||
|
||||
std::string rtn = m_pData->Get(key.c_str(), defaultValue);
|
||||
|
||||
if (rtn == defaultValue)
|
||||
{
|
||||
return m_pCallbackData->Get(key.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
31
src/locale.h
31
src/locale.h
@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "json.h"
|
||||
#include "datastore.h"
|
||||
|
||||
/*
|
||||
A custom i18n library
|
||||
Loads strings from a json file
|
||||
Requires the CJson class
|
||||
Requires the DataStore class
|
||||
*/
|
||||
class Locale
|
||||
{
|
||||
private:
|
||||
static inline std::vector<std::string> m_locales;
|
||||
static inline std::string m_path;
|
||||
static inline CJson *m_pData = nullptr;
|
||||
static inline CJson *m_pCallbackJson = nullptr;
|
||||
static inline DataStore *m_pData = nullptr;
|
||||
static inline DataStore *m_pCallbackData = nullptr;
|
||||
static inline size_t localeIndex;
|
||||
|
||||
public:
|
||||
@ -49,28 +49,7 @@ public:
|
||||
You need to call SetLanguage once before calling this function
|
||||
By default, the language is set to "en"
|
||||
*/
|
||||
static inline std::string GetText(std::string&& key, std::string&& defaultValue = "")
|
||||
{
|
||||
if (m_pData == nullptr)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Return keyname if no default value is provided
|
||||
if (defaultValue == "")
|
||||
{
|
||||
defaultValue = "#" + key;
|
||||
}
|
||||
|
||||
std::string rtn = m_pData->GetValueStr(key, defaultValue);
|
||||
|
||||
if (rtn == defaultValue)
|
||||
{
|
||||
return m_pCallbackJson->GetValueStr(key, defaultValue);
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
static std::string GetText(std::string&& key, std::string&& defaultValue = "");
|
||||
|
||||
/*
|
||||
Sets the language to use
|
||||
|
72
src/menu.cpp
72
src/menu.cpp
@ -16,22 +16,22 @@ void Menu::Init()
|
||||
{
|
||||
// TODO: use structs
|
||||
// Load config data
|
||||
m_Overlay::bCoord = gConfig.GetValue("overlay.coord", false);
|
||||
m_Overlay::bCpuUsage = gConfig.GetValue("overlay.cpu_usage", false);
|
||||
m_Overlay::bFPS = gConfig.GetValue("overlay.fps", false);
|
||||
m_Overlay::bLocName = gConfig.GetValue("overlay.loc_name", false);
|
||||
m_Overlay::bTransparent = gConfig.GetValue("overlay.transparent", false);
|
||||
m_Overlay::bMemUsage = gConfig.GetValue("overlay.mem_usage", false);
|
||||
m_Overlay::bVehHealth = gConfig.GetValue("overlay.veh_health", false);
|
||||
m_Overlay::bVehSpeed = gConfig.GetValue("overlay.veh_speed", false);
|
||||
m_Overlay::mSelectedPos = (DisplayPos)gConfig.GetValue("overlay.selected_pos", (int)DisplayPos::BOTTOM_RIGHT);
|
||||
m_Overlay::fPosX = gConfig.GetValue("overlay.pox", 0);
|
||||
m_Overlay::fPosY = gConfig.GetValue("overlay.posy", 0);
|
||||
m_Overlay::textColor[0] = gConfig.GetValue("overlay.text_color.r", 1.0f);
|
||||
m_Overlay::textColor[1] = gConfig.GetValue("overlay.text_color.g", 1.0f);
|
||||
m_Overlay::textColor[2] = gConfig.GetValue("overlay.text_color.b", 1.0f);
|
||||
m_Overlay::textColor[3] = gConfig.GetValue("overlay.text_color.a", 1.0f);
|
||||
m_bDiscordRPC = gConfig.GetValue("menu.discord_rpc", false);
|
||||
m_Overlay::bCoord = gConfig.Get("Overlay.ShowCoordinates", false);
|
||||
m_Overlay::bCpuUsage = gConfig.Get("Overlay.ShowCPUUsage", false);
|
||||
m_Overlay::bFPS = gConfig.Get("Overlay.ShowFPS", false);
|
||||
m_Overlay::bLocName = gConfig.Get("Overlay.ShowLocationName", false);
|
||||
m_Overlay::bTransparent = gConfig.Get("Overlay.Transparent", false);
|
||||
m_Overlay::bMemUsage = gConfig.Get("Overlay.ShowMemoryUsage", false);
|
||||
m_Overlay::bVehHealth = gConfig.Get("Overlay.ShowVehicleName", false);
|
||||
m_Overlay::bVehSpeed = gConfig.Get("Overlay.ShowVehicleSpeed", false);
|
||||
m_Overlay::mSelectedPos = (DisplayPos)gConfig.Get("Overlay.SelectedPosition", (int)DisplayPos::BOTTOM_RIGHT);
|
||||
m_Overlay::fPosX = gConfig.Get("Overlay.PosX", 0);
|
||||
m_Overlay::fPosY = gConfig.Get("Overlay.PosY", 0);
|
||||
m_Overlay::textColor[0] = gConfig.Get("Overlay.TextColor.Red", 1.0f);
|
||||
m_Overlay::textColor[1] = gConfig.Get("Overlay.TextColor.Green", 1.0f);
|
||||
m_Overlay::textColor[2] = gConfig.Get("Overlay.TextColor.Blue", 1.0f);
|
||||
m_Overlay::textColor[3] = gConfig.Get("Overlay.TextColor.Alpha", 1.0f);
|
||||
m_bDiscordRPC = gConfig.Get("Menu.DiscordRPC", false);
|
||||
|
||||
Util::GetCPUUsageInit();
|
||||
MEMORYSTATUSEX memInfo;
|
||||
@ -63,8 +63,8 @@ void Menu::DrawOverlay()
|
||||
{
|
||||
if (m_Overlay::fPosX != NULL && m_Overlay::fPosY != NULL)
|
||||
{
|
||||
gConfig.SetValue("overlay.posx", m_Overlay::fPosX);
|
||||
gConfig.SetValue("overlay.posy", m_Overlay::fPosY);
|
||||
gConfig.Set("Overlay.PosX", m_Overlay::fPosX);
|
||||
gConfig.Set("Overlay.PosY", m_Overlay::fPosY);
|
||||
ImGui::SetNextWindowPos(ImVec2(m_Overlay::fPosX, m_Overlay::fPosY), ImGuiCond_Once);
|
||||
}
|
||||
}
|
||||
@ -323,13 +323,7 @@ void Menu::ShowPage()
|
||||
if (ImGui::BeginTabItem(TEXT("Menu.Config")))
|
||||
{
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button(TEXT("Menu.ResetConfig"), ImVec2(Ui::GetSize(2))))
|
||||
{
|
||||
gConfig.m_Data.clear();
|
||||
SetHelpMessage(TEXT("Menu.ResetConfigMSG"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(TEXT("Menu.ResetSize"), ImVec2(Ui::GetSize(2))))
|
||||
if (ImGui::Button(TEXT("Menu.ResetSize"), ImVec2(Ui::GetSize(1))))
|
||||
{
|
||||
CheatMenu::ResetMenuSize();
|
||||
}
|
||||
@ -366,7 +360,7 @@ void Menu::ShowPage()
|
||||
{
|
||||
RPC::Shutdown();
|
||||
}
|
||||
gConfig.SetValue("menu.discord_rpc", m_bDiscordRPC);
|
||||
gConfig.Set("Menu.DiscordRPC", m_bDiscordRPC);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
ImGui::Columns(1);
|
||||
@ -380,17 +374,17 @@ void Menu::ShowPage()
|
||||
ImGui::SameLine();
|
||||
if (Ui::ListBox(TEXT("Menu.Position"), m_Overlay::posNames, (int&)m_Overlay::mSelectedPos))
|
||||
{
|
||||
gConfig.SetValue("overlay.selected_pos", m_Overlay::mSelectedPos);
|
||||
gConfig.Set<int>("Overlay.SelectedPosition", static_cast<int>(m_Overlay::mSelectedPos));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ColorEdit4(TEXT("Menu.TextColor"), m_Overlay::textColor))
|
||||
{
|
||||
gConfig.SetValue("overlay.text_color.r", m_Overlay::textColor[0]);
|
||||
gConfig.SetValue("overlay.text_color.g", m_Overlay::textColor[1]);
|
||||
gConfig.SetValue("overlay.text_color.b", m_Overlay::textColor[2]);
|
||||
gConfig.SetValue("overlay.text_color.a", m_Overlay::textColor[3]);
|
||||
gConfig.Set("Overlay.TextColor.Red", m_Overlay::textColor[0]);
|
||||
gConfig.Set("Overlay.TextColor.Green", m_Overlay::textColor[1]);
|
||||
gConfig.Set("Overlay.TextColor.Blue", m_Overlay::textColor[2]);
|
||||
gConfig.Set("Overlay.TextColor.Alpha", m_Overlay::textColor[3]);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
@ -398,44 +392,44 @@ void Menu::ShowPage()
|
||||
ImGui::Columns(2, nullptr, false);
|
||||
if (ImGui::Checkbox(TEXT("Menu.NoBG"), &m_Overlay::bTransparent))
|
||||
{
|
||||
gConfig.SetValue("overlay.transparent", m_Overlay::bTransparent);
|
||||
gConfig.Set("Overlay.Transparent", m_Overlay::bTransparent);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowCoords"), &m_Overlay::bCoord))
|
||||
{
|
||||
gConfig.SetValue("overlay.coord", m_Overlay::bCoord);
|
||||
gConfig.Set("Overlay.ShowCoordinates", m_Overlay::bCoord);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowCPU"), &m_Overlay::bCpuUsage))
|
||||
{
|
||||
gConfig.SetValue("overlay.cpu_usage", m_Overlay::bCpuUsage);
|
||||
gConfig.Set("Overlay.ShowCPUUsage", m_Overlay::bCpuUsage);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowFPS"), &m_Overlay::bFPS))
|
||||
{
|
||||
gConfig.SetValue("overlay.fps", m_Overlay::bFPS);
|
||||
gConfig.Set("Overlay.ShowFPS", m_Overlay::bFPS);
|
||||
}
|
||||
|
||||
ImGui::NextColumn();
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowLocation"), &m_Overlay::bLocName))
|
||||
{
|
||||
gConfig.SetValue("overlay.loc_name", m_Overlay::bLocName);
|
||||
gConfig.Set("Overlay.ShowLocationName", m_Overlay::bLocName);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowRAM"), &m_Overlay::bMemUsage))
|
||||
{
|
||||
gConfig.SetValue("overlay.mem_usage", m_Overlay::bMemUsage);
|
||||
gConfig.Set("Overlay.ShowMemoryUsage", m_Overlay::bMemUsage);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowVehHealth"), &m_Overlay::bVehHealth))
|
||||
{
|
||||
gConfig.SetValue("overlay.veh_health", m_Overlay::bVehHealth);
|
||||
gConfig.Set("Overlay.ShowVehicleHealth", m_Overlay::bVehHealth);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(TEXT("Menu.ShowVehSpeed"), &m_Overlay::bVehSpeed))
|
||||
{
|
||||
gConfig.SetValue("overlay.veh_speed", m_Overlay::bVehSpeed);
|
||||
gConfig.Set("Overlay.ShowVehicleSpeed", m_Overlay::bVehSpeed);
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "pch.h"
|
||||
eRenderer gRenderer = Render_Unknown;
|
||||
CJson gConfig = CJson("config");
|
||||
DataStore gConfig = DataStore(FILE_NAME, true);
|
||||
|
||||
Hotkey aimSkinChanger {VK_RETURN, VK_RETURN, "AimSkinChanger"};
|
||||
Hotkey freeCam {VK_F6, VK_F6, "Freecam.Toggle"};
|
||||
|
@ -57,7 +57,6 @@
|
||||
#include "defines.h"
|
||||
#include "log.h"
|
||||
#include "datastore.h"
|
||||
#include "json.h"
|
||||
#include "hotkeys.h"
|
||||
#include "resourcestore.h"
|
||||
#include "fontmgr.h"
|
||||
@ -76,7 +75,7 @@ enum eRenderer
|
||||
};
|
||||
|
||||
extern eRenderer gRenderer;
|
||||
extern CJson gConfig;
|
||||
extern DataStore gConfig;
|
||||
|
||||
// Fix function clashes
|
||||
static void SetHelpMessage(const char *message, bool b1 = false, bool b2 = false, bool b3 = false)
|
||||
|
@ -52,9 +52,9 @@ private:
|
||||
public:
|
||||
#ifdef GTASA
|
||||
static inline DataStore m_SpecialPedData {"special_peds"};
|
||||
static inline ResourceStore m_PedData{"ped", eResourceType::TYPE_BOTH, ImVec2(65, 110)};
|
||||
static inline ResourceStore m_PedData{"peds", eResourceType::TYPE_BOTH, ImVec2(65, 110)};
|
||||
#else
|
||||
static inline ResourceStore m_PedData {"ped", eResourceType::TYPE_TEXT};
|
||||
static inline ResourceStore m_PedData {"peds", eResourceType::TYPE_TEXT};
|
||||
#endif
|
||||
|
||||
Ped() = delete;
|
||||
|
@ -88,7 +88,7 @@ void Player::Init()
|
||||
#ifdef GTASA
|
||||
// Fix player model being broken after rebuild
|
||||
patch::RedirectCall(0x5A834D, &PlayerModelBrokenFix);
|
||||
m_bAimSkinChanger = gConfig.GetValue("aim_skin_changer", false);
|
||||
m_bAimSkinChanger = gConfig.Get("aim_skin_changer", false);
|
||||
#endif
|
||||
|
||||
// Custom skins setup
|
||||
@ -477,7 +477,7 @@ void Player::ShowPage()
|
||||
Ui::CheckboxAddress(TEXT("Player.InfO2"), 0x96916E);
|
||||
if (Ui::CheckboxBitFlag(TEXT("Player.InvisPlayer"), pPlayer->m_nPedFlags.bDontRender))
|
||||
{
|
||||
pPlayer->m_nPedFlags.bDontRender != pPlayer->m_nPedFlags.bDontRender;
|
||||
pPlayer->m_nPedFlags.bDontRender = !pPlayer->m_nPedFlags.bDontRender;
|
||||
}
|
||||
Ui::CheckboxAddress(TEXT("Player.InfSprint"), 0xB7CEE4);
|
||||
#else
|
||||
@ -489,7 +489,7 @@ void Player::ShowPage()
|
||||
#ifdef GTASA
|
||||
if (Ui::CheckboxBitFlag(TEXT("Player.LockControl"), pad->bPlayerSafe))
|
||||
{
|
||||
pad->bPlayerSafe != pad->bPlayerSafe;
|
||||
pad->bPlayerSafe = !pad->bPlayerSafe;
|
||||
}
|
||||
Ui::CheckboxAddressEx(TEXT("Player.MaxAppeal"), 0x969180, 1, 0);
|
||||
Ui::CheckboxAddress(TEXT("Player.MegaJump"), 0x96916C);
|
||||
@ -766,7 +766,7 @@ void Player::ShowPage()
|
||||
|
||||
if (Ui::CheckboxWithHint(TEXT("Player.AimSkinChanger"), &m_bAimSkinChanger, TEXT("Player.AimSkinChangerTip") + aimSkinChanger.Pressed()))
|
||||
{
|
||||
gConfig.SetValue("aim_skin_changer", m_bAimSkinChanger);
|
||||
gConfig.Set("aim_skin_changer", m_bAimSkinChanger);
|
||||
}
|
||||
if (ImGui::BeginTabBar("AppearanceTabBar"))
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ void Teleport::FetchRadarSpriteData()
|
||||
|
||||
void Teleport::Init()
|
||||
{
|
||||
m_bQuickTeleport = gConfig.GetValue("quick_teleport", false);
|
||||
m_bQuickTeleport = gConfig.Get("quick_teleport", false);
|
||||
|
||||
Events::processScriptsEvent += []
|
||||
{
|
||||
@ -219,7 +219,7 @@ void Teleport::ShowPage()
|
||||
std::string(TEXT_S("Teleport.QuickTeleportHint")
|
||||
+ quickTeleport.GetNameString()).c_str()))
|
||||
{
|
||||
gConfig.SetValue("quick_teleport", m_bQuickTeleport);
|
||||
gConfig.Set("quick_teleport", m_bQuickTeleport);
|
||||
}
|
||||
#endif
|
||||
ImGui::Columns(1);
|
||||
|
@ -37,7 +37,7 @@ void Updater::Process()
|
||||
}
|
||||
|
||||
const char* link = "https://api.github.com/repos/user-grinch/Cheat-Menu/tags";
|
||||
char* path = PLUGIN_PATH((char*)"CheatMenu/json/versioninfo.json");
|
||||
char* path = PLUGIN_PATH((char*)"CheatMenu/data/versioninfo.json");
|
||||
HRESULT res = URLDownloadToFile(NULL, link, path, 0, NULL);
|
||||
|
||||
if (res == E_OUTOFMEMORY || res == INET_E_DOWNLOAD_FAILURE)
|
||||
@ -46,17 +46,26 @@ void Updater::Process()
|
||||
return;
|
||||
}
|
||||
|
||||
CJson verinfo = CJson("versioninfo");
|
||||
|
||||
// fetch the version number
|
||||
if (verinfo.m_Data.empty())
|
||||
// Extract the version number
|
||||
FILE *pFile= fopen(path, "r");
|
||||
if (pFile != NULL)
|
||||
{
|
||||
latestVer = MENU_VERSION_NUMBER;
|
||||
}
|
||||
else
|
||||
{
|
||||
latestVer = verinfo.m_Data.items().begin().value()["name"].get<std::string>();
|
||||
char buf[64];
|
||||
float version = 0.0f;
|
||||
while (fgets(buf, 64, pFile) != NULL)
|
||||
{
|
||||
sscanf(buf, "[{\"name\": \"%f\",", &version);
|
||||
if (version != 0.0f)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(2) << version;
|
||||
latestVer = ss.str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
remove(path);
|
||||
|
||||
if (latestVer > MENU_VERSION_NUMBER)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user