Skip to content

Commit 5201a14

Browse files
committed
LooseFileLoader: Hook extremely early to intercept files
1 parent 91b03ad commit 5201a14

5 files changed

+73
-24
lines changed

src/Mods.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ std::optional<std::string> Mods::on_initialize() const {
8282
}
8383
}
8484

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

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

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

101101
// once here to at least setup the values
102102
for (auto& mod : m_mods) {

src/REFramework.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extern "C" {
2828
#include "utility/Thread.hpp"
2929

3030
#include "Mods.hpp"
31+
#include "mods/LooseFileLoader.hpp"
3132
#include "mods/PluginLoader.hpp"
3233
#include "sdk/REGlobals.hpp"
3334
#include "sdk/Application.hpp"
@@ -43,7 +44,6 @@ extern "C" {
4344
namespace fs = std::filesystem;
4445
using namespace std::literals;
4546

46-
4747
std::unique_ptr<REFramework> g_framework{};
4848

4949
void REFramework::hook_monitor() {
@@ -539,6 +539,20 @@ REFramework::REFramework(HMODULE reframework_module)
539539
bool found_renderer = false;
540540
bool renderer_has_render_frame_fn = false;
541541

542+
if (sdk::RETypeDB::get() != nullptr) {
543+
auto& loader = LooseFileLoader::get(); // Initialize this really early
544+
545+
const auto config_path = get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string();
546+
if (fs::exists(utility::widen(config_path))) {
547+
utility::Config cfg{ config_path };
548+
loader->on_config_load(cfg);
549+
}
550+
551+
if (loader->is_enabled()) {
552+
loader->hook();
553+
}
554+
}
555+
542556
while (true) try {
543557
const auto tdb = sdk::RETypeDB::get();
544558

@@ -827,7 +841,7 @@ void REFramework::on_frame_d3d11() {
827841
if (is_init_ok) {
828842
// Write default config once if it doesn't exist.
829843
if (!std::exchange(m_created_default_cfg, true)) {
830-
if (!fs::exists({utility::widen(get_persistent_dir("re2_fw_config.txt").string())})) {
844+
if (!fs::exists({utility::widen(get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())})) {
831845
save_config();
832846
}
833847
}
@@ -932,7 +946,7 @@ void REFramework::on_frame_d3d12() {
932946
if (is_init_ok) {
933947
// Write default config once if it doesn't exist.
934948
if (!std::exchange(m_created_default_cfg, true)) {
935-
if (!fs::exists({utility::widen(get_persistent_dir("re2_fw_config.txt").string())})) {
949+
if (!fs::exists({utility::widen(get_persistent_dir(REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())})) {
936950
save_config();
937951
}
938952
}
@@ -1313,7 +1327,7 @@ std::filesystem::path REFramework::get_persistent_dir() {
13131327
void REFramework::save_config() {
13141328
std::scoped_lock _{m_config_mtx};
13151329

1316-
spdlog::info("Saving config re2_fw_config.txt");
1330+
spdlog::info("Saving config {}", REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data());
13171331

13181332
utility::Config cfg{};
13191333

@@ -1322,7 +1336,7 @@ void REFramework::save_config() {
13221336
}
13231337

13241338
try {
1325-
if (!cfg.save((get_persistent_dir() / "re2_fw_config.txt").string())) {
1339+
if (!cfg.save((get_persistent_dir() / REFrameworkConfig::REFRAMEWORK_CONFIG_NAME.data()).string())) {
13261340
spdlog::error("Failed to save config");
13271341
return;
13281342
}

src/mods/LooseFileLoader.cpp

+45-16
Original file line numberDiff line numberDiff line change
@@ -161,30 +161,53 @@ void LooseFileLoader::hook() {
161161
return;
162162
}
163163

164-
auto initial_candidate = [&]() -> std::optional<uintptr_t> {
165-
const auto via_io_file = tdb->find_type("via.io.file");
164+
auto initial_candidates = [&]() -> std::vector<uintptr_t> {
165+
sdk::RETypeDefinition* via_io_file = nullptr;
166+
167+
// We need to look for via.io.file manually because LooseFileLoader gets loaded extremely early
168+
// meaning VM stuff may not work correctly
169+
for (auto i = 0; i < tdb->get_num_types(); ++i) {
170+
const auto t = tdb->get_type(i);
171+
172+
if (t == nullptr || t->get_name() == nullptr) {
173+
continue;
174+
}
175+
176+
if (std::string_view{t->get_name()} == "file") {
177+
if (t->get_declaring_type() != nullptr && std::string_view{t->get_declaring_type()->get_name()} == "io") {
178+
via_io_file = t;
179+
break;
180+
}
181+
}
182+
}
166183

167184
if (via_io_file == nullptr) {
168185
spdlog::error("[LooseFileLoader] Failed to find via.io.file");
169-
return std::nullopt;
186+
return {};
170187
}
171188

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

175-
if (exists_fn == nullptr) {
176-
spdlog::error("[LooseFileLoader] Failed to find via.io.file.exists(System.String)");
177-
return std::nullopt;
178-
}
191+
std::vector<uintptr_t> candidates{};
192+
193+
// Same reason as above, manually loop through methods because VM stuff may not work correctly
194+
for (auto& m : via_io_file->get_methods()) {
195+
if (m.get_name() == nullptr) {
196+
continue;
197+
}
179198

180-
const auto exists_ptr = exists_fn->get_function();
199+
if (std::string_view{m.get_name()} == "exists") {
200+
if (auto func = m.get_function(); func != nullptr) {
201+
candidates.push_back((uintptr_t)func);
202+
}
203+
}
204+
}
181205

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

187-
return (uintptr_t)exists_ptr;
210+
return candidates;
188211
}();
189212

190213
const auto game_module = utility::get_executable();
@@ -224,7 +247,7 @@ void LooseFileLoader::hook() {
224247
});
225248
};
226249

227-
if (!initial_candidate) {
250+
if (initial_candidates.empty()) {
228251
// Basically what we're doing here is finding an initial "mov r8d, 800h"
229252
// and then finding a "mov r8d, 400h" in the function, as well as another "mov r8d, 800h"
230253
// I call this a landmark scan, where we find a sequence of instructions that are unique to the function
@@ -263,7 +286,13 @@ void LooseFileLoader::hook() {
263286
}
264287
}
265288
} else {
266-
check_fn(initial_candidate.value());
289+
for (const auto& c : initial_candidates) {
290+
check_fn(c);
291+
292+
if (candidate) {
293+
break;
294+
}
295+
}
267296
}
268297

269298
if (!candidate) {

src/mods/LooseFileLoader.hpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ class LooseFileLoader : public Mod {
2323
void on_frame() override;
2424
void on_draw_ui() override;
2525

26-
private:
2726
void hook();
27+
28+
bool is_enabled() const {
29+
return m_enabled->value();
30+
}
31+
32+
private:
2833
bool handle_path(const wchar_t* path, size_t hash);
2934

3035
#if TDB_VER > 67

src/mods/REFrameworkConfig.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

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

910
public:

0 commit comments

Comments
 (0)