From 29a8e59d6fef73d61c0937018272345932449970 Mon Sep 17 00:00:00 2001 From: praydog Date: Sun, 22 Oct 2023 00:40:21 -0700 Subject: [PATCH] UObjectHook: Add basic field/function browser --- src/mods/UObjectHook.cpp | 116 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/src/mods/UObjectHook.cpp b/src/mods/UObjectHook.cpp index 88ea4284..b70e0eed 100644 --- a/src/mods/UObjectHook.cpp +++ b/src/mods/UObjectHook.cpp @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include #include "UObjectHook.hpp" @@ -141,8 +144,13 @@ void UObjectHook::on_draw_ui() { } if (ImGui::TreeNode("Objects by class")) { + static char filter[256]{}; + ImGui::InputText("Filter", filter, sizeof(filter)); + + const bool filter_empty = std::string_view{filter}.empty(); + const auto now = std::chrono::steady_clock::now(); - bool needs_sort = now - m_last_sort_time >= std::chrono::seconds(1) || m_sorted_classes.empty(); + bool needs_sort = true; if (sorting_task.valid()) { // Check if the sorting task is finished @@ -182,6 +190,8 @@ void UObjectHook::on_draw_ui() { m_last_sort_time = now; } + const auto wide_filter = utility::widen(filter); + for (auto uclass : m_sorted_classes) { const auto& objects = m_objects_by_class[uclass]; @@ -189,11 +199,113 @@ void UObjectHook::on_draw_ui() { continue; } + if (!m_meta_objects.contains(uclass)) { + continue; + } + const auto uclass_name = utility::narrow(m_meta_objects[uclass]->full_name); + bool valid = true; + + if (!filter_empty) { + valid = false; + + for (auto super = (sdk::UStruct*)uclass; super; super = super->get_super_struct()) { + if (auto it = m_meta_objects.find(super); it != m_meta_objects.end()) { + if (it->second->full_name.find(wide_filter) != std::wstring::npos) { + valid = true; + break; + } + } + } + } + + if (!valid) { + continue; + } if (ImGui::TreeNode(uclass_name.data())) { for (const auto& object : objects) { - ImGui::Text("%s", utility::narrow(m_meta_objects[object]->full_name).data()); + if (ImGui::TreeNode(utility::narrow(m_meta_objects[object]->full_name).data())) { + std::vector sorted_fields{}; + std::vector sorted_functions{}; + + const auto ufunction_t = sdk::UFunction::static_class(); + + for (auto super = (sdk::UStruct*)uclass; super != nullptr; super = super->get_super_struct()) { + auto props = super->get_child_properties(); + + for (auto prop = props; prop != nullptr; prop = prop->get_next()) { + sorted_fields.push_back(prop); + } + + auto funcs = super->get_children(); + + for (auto func = funcs; func != nullptr; func = func->get_next()) { + if (func->get_class()->is_a(ufunction_t)) { + sorted_functions.push_back((sdk::UFunction*)func); + } + } + } + + std::sort(sorted_fields.begin(), sorted_fields.end(), [](sdk::FField* a, sdk::FField* b) { + return a->get_field_name().to_string() < b->get_field_name().to_string(); + }); + + std::sort(sorted_functions.begin(), sorted_functions.end(), [](sdk::UFunction* a, sdk::UFunction* b) { + return a->get_fname().to_string() < b->get_fname().to_string(); + }); + + if (ImGui::TreeNode("Functions")) { + for (auto func : sorted_functions) { + /*if (ImGui::Button(utility::narrow(func->get_class()->get_fname().to_string()).data())) { + func->process_event(object, nullptr); + }*/ + + if (ImGui::TreeNode(utility::narrow(func->get_fname().to_string()).data())) { + auto parameters = func->get_child_properties(); + + for (auto param = parameters; param != nullptr; param = param->get_next()) { + ImGui::Text("%s %s", utility::narrow(param->get_class()->get_name().to_string()), utility::narrow(param->get_field_name().to_string()).data()); + } + + ImGui::TreePop(); + } + } + + ImGui::TreePop(); + } + + for (auto prop : sorted_fields) { + auto propc = prop->get_class(); + + const auto propc_type = utility::narrow(propc->get_name().to_string()); + + switch (utility::hash(propc_type)) { + case "FloatProperty"_fnv: + { + auto& value = *(float*)((uintptr_t)object + ((sdk::FProperty*)prop)->get_offset()); + ImGui::DragFloat(utility::narrow(prop->get_field_name().to_string()).data(), &value, 0.01f); + } + break; + case "IntProperty"_fnv: + { + auto& value = *(int32_t*)((uintptr_t)object + ((sdk::FProperty*)prop)->get_offset()); + ImGui::DragInt(utility::narrow(prop->get_field_name().to_string()).data(), &value, 1); + } + break; + case "BoolProperty"_fnv: + { + auto& value = *(bool*)((uintptr_t)object + ((sdk::FProperty*)prop)->get_offset()); + ImGui::Checkbox(utility::narrow(prop->get_field_name().to_string()).data(), &value); + } + break; + default: + ImGui::Text("%s %s", utility::narrow(propc->get_name().to_string()), utility::narrow(prop->get_field_name().to_string()).data()); + }; + } + + ImGui::TreePop(); + } } ImGui::TreePop();