Skip to content

Commit

Permalink
LooseFileLoader: Hook extremely early to intercept files
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Nov 1, 2024
1 parent 91b03ad commit 5201a14
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 24 deletions.
4 changes: 2 additions & 2 deletions src/Mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ std::optional<std::string> Mods::on_initialize() const {
}
}

utility::Config cfg{ (REFramework::get_persistent_dir() / "re2_fw_config.txt").string() };
utility::Config cfg{ (REFramework::get_persistent_dir() / REFrameworkConfig::REFRAMEWORK_CONFIG_NAME).string() };

for (auto& mod : m_mods) {
spdlog::info("{:s}::on_config_load()", mod->get_name().data());
Expand All @@ -96,7 +96,7 @@ std::optional<std::string> Mods::on_initialize() const {
std::optional<std::string> Mods::on_initialize_d3d_thread() const {
std::scoped_lock _{g_framework->get_hook_monitor_mutex()};

utility::Config cfg{ (REFramework::get_persistent_dir() / "re2_fw_config.txt").string() };
utility::Config cfg{ (REFramework::get_persistent_dir() / REFrameworkConfig::REFRAMEWORK_CONFIG_NAME).string() };

// once here to at least setup the values
for (auto& mod : m_mods) {
Expand Down
24 changes: 19 additions & 5 deletions src/REFramework.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern "C" {
#include "utility/Thread.hpp"

#include "Mods.hpp"
#include "mods/LooseFileLoader.hpp"
#include "mods/PluginLoader.hpp"
#include "sdk/REGlobals.hpp"
#include "sdk/Application.hpp"
Expand All @@ -43,7 +44,6 @@ extern "C" {
namespace fs = std::filesystem;
using namespace std::literals;


std::unique_ptr<REFramework> g_framework{};

void REFramework::hook_monitor() {
Expand Down Expand Up @@ -539,6 +539,20 @@ REFramework::REFramework(HMODULE reframework_module)
bool found_renderer = false;
bool renderer_has_render_frame_fn = false;

if (sdk::RETypeDB::get() != nullptr) {
auto& loader = LooseFileLoader::get(); // Initialize this really early

const auto config_path = get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string();
if (fs::exists(utility::widen(config_path))) {
utility::Config cfg{ config_path };
loader->on_config_load(cfg);
}

if (loader->is_enabled()) {
loader->hook();
}
}

while (true) try {
const auto tdb = sdk::RETypeDB::get();

Expand Down Expand Up @@ -827,7 +841,7 @@ void REFramework::on_frame_d3d11() {
if (is_init_ok) {
// Write default config once if it doesn't exist.
if (!std::exchange(m_created_default_cfg, true)) {
if (!fs::exists({utility::widen(get_persistent_dir("re2_fw_config.txt").string())})) {
if (!fs::exists({utility::widen(get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())})) {
save_config();
}
}
Expand Down Expand Up @@ -932,7 +946,7 @@ void REFramework::on_frame_d3d12() {
if (is_init_ok) {
// Write default config once if it doesn't exist.
if (!std::exchange(m_created_default_cfg, true)) {
if (!fs::exists({utility::widen(get_persistent_dir("re2_fw_config.txt").string())})) {
if (!fs::exists({utility::widen(get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())})) {
save_config();
}
}
Expand Down Expand Up @@ -1313,7 +1327,7 @@ std::filesystem::path REFramework::get_persistent_dir() {
void REFramework::save_config() {
std::scoped_lock _{m_config_mtx};

spdlog::info("Saving config re2_fw_config.txt");
spdlog::info("Saving config {}", REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data());

utility::Config cfg{};

Expand All @@ -1322,7 +1336,7 @@ void REFramework::save_config() {
}

try {
if (!cfg.save((get_persistent_dir() / "re2_fw_config.txt").string())) {
if (!cfg.save((get_persistent_dir() / REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())) {
spdlog::error("Failed to save config");
return;
}
Expand Down
61 changes: 45 additions & 16 deletions src/mods/LooseFileLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,30 +161,53 @@ void LooseFileLoader::hook() {
return;
}

auto initial_candidate = [&]() -> std::optional<uintptr_t> {
const auto via_io_file = tdb->find_type("via.io.file");
auto initial_candidates = [&]() -> std::vector<uintptr_t> {
sdk::RETypeDefinition* via_io_file = nullptr;

// We need to look for via.io.file manually because LooseFileLoader gets loaded extremely early
// meaning VM stuff may not work correctly
for (auto i = 0; i < tdb->get_num_types(); ++i) {
const auto t = tdb->get_type(i);

if (t == nullptr || t->get_name() == nullptr) {
continue;
}

if (std::string_view{t->get_name()} == "file") {
if (t->get_declaring_type() != nullptr && std::string_view{t->get_declaring_type()->get_name()} == "io") {
via_io_file = t;
break;
}
}
}

if (via_io_file == nullptr) {
spdlog::error("[LooseFileLoader] Failed to find via.io.file");
return std::nullopt;
return {};
}

// "exist" and "exists" are 2 different functions. one searches in pak and other doesnt.
const auto exists_fn = via_io_file->get_method("exists(System.String)");
spdlog::info("[LooseFileLoader] Found via.io.file");

if (exists_fn == nullptr) {
spdlog::error("[LooseFileLoader] Failed to find via.io.file.exists(System.String)");
return std::nullopt;
}
std::vector<uintptr_t> candidates{};

// Same reason as above, manually loop through methods because VM stuff may not work correctly
for (auto& m : via_io_file->get_methods()) {
if (m.get_name() == nullptr) {
continue;
}

const auto exists_ptr = exists_fn->get_function();
if (std::string_view{m.get_name()} == "exists") {
if (auto func = m.get_function(); func != nullptr) {
candidates.push_back((uintptr_t)func);
}
}
}

if (exists_ptr == nullptr) {
spdlog::error("[LooseFileLoader] Failed to get function pointer for via.io.file.exists(System.String)");
return std::nullopt;
if (candidates.empty()) {
spdlog::error("[LooseFileLoader] Failed to find via.io.file.exists methods");
}

return (uintptr_t)exists_ptr;
return candidates;
}();

const auto game_module = utility::get_executable();
Expand Down Expand Up @@ -224,7 +247,7 @@ void LooseFileLoader::hook() {
});
};

if (!initial_candidate) {
if (initial_candidates.empty()) {
// Basically what we're doing here is finding an initial "mov r8d, 800h"
// and then finding a "mov r8d, 400h" in the function, as well as another "mov r8d, 800h"
// I call this a landmark scan, where we find a sequence of instructions that are unique to the function
Expand Down Expand Up @@ -263,7 +286,13 @@ void LooseFileLoader::hook() {
}
}
} else {
check_fn(initial_candidate.value());
for (const auto& c : initial_candidates) {
check_fn(c);

if (candidate) {
break;
}
}
}

if (!candidate) {
Expand Down
7 changes: 6 additions & 1 deletion src/mods/LooseFileLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ class LooseFileLoader : public Mod {
void on_frame() override;
void on_draw_ui() override;

private:
void hook();

bool is_enabled() const {
return m_enabled->value();
}

private:
bool handle_path(const wchar_t* path, size_t hash);

#if TDB_VER > 67
Expand Down
1 change: 1 addition & 0 deletions src/mods/REFrameworkConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

class REFrameworkConfig : public Mod {
public:
static inline constexpr std::string_view REFRAMEWORK_CONFIG_NAME{ "re2_fw_config.txt" };
static std::shared_ptr<REFrameworkConfig>& get();

public:
Expand Down

0 comments on commit 5201a14

Please sign in to comment.