Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 197 additions & 27 deletions src/game/frontend/submenus/Recovery/StatEditor.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
#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"
#include <charconv>

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;
}
Expand All @@ -26,18 +29,19 @@ namespace YimMenu::Submenus
bool m_IsBoolStat;
bool m_IsValid;

bool IsValid()
bool IsValid() const
{
return m_IsValid;
}
};

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[21];
Date m_Date;
};

// https://stackoverflow.com/questions/66897068/can-trim-of-a-string-be-done-inplace-with-c20-ranges
Expand Down Expand Up @@ -106,7 +110,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));

Expand All @@ -116,7 +120,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:
Expand All @@ -128,11 +132,27 @@ 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:
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[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:
return; // data type not supported
}
Expand All @@ -146,29 +166,69 @@ 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:
case sStatData::Type::UINT16:
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<float>(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<float>(1));
return;
case sStatData::Type::STRING:
STATS::STAT_SET_STRING(hash, value.m_AsString, true);
return;
case sStatData::Type::USERID:
{
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
STATS::STAT_INCREMENT(hash, static_cast<float>(1));*/
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);
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:
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)
{
Expand All @@ -182,7 +242,7 @@ namespace YimMenu::Submenus
return tolower(c);
});

if (as_string == "true" || as_string == "1")
if (as_string != "false" && as_string != "0")
{
_bool = true;
}
Expand All @@ -208,20 +268,88 @@ 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<float>(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<float>(1));
return;
}
case sStatData::Type::STRING:
STATS::STAT_SET_STRING(hash, value.data(), true);
return;
case sStatData::Type::PACKED:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to put this case in braces (initialization of 'uint64_' is skipped by 'default' label).

{
auto uint64_ = std::strtoull(value.data(), nullptr, 10);
Stats::SetMaskedAll(hash, uint64_);
return;
}
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);
return;
case sStatData::Type::DATE:
{
std::stringstream ss(value.data());
std::string token;
std::vector<int> date;
date.reserve(7);

while (std::getline(ss, token, ','))
{
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)
{
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<float> pos;
pos.reserve(3);

while (std::getline(ss, token, ','))
{
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)
{
STATS::STAT_SET_POS(hash, pos[0], pos[1], pos[2], true);
}
return;
}

default:
return; // data type not supported
}
Expand All @@ -235,7 +363,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:
Expand All @@ -245,11 +373,52 @@ 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);
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");
Expand Down Expand Up @@ -332,7 +501,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())
Expand All @@ -342,12 +511,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"))
Expand Down
Loading
Loading