Locals editor (#1211)

This commit is contained in:
DayibBaba 2023-04-11 21:36:43 +02:00 committed by GitHub
parent 5ed00303bf
commit a6dc40b890
3 changed files with 402 additions and 0 deletions

View File

@ -0,0 +1,126 @@
#include "locals_service.hpp"
#include "core/data/all_script_names.hpp"
#include "natives.hpp"
#include "pointers.hpp"
#include "fiber_pool.hpp"
namespace big
{
bool locals_service::is_script_thread_running(GtaThread* thread)
{
if (thread)
{
return thread->m_context.m_state == rage::eThreadState::running || thread->m_context.m_state == rage::eThreadState::idle;
}
return false;
}
bool locals_service::does_script_exist(std::string script_name)
{
for (auto s : all_script_names)
if (script_name == s)
return true;
bool script_exists = false;
g_fiber_pool->queue_job([&] {script_exists = SCRIPT::DOES_SCRIPT_EXIST(script_name.data());});
return script_exists;
}
std::filesystem::path locals_service::get_path()
{
return g_file_manager->get_project_file("locals.json").get_path();
}
bool locals_service::load()
{
std::ifstream file(locals_service::get_path());
if (!file.is_open())
return false;
try
{
nlohmann::json j;
file >> j;
m_locals.clear();
for (const auto& l : j.items())
{
if (!l.key().empty())
{
local new_local{"", "", 0, 0, 0, 0};
new_local.m_base_address = j[l.key()]["base_address"];
std::string script_name = j[l.key()]["script_thread_name"];
strcpy(new_local.m_script_thread_name, script_name.data());
new_local.m_freeze = j[l.key()]["freeze"];
std::string name = j[l.key()]["name"];
strcpy(new_local.m_name, name.data());
new_local.m_value = j[l.key()]["value"];
if (!j[l.key()]["offsets"].is_null())
{
for (const auto& offset : j[l.key()]["offsets"].items())
{
if (!offset.key().empty())
{
local_offset new_offset{0, 0};
new_offset.m_offset = j[l.key()]["offsets"][offset.key()]["offset"];
if (!j[l.key()]["offsets"][offset.key()]["size"].is_null())
new_offset.m_size = j[l.key()]["offsets"][offset.key()]["size"];
new_local.m_offsets.push_back(new_offset);
}
}
}
new_local.fetch_local_pointer();
m_locals.push_back(new_local);
}
}
}
catch (const std::exception&)
{
LOG(WARNING) << "Failure to parse locals.json, aborting...";
return false;
}
return true;
}
void locals_service::save()
{
std::map<std::string, local> locals_with_names;
for (auto& l : m_locals)
{
locals_with_names.insert(std::pair<std::string, local>(std::string(l.m_name).empty() ? std::string(l.m_script_thread_name + std::string("_") + std::to_string(l.m_base_address)) : l.m_name, l));
}
nlohmann::json j;
for (auto& l : locals_with_names)
{
j[l.first]["script_thread_name"] = l.second.m_script_thread_name;
j[l.first]["base_address"] = l.second.m_base_address;
j[l.first]["freeze"] = l.second.m_freeze;
j[l.first]["name"] = l.second.m_name;
j[l.first]["value"] = l.second.m_value;
for (int i = 0; i < l.second.m_offsets.size(); i++)
{
j[l.first]["offsets"][std::to_string(i)]["offset"] = l.second.m_offsets[i].m_offset;
if (l.second.m_offsets[i].m_size > 0)
j[l.first]["offsets"][std::to_string(i)]["size"] = l.second.m_offsets[i].m_size;
};
}
std::ofstream file(locals_service::get_path(), std::ios::out | std::ios::trunc);
try
{
file << j.dump(4);
file.close();
}
catch (const std::exception&)
{
LOG(WARNING) << "Failed to write to locals.json";
}
}
};

View File

@ -0,0 +1,104 @@
#pragma once
#include "file_manager.hpp"
#include "file_manager/file.hpp"
#include "gta/script_thread.hpp"
#include "gta_util.hpp"
#include "script_local.hpp"
namespace big
{
struct local_offset
{
local_offset(int offset, int size = 0)
{
m_offset = offset;
if (size)
m_size = size;
}
int m_offset = 0;
int m_size = 0;
};
struct local
{
GtaThread* m_script_thread;
char m_script_thread_name[200];
int m_base_address;
bool m_freeze = false;
char m_name[200];
std::vector<local_offset> m_offsets;
int m_value;
int m_freeze_value;
int* m_internal_address;
local(const char* script_thread_name, const char* name, const int base_address, const bool freeze, const int (*offsets)[2], int offset_count)
{
m_internal_id = ++m_instance_count;
strcpy(m_script_thread_name, script_thread_name);
m_base_address = base_address;
m_freeze = freeze;
strcpy(m_name, name);
m_value = 0;
for (int i = 0; i < offset_count; i++)
m_offsets.push_back(local_offset(offsets[i][0], offsets[i][1]));
fetch_local_pointer();
}
int get_id() const
{
return m_internal_id;
}
int* fetch_local_pointer()
{
m_script_thread = gta_util::find_script_thread(rage::joaat(m_script_thread_name));
if (m_script_thread)
{
script_local actual_local = script_local(m_script_thread, m_base_address);
for (auto offset : m_offsets)
{
if (offset.m_size > 0)
actual_local = actual_local.at(offset.m_offset, offset.m_size);
else
actual_local = actual_local.at(offset.m_offset);
}
m_internal_address = actual_local.as<int*>();
return m_internal_address;
}
return nullptr;
}
private:
inline static int m_instance_count;
int m_internal_id;
};
class locals_service
{
public:
std::filesystem::path get_path();
bool load();
void save();
static bool does_script_exist(std::string script);
static bool is_script_thread_running(GtaThread* thread);
std::vector<local> m_locals;
bool m_running = false;
};
inline locals_service g_locals_service{};
}

View File

@ -1,11 +1,183 @@
#include "gui/components/components.hpp"
#include "services/locals/locals_service.hpp"
#include "view_debug.hpp"
namespace big
{
void render_local_creator_popup_content()
{
static int base_address = 0;
static bool freeze = false;
static char name[200] = "";
static char script_thread_name[200] = "";
static int(*offsets)[2] = nullptr;
static int offset_count = 0;
static int previous_offset_count = 0;
components::input_text_with_hint("##local_name", "Name", name, sizeof(name));
components::input_text_with_hint("##local_script_thread_name", "Script thread", script_thread_name, sizeof(script_thread_name));
ImGui::Text("Base address");
ImGui::InputInt("##local_base_address", &base_address);
ImGui::Text("Offsetcount");
ImGui::InputInt("##modal_offset_count", &offset_count);
if (offset_count < 0)
offset_count = 0;
else if (offset_count > 10)
offset_count = 10;
if (offset_count != previous_offset_count)
{
int(*new_offsets)[2] = new int[offset_count][2]{0};
memcpy(new_offsets, offsets, sizeof(int) * std::min(offset_count, previous_offset_count) * 2);
delete[] offsets;
offsets = new_offsets;
previous_offset_count = offset_count;
}
ImGui::PushItemWidth(320.f);
for (int i = 0; i < offset_count; i++)
{
ImGui::PushID(i);
ImGui::Separator();
ImGui::Text("DEBUG_GLOBAL_OFFSET"_T.data(), i + 1);
ImGui::InputInt("##offset", &offsets[i][0]);
ImGui::Text("DEBUG_GLOBAL_SIZE"_T.data());
ImGui::SameLine();
ImGui::InputInt("##size", &offsets[i][1]);
ImGui::PopID();
}
ImGui::PopItemWidth();
static auto reset_values = []() -> void {
strcpy(name, "");
freeze = false;
delete[] offsets;
offsets = nullptr;
offset_count = 0;
previous_offset_count = 0;
};
if (components::button("CANCEL"_T))
{
reset_values();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (components::button("SAVE"_T))
{
if (locals_service::does_script_exist(script_thread_name))
{
auto new_local = local(script_thread_name, name, base_address, freeze, offsets, offset_count);
g_locals_service.m_locals.push_back(new_local);
reset_values();
ImGui::CloseCurrentPopup();
}
else
{
g_notification_service->push_error("Locals editor", "Script does not exist");
}
};
}
void debug::locals()
{
if (ImGui::BeginTabItem("DEBUG_TAB_LOCALS"_T.data()))
{
if (components::button("LOAD"_T))
g_locals_service.load();
ImGui::SameLine();
if (components::button("SAVE"_T))
g_locals_service.save();
if (components::button("Add local"))
{
ImGui::OpenPopup("##addlocal");
}
if (ImGui::BeginPopupModal("##addlocal"))
{
render_local_creator_popup_content();
ImGui::EndPopup();
}
for (auto& local_ : g_locals_service.m_locals)
{
ImGui::BeginGroup();
ImGui::PushID(local_.get_id());
ImGui::Text("%s : %s", local_.m_name, local_.m_script_thread_name);
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
char offsetschain[200] = "";
strcat(offsetschain, std::to_string(local_.m_base_address).data());
for (auto o : local_.m_offsets)
{
strcat(offsetschain, std::string(".f_" + std::to_string(o.m_offset)).data());
if (o.m_size)
strcat(offsetschain, std::string("/" + std::to_string(o.m_size)).data());
}
ImGui::Text(offsetschain);
ImGui::EndTooltip();
}
//Find the thread among the script threads
if (!local_.m_script_thread)
local_.m_script_thread = gta_util::find_script_thread(rage::joaat(local_.m_script_thread_name));
if (local_.m_script_thread && locals_service::is_script_thread_running(local_.m_script_thread))
{
//Check whether the address is found
if (local_.m_internal_address)
{
ImGui::Text("Value");
ImGui::SetNextItemWidth(200);
if (ImGui::InputInt("##local_value", local_.m_internal_address))
{
local_.m_value = *local_.m_internal_address;
}
ImGui::SameLine();
if (ImGui::Checkbox("Freeze", &local_.m_freeze))
local_.m_freeze_value = *local_.m_internal_address;
if (local_.m_freeze)
*local_.m_internal_address = local_.m_freeze_value;
}
else
{
if (components::button("Fetch"))
{
local_.fetch_local_pointer();
}
}
}
else
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
ImGui::Text("%s isn't running", local_.m_script_thread_name);
ImGui::PopStyleColor();
}
if (components::button("Delete"))
std::erase_if(g_locals_service.m_locals, [local_](local l) {
return l.get_id() == local_.get_id();
});
ImGui::PopID();
ImGui::Separator();
ImGui::EndGroup();
}
ImGui::EndTabItem();
}
}