From 74a6c1d99dd1610c31b5ea2eaceafb438df5196f Mon Sep 17 00:00:00 2001 From: fangshun Date: Mon, 3 Aug 2026 23:04:36 +0800 Subject: [PATCH 1/6] =?UTF-8?q?1.Fix=20int64/uint64=20data=20overflow=20wh?= =?UTF-8?q?en=20writing=E3=80=82=202.Add=20packed=20int/bool=20stats=20for?= =?UTF-8?q?=20all=20writes=20and=20reads=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/submenus/Recovery/StatEditor.cpp | 55 +++++++++++++----- src/game/gta/Stats.cpp | 58 ++++++++++++++++++- src/game/gta/Stats.hpp | 11 +++- src/types/stats/sStatData.hpp | 9 ++- 4 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index 530ce54a9..ecb024537 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -1,8 +1,10 @@ -#include "StatEditor.hpp" -#include "core/backend/FiberPool.hpp" +#include "core/backend/FiberPool.hpp" +#include "core/frontend/widgets/imgui_bitfield.hpp" #include "game/backend/AnticheatBypass.hpp" -#include "game/pointers/Pointers.hpp" #include "game/gta/Natives.hpp" +#include "game/gta/Stats.hpp" +#include "game/pointers/Pointers.hpp" +#include "StatEditor.hpp" #include "types/stats/CStatsMgr.hpp" namespace YimMenu::Submenus @@ -10,11 +12,11 @@ namespace YimMenu::Submenus struct StatInfo { std::string m_Name; - std::uint32_t m_NameHash; + std::uint32_t m_NameHash = 0; bool m_Normalized = false; sStatData* m_Data = nullptr; - bool IsValid() + bool IsValid() const { return m_Data != nullptr; } @@ -26,7 +28,7 @@ namespace YimMenu::Submenus bool m_IsBoolStat; bool m_IsValid; - bool IsValid() + bool IsValid() const { return m_IsValid; } @@ -128,6 +130,7 @@ namespace YimMenu::Submenus value.m_AsU64 = data->GetInt64(); return; case sStatData::Type::UINT64: + case sStatData::Type::PACKED: value.m_AsU64 = data->GetUInt64(); return; case sStatData::Type::STRING: @@ -154,16 +157,30 @@ namespace YimMenu::Submenus case sStatData::Type::UINT8: STATS::STAT_SET_INT(hash, value.m_AsInt, true); return; - case sStatData::Type::INT64: - data->SetInt64(value.m_AsU64); // TODO this isn't a good idea! natives can't set this + case sStatData::Type::INT64: + data->SetInt64(value.m_AsU64 - 1); + STATS::STAT_INCREMENT(hash, static_cast(1)); return; case sStatData::Type::UINT64: - STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)value.m_AsU64, 0, 32, true); - STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)(value.m_AsU64 >> 32), 32, 32, true); + //Stats::SetMaskedAll(hash, value.m_AsU64); + //This code is simpler + //After writing, restarting will restore the data + data->SetUInt64(value.m_AsU64 - 1); + //You need to use STATS::STAT_INCREMENT to save the data on the server. + STATS::STAT_INCREMENT(hash, static_cast(1)); return; case sStatData::Type::STRING: STATS::STAT_SET_STRING(hash, value.m_AsString, true); return; + case sStatData::Type::PACKED: + /*data->SetUInt64(value.m_AsU64 - 1); + Packed data can't be written using STATS::STAT_INCREMENT + STATS::STAT_INCREMENT(hash, static_cast(1));*/ + Stats::SetMaskedAll(hash, value.m_AsU64); + return; + case sStatData::Type::POS: + case sStatData::Type::DATE: + case sStatData::Type::PROFILE_SETTING: default: return; // data type not supported } @@ -208,20 +225,24 @@ namespace YimMenu::Submenus case sStatData::Type::INT64: { auto int64_ = std::strtoll(value.data(), nullptr, 10); - data->SetInt64(int64_); // TODO this isn't a good idea! natives can't set this + data->SetInt64(int64_-1); + STATS::STAT_INCREMENT(hash, static_cast(1)); return; } case sStatData::Type::UINT64: { auto uint64_ = std::strtoull(value.data(), nullptr, 10); - - STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)uint64_, 0, 32, true); - STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)(uint64_ >> 32), 32, 32, true); + data->SetUInt64(uint64_ - 1); + STATS::STAT_INCREMENT(hash, static_cast(1)); return; } case sStatData::Type::STRING: STATS::STAT_SET_STRING(hash, value.data(), true); return; + case sStatData::Type::PACKED: + auto uint64_ = std::strtoull(value.data(), nullptr, 10); + Stats::SetMaskedAll(hash, uint64_); + return; default: return; // data type not supported } @@ -245,11 +266,13 @@ namespace YimMenu::Submenus case sStatData::Type::UINT8: return ImGui::InputScalar("Value", ImGuiDataType_U8, &value.m_AsInt); case sStatData::Type::INT64: - return ImGui::InputScalar("Value", ImGuiDataType_S64, &value.m_AsInt); + return ImGui::InputScalar("Value", ImGuiDataType_S64, &value.m_AsU64); case sStatData::Type::UINT64: - return ImGui::InputScalar("Value", ImGuiDataType_U64, &value.m_AsInt); + return ImGui::InputScalar("Value", ImGuiDataType_U64, &value.m_AsU64); case sStatData::Type::STRING: return ImGui::InputText("Value", value.m_AsString, sizeof(value.m_AsString)); + case sStatData::Type::PACKED: + return ImGui::Bitfield("Value", &value.m_AsU64); default: ImGui::BeginDisabled(); ImGui::Text("Data type not supported"); diff --git a/src/game/gta/Stats.cpp b/src/game/gta/Stats.cpp index 51d3bcc7f..8189faa3d 100644 --- a/src/game/gta/Stats.cpp +++ b/src/game/gta/Stats.cpp @@ -1,4 +1,4 @@ -#include "Stats.hpp" +#include "Stats.hpp" #include "game/gta/Natives.hpp" namespace YimMenu::Stats @@ -132,4 +132,60 @@ namespace YimMenu::Stats STATS::STAT_GET_MASKED_INT(Joaat(statName), &value, bitIndex, bitSize, -1); return value; } + + + void Stats::SetMaskedAll(Hash hash, uint64_t value) + { + uint64_t uint64_value = value; + int part0 = uint64_value & 0xFFFFu; + int part1 = (uint64_value >> 16) & 0xFFFFu; + int part2 = (uint64_value >> 32) & 0xFFFFu; + int part3 = (uint64_value >> 48) & 0xFFFFu; + // The second input parameter is of type int. Using -1 will cause the entire data to overflow + //so you have to split the 64-bit data into 4 parts to write it. + STATS::STAT_SET_MASKED_INT(hash, part0, 0, 16, true); //bit0-bit15 + STATS::STAT_SET_MASKED_INT(hash, part1, 16, 16, true); //bit16-bit31 + STATS::STAT_SET_MASKED_INT(hash, part2, 32, 16, true); //bit32-bit47 + STATS::STAT_SET_MASKED_INT(hash, part3, 48, 16, true); //bit48-bit63 + } + + void Stats::SetMaskedAll(std::string statName, uint64_t value) + { + ConvertMPX(statName); + uint64_t uint64_value = value; + int part0 = uint64_value & 0xFFFFu; + int part1 = (uint64_value >> 16) & 0xFFFFu; + int part2 = (uint64_value >> 32) & 0xFFFFu; + int part3 = (uint64_value >> 48) & 0xFFFFu; + + auto hash = Joaat(statName); + STATS::STAT_SET_MASKED_INT(hash, part0, 0, 16, true); //bit0-bit15 + STATS::STAT_SET_MASKED_INT(hash, part1, 16, 16, true); //bit16-bit31 + STATS::STAT_SET_MASKED_INT(hash, part2, 32, 16, true); //bit32-bit47 + STATS::STAT_SET_MASKED_INT(hash, part3, 48, 16, true); //bit48-bit63 + } + + uint64_t GetMaskedAll(Hash hash, int playerindex) + { + int part0 = 0, part1 = 0, part2 = 0, part3 = 0; + STATS::STAT_GET_MASKED_INT(hash, &part0, 0, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part1, 16, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part2, 32, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part3, 48, 16, playerindex); + uint64_t value = (static_cast(part3) << 48) | (static_cast(part2) << 32) | (static_cast(part1) << 16) | part0; + return value; + } + + uint64_t GetMaskedAll(std::string statName, int playerindex) + { + Hash hash = Joaat(statName); + int part0 = 0, part1 = 0, part2 = 0, part3 = 0; + STATS::STAT_GET_MASKED_INT(hash, &part0, 0, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part1, 16, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part2, 32, 16, playerindex); + STATS::STAT_GET_MASKED_INT(hash, &part3, 48, 16, playerindex); + uint64_t value = (static_cast(part3) << 48) | (static_cast(part2) << 32) | (static_cast(part1) << 16) | part0; + return value; + } + } \ No newline at end of file diff --git a/src/game/gta/Stats.hpp b/src/game/gta/Stats.hpp index 531f8990f..d68d52f1b 100644 --- a/src/game/gta/Stats.hpp +++ b/src/game/gta/Stats.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "core/util/Joaat.hpp" #include "types/script/types.hpp" @@ -7,7 +7,7 @@ namespace YimMenu::Stats extern int GetCharIndex(); extern void SetInt(std::string statName, int value); - extern void SetBool(std::string statName, bool value); + extern void SetBool(std::string statName, bool value = true); extern void SetFloat(std::string statName, float value); extern void SetDate(std::string statName, Date* value); extern void SetString(std::string statName, const char* value); @@ -26,4 +26,11 @@ namespace YimMenu::Stats extern void SetMaskedInt(std::string statName, int bitStart, int bitSize, int value); extern bool GetMaskedBool(std::string statName, int bitIndex); extern int GetMaskedInt(std::string statName, int bitIndex, int bitSize); + + //You can directly write/read all packed int/bool and uint64 statistics. + //pos/date seems to work too,but the data structure is still unclear for now. + extern void SetMaskedAll(std::string statName, uint64_t value); + extern void SetMaskedAll(Hash hash, uint64_t value); + uint64_t GetMaskedAll(Hash hash, int playerindex=-1); + uint64_t GetMaskedAll(std::string statName, int playerindex = -1); } \ No newline at end of file diff --git a/src/types/stats/sStatData.hpp b/src/types/stats/sStatData.hpp index 7e4a61706..79da02a01 100644 --- a/src/types/stats/sStatData.hpp +++ b/src/types/stats/sStatData.hpp @@ -1,10 +1,11 @@ -#pragma once +#pragma once class sStatData { public: enum class Type { + NONE=0, INT = 1, FLOAT, STRING, @@ -15,7 +16,11 @@ class sStatData UINT64, DATE = 20, POS, - INT64 = 26, + TEXTLABEL = 22, + PACKED = 23, + USERID = 24, + PROFILE_SETTING = 25, + INT64 = 26 }; // it isn't recommended to call the SetXXX() functions directly, use the natives instead From 667c6658b928f759763cb351eace2d9f90baa06a Mon Sep 17 00:00:00 2001 From: fangshun Date: Mon, 17 Aug 2026 19:25:11 +0800 Subject: [PATCH 2/6] Add modifications to the data types of date, userid, and pos --- .../frontend/submenus/Recovery/StatEditor.cpp | 168 ++++++++++++++++-- src/game/gta/Stats.hpp | 1 - 2 files changed, 157 insertions(+), 12 deletions(-) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index ecb024537..37c64de54 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -35,11 +35,12 @@ namespace YimMenu::Submenus }; union StatValue { - float m_AsFloat; + float m_AsFloat[3]; int m_AsInt; bool m_AsBool; std::uint64_t m_AsU64; - char m_AsString[12]; + char m_AsString[64]; + Date m_Date; }; // https://stackoverflow.com/questions/66897068/can-trim-of-a-string-be-done-inplace-with-c20-ranges @@ -108,7 +109,7 @@ namespace YimMenu::Submenus return name; } - static void ReadStat(StatValue& value, sStatData* data) + static void ReadStat(std::uint32_t hash,StatValue& value, sStatData* data) { memset(&value, 0, sizeof(StatValue)); @@ -118,7 +119,7 @@ namespace YimMenu::Submenus value.m_AsBool = data->GetBool(); return; case sStatData::Type::FLOAT: - value.m_AsFloat = data->GetFloat(); + value.m_AsFloat[0] = data->GetFloat(); return; case sStatData::Type::INT: case sStatData::Type::UINT32: @@ -136,6 +137,19 @@ namespace YimMenu::Submenus case sStatData::Type::STRING: strncpy(value.m_AsString, data->GetString(), sizeof(value.m_AsString)); return; + case sStatData::Type::POS: + STATS::STAT_GET_POS(hash, &value.m_AsFloat[0], &value.m_AsFloat[1], &value.m_AsFloat[2], true); + return; + case sStatData::Type::DATE: + STATS::STAT_GET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true); + return; + case sStatData::Type::USERID: + char user_id[64]{}; + data->GetUserID(user_id, sizeof(user_id)); + value.m_AsU64 = std::strtoull(user_id, nullptr, 10); + return; + case sStatData::Type::PROFILE_SETTING: + case sStatData::Type::TEXTLABEL: default: return; // data type not supported } @@ -149,7 +163,7 @@ namespace YimMenu::Submenus STATS::STAT_SET_BOOL(hash, value.m_AsBool, true); return; case sStatData::Type::FLOAT: - STATS::STAT_SET_FLOAT(hash, value.m_AsFloat, true); + STATS::STAT_SET_FLOAT(hash, value.m_AsFloat[0], true); return; case sStatData::Type::INT: case sStatData::Type::UINT32: @@ -172,6 +186,10 @@ namespace YimMenu::Submenus case sStatData::Type::STRING: STATS::STAT_SET_STRING(hash, value.m_AsString, true); return; + case sStatData::Type::USERID: + STATS::STAT_SET_USER_ID(hash, std::string(value.m_AsString).c_str(), true); + //data->SetUserID(value.m_AsString); + return; case sStatData::Type::PACKED: /*data->SetUInt64(value.m_AsU64 - 1); Packed data can't be written using STATS::STAT_INCREMENT @@ -179,13 +197,30 @@ namespace YimMenu::Submenus Stats::SetMaskedAll(hash, value.m_AsU64); return; case sStatData::Type::POS: + STATS::STAT_SET_POS(hash, value.m_AsFloat[0], value.m_AsFloat[1], value.m_AsFloat[2], true); case sStatData::Type::DATE: + STATS::STAT_SET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true); case sStatData::Type::PROFILE_SETTING: + case sStatData::Type::TEXTLABEL: default: return; // data type not supported } } + static bool CheckDate(Date date) + { + int &year = date.Year, &Month = date.Month, &day = date.Day, &Hour = date.Hour, &Minute = date.Minute, &Second = date.Second, &Mil = date.Millisecond; + + int checkfeb = 30 + ((Month % 2) + (Month >= 8)) % 2; + checkfeb = checkfeb - (2 * (Month == 2)); + checkfeb = checkfeb + ((Month == 2) && ((year % 100) && (year % 4 == 0) || (year % 400 == 0))); + + if (year >= 0 && day >= 0 && day <= checkfeb && Month >= 0 && Month <= 12 && Hour >= 0 && Hour < 24 && Minute >= 0 && Minute < 60 && Second >= 0 && Second < 60 && Mil >= 0 && Mil < 1000) + return true; + + return false; + } + // TODO: don't call std::string_view::data() static void WriteStatWithStringValue(std::uint32_t hash, std::string_view value, sStatData* data) { @@ -199,7 +234,7 @@ namespace YimMenu::Submenus return tolower(c); }); - if (as_string == "true" || as_string == "1") + if (as_string != "false" && as_string != "0") { _bool = true; } @@ -240,9 +275,80 @@ namespace YimMenu::Submenus STATS::STAT_SET_STRING(hash, value.data(), true); return; case sStatData::Type::PACKED: + { auto uint64_ = std::strtoull(value.data(), nullptr, 10); Stats::SetMaskedAll(hash, uint64_); return; + } + case sStatData::Type::USERID: + { + auto uint64_ = std::strtoull(value.data(), nullptr, 10); + std::string user_id = std::to_string(uint64_); + STATS::STAT_SET_USER_ID(hash, user_id.c_str(), true); + return; + } + case sStatData::Type::DATE: + { + std::stringstream ss(value.data()); + std::string token; + std::vector date; + date.reserve(7); + + while (std::getline(ss, token, ',')) + { + try + { + date.emplace_back(std::stoi(token)); + } + catch (...) + { + date.emplace_back(0); + } + } + + if (date.size() == 7) + { + Date temp{ + date[0], // Year + date[1], // Month + date[2], // Day + date[3], // Hour + date[4], // Minute + date[5], // Second + date[6] // Millisecond + }; + if (CheckDate(temp)) + { + STATS::STAT_SET_DATE(hash, &temp, sizeof(Date) / 8, true); + } + } + return; + } + case sStatData::Type::POS: + { + std::stringstream ss(value.data()); + std::string token; + std::vector pos; + pos.reserve(3); + + while (std::getline(ss, token, ',')) + { + try + { + pos.emplace_back(std::stof(token)); + } + catch (...) + { + pos.emplace_back(0.0f); + } + } + if (pos.size() == 3) + { + STATS::STAT_SET_POS(hash, pos[0], pos[1], pos[2], true); + } + return; + } + default: return; // data type not supported } @@ -256,7 +362,7 @@ namespace YimMenu::Submenus case sStatData::Type::_BOOL: return ImGui::Checkbox("Value", &value.m_AsBool); case sStatData::Type::FLOAT: - return ImGui::InputFloat("Value", &value.m_AsFloat); + return ImGui::InputFloat("Value", &value.m_AsFloat[0]); case sStatData::Type::INT: return ImGui::InputInt("Value", &value.m_AsInt); case sStatData::Type::UINT32: @@ -268,11 +374,50 @@ namespace YimMenu::Submenus case sStatData::Type::INT64: return ImGui::InputScalar("Value", ImGuiDataType_S64, &value.m_AsU64); case sStatData::Type::UINT64: + case sStatData::Type::USERID: return ImGui::InputScalar("Value", ImGuiDataType_U64, &value.m_AsU64); case sStatData::Type::STRING: return ImGui::InputText("Value", value.m_AsString, sizeof(value.m_AsString)); case sStatData::Type::PACKED: return ImGui::Bitfield("Value", &value.m_AsU64); + case sStatData::Type::POS: + ImGui::PushItemWidth(50.0f); + ImGui::InputFloat("X", &value.m_AsFloat[0]); + ImGui::SameLine(); + ImGui::InputFloat("Y", &value.m_AsFloat[1]); + ImGui::SameLine(); + ImGui::InputFloat("Z", &value.m_AsFloat[2]); + ImGui::PopItemWidth(); + return true; + case sStatData::Type::DATE: + { + ImGui::PushItemWidth(60.0f); + ImGui::InputScalar("Year", ImGuiDataType_U32, &value.m_Date.Year); + ImGui::SameLine(); + ImGui::PopItemWidth(); + ImGui::PushItemWidth(50.0f); + ImGui::InputScalar("Month", ImGuiDataType_U32, &value.m_Date.Month); + ImGui::SameLine(); + ImGui::InputScalar("Day", ImGuiDataType_U32, &value.m_Date.Day); + ImGui::SameLine(); + ImGui::InputScalar("Hour", ImGuiDataType_U32, &value.m_Date.Hour); + ImGui::SameLine(); + ImGui::InputScalar("Minute", ImGuiDataType_U32, &value.m_Date.Minute); + ImGui::SameLine(); + ImGui::InputScalar("Second", ImGuiDataType_U32, &value.m_Date.Second); + ImGui::SameLine(); + ImGui::InputScalar("Millisecond", ImGuiDataType_U32, &value.m_Date.Millisecond); + ImGui::PopItemWidth(); + if (CheckDate(value.m_Date)) + return true; + else + { + ImGui::TextColored(ImVec4(0.957f, 0.643f, 0.376f, 1.00f), "The entered date or time is invalid, please recheck the input data."); + return false; + } + } + case sStatData::Type::PROFILE_SETTING: + case sStatData::Type::TEXTLABEL: default: ImGui::BeginDisabled(); ImGui::Text("Data type not supported"); @@ -355,7 +500,7 @@ namespace YimMenu::Submenus { current_info = GetStatInfo(stat_buf); if (current_info.IsValid()) - ReadStat(value, current_info.m_Data); + ReadStat(current_info.m_NameHash,value, current_info.m_Data); } if (!current_info.IsValid()) @@ -365,12 +510,13 @@ namespace YimMenu::Submenus ImGui::Text("Normalized name to: %s", current_info.m_Name.data()); } - bool can_edit = !current_info.m_Data->IsControlledByNetshop(); + bool can_edit = RenderStatEditor(value, current_info.m_Data); - RenderStatEditor(value, current_info.m_Data); + if (can_edit) + can_edit = !current_info.m_Data->IsControlledByNetshop(); if (ImGui::Button("Refresh")) - ReadStat(value, current_info.m_Data); + ReadStat(current_info.m_NameHash,value, current_info.m_Data); ImGui::SameLine(); ImGui::BeginDisabled(!can_edit); if (ImGui::Button("Write")) diff --git a/src/game/gta/Stats.hpp b/src/game/gta/Stats.hpp index d68d52f1b..e1ff827a6 100644 --- a/src/game/gta/Stats.hpp +++ b/src/game/gta/Stats.hpp @@ -28,7 +28,6 @@ namespace YimMenu::Stats extern int GetMaskedInt(std::string statName, int bitIndex, int bitSize); //You can directly write/read all packed int/bool and uint64 statistics. - //pos/date seems to work too,but the data structure is still unclear for now. extern void SetMaskedAll(std::string statName, uint64_t value); extern void SetMaskedAll(Hash hash, uint64_t value); uint64_t GetMaskedAll(Hash hash, int playerindex=-1); From 9c27f7eb2ad80ce8eae291c2d4e730b0d7b653df Mon Sep 17 00:00:00 2001 From: fangshun Date: Mon, 17 Aug 2026 19:58:23 +0800 Subject: [PATCH 3/6] Safe Conversion --- .../frontend/submenus/Recovery/StatEditor.cpp | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index 37c64de54..1f486789c 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -281,12 +281,10 @@ namespace YimMenu::Submenus return; } case sStatData::Type::USERID: - { - auto uint64_ = std::strtoull(value.data(), nullptr, 10); - std::string user_id = std::to_string(uint64_); - STATS::STAT_SET_USER_ID(hash, user_id.c_str(), true); + if (value.find_first_not_of("0123456789") == std::string::npos && !value.empty()) + STATS::STAT_SET_USER_ID(hash, value.data(), true); + //data->SetUserID(value.data()); return; - } case sStatData::Type::DATE: { std::stringstream ss(value.data()); @@ -296,14 +294,12 @@ namespace YimMenu::Submenus while (std::getline(ss, token, ',')) { - try - { - date.emplace_back(std::stoi(token)); - } - catch (...) - { - date.emplace_back(0); - } + uint32_t date_token = 0; + auto [ptr, ec] = std::from_chars(token.c_str(), token.c_str() + token.size(), date_token); + if (ec != std::errc()) + return; + + date.emplace_back(date_token); } if (date.size() == 7) @@ -333,14 +329,12 @@ namespace YimMenu::Submenus while (std::getline(ss, token, ',')) { - try - { - pos.emplace_back(std::stof(token)); - } - catch (...) - { - pos.emplace_back(0.0f); - } + float pos_token = 0.0f; + auto [ptr, ec] = std::from_chars(token.c_str(), token.c_str() + token.size(), pos_token); + if (ec != std::errc()) + return; + + pos.emplace_back(pos_token); } if (pos.size() == 3) { From f797db6a4df8fb54c673d74bf1fa3d3f8bf22447 Mon Sep 17 00:00:00 2001 From: fangshun Date: Mon, 17 Aug 2026 20:40:05 +0800 Subject: [PATCH 4/6] update "StatEditor.cpp" --- src/game/frontend/submenus/Recovery/StatEditor.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index 1f486789c..4586f8f5a 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -39,7 +39,7 @@ namespace YimMenu::Submenus int m_AsInt; bool m_AsBool; std::uint64_t m_AsU64; - char m_AsString[64]; + char m_AsString[21]; Date m_Date; }; @@ -144,10 +144,12 @@ namespace YimMenu::Submenus STATS::STAT_GET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true); return; case sStatData::Type::USERID: - char user_id[64]{}; + { + char user_id[21]{}; data->GetUserID(user_id, sizeof(user_id)); value.m_AsU64 = std::strtoull(user_id, nullptr, 10); return; + } case sStatData::Type::PROFILE_SETTING: case sStatData::Type::TEXTLABEL: default: @@ -198,8 +200,10 @@ namespace YimMenu::Submenus return; case sStatData::Type::POS: STATS::STAT_SET_POS(hash, value.m_AsFloat[0], value.m_AsFloat[1], value.m_AsFloat[2], true); + return; case sStatData::Type::DATE: STATS::STAT_SET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true); + return; case sStatData::Type::PROFILE_SETTING: case sStatData::Type::TEXTLABEL: default: @@ -283,7 +287,6 @@ namespace YimMenu::Submenus case sStatData::Type::USERID: if (value.find_first_not_of("0123456789") == std::string::npos && !value.empty()) STATS::STAT_SET_USER_ID(hash, value.data(), true); - //data->SetUserID(value.data()); return; case sStatData::Type::DATE: { From 9f2d502b1a77cdc965522c7bcc9538b57664c4cf Mon Sep 17 00:00:00 2001 From: fangshun Date: Mon, 17 Aug 2026 22:02:26 +0800 Subject: [PATCH 5/6] fix userid Write error --- src/game/frontend/submenus/Recovery/StatEditor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index 4586f8f5a..ee3c9c727 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -189,9 +189,12 @@ namespace YimMenu::Submenus STATS::STAT_SET_STRING(hash, value.m_AsString, true); return; case sStatData::Type::USERID: - STATS::STAT_SET_USER_ID(hash, std::string(value.m_AsString).c_str(), true); + { + std::string user_id = std::to_string(value.m_AsU64); + STATS::STAT_SET_USER_ID(hash, user_id.c_str(), true); //data->SetUserID(value.m_AsString); return; + } case sStatData::Type::PACKED: /*data->SetUInt64(value.m_AsU64 - 1); Packed data can't be written using STATS::STAT_INCREMENT From cfefe67daa5285b6e1f3e1f1d9f7ac0793561522 Mon Sep 17 00:00:00 2001 From: fangshun Date: Tue, 18 Aug 2026 21:39:41 +0800 Subject: [PATCH 6/6] Add some header files needed by the compiler --- src/game/frontend/submenus/Recovery/StatEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index ee3c9c727..4b5b46074 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -6,6 +6,7 @@ #include "game/pointers/Pointers.hpp" #include "StatEditor.hpp" #include "types/stats/CStatsMgr.hpp" +#include namespace YimMenu::Submenus {