Skip to content

Commit

Permalink
Merge branch 'praydog:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyhodge authored Mar 26, 2024
2 parents 58f94ca + b125110 commit a1dc18c
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 15 deletions.
10 changes: 8 additions & 2 deletions src/mods/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,14 @@ void Camera::set_vignette_brightness(float value) noexcept {
return;
}

// Not a TDB method.
utility::re_managed_object::call_method((::REManagedObject*)m_tone_map, "setVignettingBrightness", (double)value);
static auto set_vignetting_brightness_method = sdk::find_method_definition("via.render.ToneMapping", "set_VignettingBrightness");

if (set_vignetting_brightness_method != nullptr) {
set_vignetting_brightness_method->call<void*>(sdk::get_thread_context(), m_tone_map, value);
} else {
// Not a TDB method.
utility::re_managed_object::call_method((::REManagedObject*)m_tone_map, "setVignettingBrightness", (double)value);
}
}

void Camera::set_fov(float fov, float aiming_fov) noexcept {
Expand Down
7 changes: 7 additions & 0 deletions src/mods/bindings/Sdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,10 @@ sol::object parse_data(lua_State* l, void* data, ::sdk::RETypeDefinition* data_t
return sol::make_object(l, ret_val_f);
}
}
case "System.Double"_fnv: {
auto ret_val_d = *(double*)data;
return sol::make_object(l, ret_val_d);
}
case "System.Boolean"_fnv: {
auto ret_val_b = *(bool*)data;
return sol::make_object(l, ret_val_b);
Expand Down Expand Up @@ -929,6 +933,9 @@ void set_data(void* data, ::sdk::RETypeDefinition* data_type, sol::object& value
case "System.Single"_fnv:
*(float*)data = value.as<float>();
return;
case "System.Double"_fnv:
*(double*)data = value.as<double>();
return;
case "System.Boolean"_fnv:
*(bool*)data = value.as<bool>();
return;
Expand Down
113 changes: 101 additions & 12 deletions src/mods/tools/ObjectExplorer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,9 +685,68 @@ void ObjectExplorer::display_pins() {
}

void ObjectExplorer::display_hooks() {
std::scoped_lock _{m_hooked_methods_mtx};
std::scoped_lock _{m_hooks_context.mtx};

ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once);
if (ImGui::TreeNode("Options")) {
ImGui::Checkbox("Hide uncalled methods", &m_hooks_context.hide_uncalled_methods);

// Combobox of the sort method instead
if (ImGui::BeginCombo("Sort by", HooksContext::s_sort_method_names[(uint8_t)m_hooks_context.sort_method])) {
for (int i = 0; i < HooksContext::s_sort_method_names.size(); i++) {
const bool is_selected = (m_hooks_context.sort_method == (HooksContext::SortMethod)i);

if (ImGui::Selectable(HooksContext::s_sort_method_names[i], is_selected)) {
m_hooks_context.sort_method = (HooksContext::SortMethod)i;
}

if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}

ImGui::EndCombo();
}

ImGui::TreePop();
}

std::vector<HookedMethod*> hooks_to_iterate{};
for (auto& h : m_hooked_methods) {
if (m_hooks_context.hide_uncalled_methods && h.call_count == 0) {
continue;
}

hooks_to_iterate.push_back(&h);
}

switch (m_hooks_context.sort_method) {
case HooksContext::SortMethod::CALL_COUNT:
std::sort(hooks_to_iterate.begin(), hooks_to_iterate.end(), [](const auto& a, const auto& b) {
return a->call_count > b->call_count;
});
break;
case HooksContext::SortMethod::CALL_TIME:
std::sort(hooks_to_iterate.begin(), hooks_to_iterate.end(), [](const auto& a, const auto& b) {
return a->last_call_time > b->last_call_time;
});
break;
case HooksContext::SortMethod::METHOD_NAME:
std::sort(hooks_to_iterate.begin(), hooks_to_iterate.end(), [](const auto& a, const auto& b) {
return a->name < b->name;
});
break;
case HooksContext::SortMethod::NUMBER_OF_CALLERS:
std::sort(hooks_to_iterate.begin(), hooks_to_iterate.end(), [](const auto& a, const auto& b) {
return a->callers.size() > b->callers.size();
});
break;
default:
break;
};

for (auto& hp : hooks_to_iterate) {
auto& h = *hp;
ImGui::PushID(h.method);

ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once);
Expand Down Expand Up @@ -4455,8 +4514,9 @@ HookManager::PreHookResult ObjectExplorer::pre_hooked_method_internal(std::vecto

auto& hooked_method = *it;

std::scoped_lock _{m_hooked_methods_mtx};
std::scoped_lock _{m_hooks_context.mtx};
++hooked_method.call_count;
hooked_method.last_call_time = std::chrono::high_resolution_clock::now();

if (!hooked_method.return_addresses.contains(ret_addr)) {
spdlog::info("Creating new entry for {}", hooked_method.name);
Expand All @@ -4467,7 +4527,11 @@ HookManager::PreHookResult ObjectExplorer::pre_hooked_method_internal(std::vecto
size_t nearest_distance = UINT64_MAX;

for (auto& it : m_method_map) {
const auto distance = std::abs(static_cast<int64_t>(it.first - ret_addr));
if (it.first > ret_addr) {
continue;
}

const auto distance = ret_addr - it.first;
if (distance < nearest_distance) {
nearest_distance = distance;
nearest_method = it.second;
Expand All @@ -4477,26 +4541,51 @@ HookManager::PreHookResult ObjectExplorer::pre_hooked_method_internal(std::vecto
if (nearest_method != nullptr) {
auto method_entry = utility::find_function_entry((uintptr_t)nearest_method->get_function());

bool added = false;
auto add_method = [&]() {
hooked_method.return_addresses_to_methods[ret_addr] = nearest_method;
hooked_method.callers.insert(nearest_method);

const auto decl_type = nearest_method->get_declaring_type();

if (decl_type != nullptr) {
spdlog::info("{} {}.{}", hooked_method.name, nearest_method->get_declaring_type()->get_full_name(), nearest_method->get_name());
} else {
spdlog::info("{} {}", hooked_method.name, nearest_method->get_name());
}

added = true;
};

if (method_entry != nullptr) {
const auto module_addr = (uintptr_t)utility::get_module_within(ret_addr).value_or(nullptr);
const auto ret_addr_rva = (uint32_t)(ret_addr - module_addr);
const auto ret_addr_entry = utility::find_function_entry(ret_addr);

// First condition isn't as heavy as fully disassembling the function which is the second condition
if (ret_addr_entry == method_entry ||
(ret_addr_rva >= method_entry->BeginAddress && ret_addr_rva <= method_entry->EndAddress) ||
(ret_addr_entry != nullptr && ret_addr_entry->BeginAddress >= method_entry->BeginAddress && ret_addr_entry->BeginAddress <= method_entry->EndAddress + 1))
{
hooked_method.return_addresses_to_methods[ret_addr] = nearest_method;
hooked_method.callers.insert(nearest_method);
add_method();
} else {
// Disassemble all possible code paths to see if we run into the return address
utility::exhaustive_decode((uint8_t*)nearest_method->get_function(), 5000, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
if (ctx.addr == ret_addr) {
add_method();
return utility::ExhaustionResult::BREAK;
}

const auto decl_type = nearest_method->get_declaring_type();
if (std::string_view{ctx.instrux.Mnemonic}.starts_with("CALL")) {
return utility::ExhaustionResult::STEP_OVER;
}

if (decl_type != nullptr) {
spdlog::info("{} {}.{}", hooked_method.name, nearest_method->get_declaring_type()->get_full_name(), nearest_method->get_name());
} else {
spdlog::info("{} {}", hooked_method.name, nearest_method->get_name());
return utility::ExhaustionResult::CONTINUE;
});

if (!added) {
spdlog::info("{} <unknown caller> @ 0x{:x}", hooked_method.name, ret_addr);
}
} else {
spdlog::info("{} <unknown caller> @ 0x{:x}", hooked_method.name, ret_addr);
}
} else {
hooked_method.return_addresses_to_methods[ret_addr] = nearest_method;
Expand Down
24 changes: 23 additions & 1 deletion src/mods/tools/ObjectExplorer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,28 @@ class ObjectExplorer : public Tool {
std::string path{};
};

std::recursive_mutex m_hooked_methods_mtx{};
struct HooksContext {
enum class SortMethod : uint8_t {
NONE,
CALL_COUNT,
CALL_TIME,
METHOD_NAME,
NUMBER_OF_CALLERS
};

static inline constexpr std::array<const char*, 5> s_sort_method_names {
"None",
"Call Count",
"Call Time",
"Method Name",
"Number of Callers"
};

std::recursive_mutex mtx{};
bool hide_uncalled_methods{false};
SortMethod sort_method{SortMethod::NONE};
} m_hooks_context{};

std::recursive_mutex m_job_mutex{};
std::vector<std::function<void()>> m_frame_jobs{};

Expand All @@ -246,6 +267,7 @@ class ObjectExplorer : public Tool {
};

std::unordered_map<sdk::REMethodDefinition*, CallerContext> callers_context{};
std::chrono::high_resolution_clock::time_point last_call_time{};
};

asmjit::JitRuntime m_jit_runtime;
Expand Down

0 comments on commit a1dc18c

Please sign in to comment.