Skip to content

Commit 8a57820

Browse files
authored
Merge pull request #14 from DFHack/resteam
Add support for redirected DLLs using .ini files
2 parents 4c48e25 + 2d84a58 commit 8a57820

3 files changed

Lines changed: 98 additions & 44 deletions

File tree

ChainLoader.cpp

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,64 @@
22

33
#include <filesystem>
44
#include <queue>
5+
#include <fstream>
6+
#include <string_view>
57

68
using std::string;
79
using std::vector;
810

9-
#if _WIN32
10-
static const string search_prefix = "dfhooks_";
11-
static const string search_ext = ".dll";
12-
#else
13-
static const string search_prefix = "libdfhooks_";
14-
# if __APPLE__
15-
static const string search_ext = ".dylib";
16-
# else
17-
static const string search_ext = ".so";
18-
# endif
19-
#endif
11+
using std::string_view_literals::operator""sv;
12+
13+
// look for libraries with these prefixes and extensions.
14+
// .ini files are expected to contain a single line with the path to the library to load, which allows for loading libraries with non-standard names or from non-standard locations
15+
// note that if both a .ini and a .dll/.so are present _both_ will be loaded, which may have unintended consequences if they point to the same library, so use with caution
16+
17+
constexpr auto search_prefix = {"dfhooks_"sv, "libdfhooks_"sv};
18+
constexpr auto search_ext = {".dll"sv, ".so"sv};
19+
constexpr auto search_ext_ini = ".ini"sv;
2020

2121
ChainLoader::ChainLoader() {
2222
auto cmp = [](const LibWrapper * lhs, const LibWrapper * rhs) { return lhs->priority < rhs->priority; };
2323
std::priority_queue<LibWrapper*, vector<LibWrapper*>, decltype(cmp)> priq(cmp);
2424

25-
for (auto const& dir_entry : std::filesystem::directory_iterator{"."}) {
25+
for (auto const& dir_entry : std::filesystem::directory_iterator{"."})
26+
{
2627
auto fname = dir_entry.path().filename();
27-
if (fname.extension() != search_ext || fname.stem().string().find(search_prefix) != 0)
28-
continue;
29-
auto wrapper = new LibWrapper(fname.string());
30-
if (wrapper->handle)
31-
priq.emplace(wrapper);
32-
else
33-
delete wrapper;
28+
bool fnd = false;
29+
for (auto& prefix : search_prefix) {
30+
if (fname.stem().string().find(prefix) == 0) {
31+
fnd = true;
32+
break;
33+
}
34+
}
35+
if (!fnd) continue;
36+
37+
if (fname.extension() == search_ext_ini)
38+
{
39+
std::string indirection;
40+
std::ifstream ini_file{fname};
41+
std::getline(ini_file, indirection);
42+
if (!indirection.empty())
43+
{
44+
std::filesystem::path lib{indirection};
45+
auto wrapper = new LibWrapper(lib);
46+
if (wrapper->handle)
47+
priq.emplace(wrapper);
48+
else
49+
delete wrapper;
50+
}
51+
}
52+
else for (auto& ext : search_ext)
53+
{
54+
if (fname.extension() == ext)
55+
{
56+
auto wrapper = new LibWrapper(fname);
57+
if (wrapper->handle)
58+
priq.emplace(wrapper);
59+
else
60+
delete wrapper;
61+
}
62+
}
3463
}
3564
while (!priq.empty()) {
3665
auto lib = priq.top();

LibWrapper.cpp

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
#include "LibWrapper.h"
22

3+
#include <filesystem>
4+
#include <iostream>
5+
36
using std::string;
7+
using std::filesystem::path;
48

59
#if _WIN32
610
# include <windows.h>
11+
# include <libloaderapi.h>
712
#else
813
# include <dlfcn.h>
914
#endif
1015

11-
static void* open_library(const string& fname) {
16+
static void* open_library(std::filesystem::path fname) {
1217
#if _WIN32
13-
return LoadLibrary(fname.c_str());
18+
std::filesystem::path path = std::filesystem::canonical(fname);
19+
std::filesystem::path ppath = path.parent_path();
20+
[[maybe_unused]] DLL_DIRECTORY_COOKIE cookie = AddDllDirectory(ppath.wstring().c_str());
21+
auto handle = LoadLibraryExW(path.filename().wstring().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
22+
return handle;
1423
#else
1524
return dlopen(fname.c_str(), RTLD_LAZY);
1625
#endif
@@ -35,21 +44,34 @@ static void* load_sym(void* handle, const char* sym) {
3544
#endif
3645
}
3746

38-
LibWrapper::LibWrapper(const string& fname) {
47+
extern "C" void dfhooks_init();
48+
49+
LibWrapper::LibWrapper(const std::filesystem::path& fname) {
3950
handle = open_library(fname);
4051
if (!handle)
4152
return;
4253

4354
int32_t * priority_sym = (int *)load_sym(handle, "dfhooks_priority");
4455
if (priority_sym) priority = *priority_sym;
4556

46-
init = (dfhooks_init_fn)load_sym(handle, "dfhooks_init");
47-
shutdown = (dfhooks_shutdown_fn)load_sym(handle, "dfhooks_shutdown");
48-
update = (dfhooks_update_fn)load_sym(handle, "dfhooks_update");
49-
prerender = (dfhooks_prerender_fn)load_sym(handle, "dfhooks_prerender");
50-
sdl_event = (dfhooks_sdl_event_fn)load_sym(handle, "dfhooks_sdl_event");
51-
sdl_loop = (dfhooks_sdl_loop_fn)load_sym(handle, "dfhooks_sdl_loop");
52-
ncurses_key = (dfhooks_ncurses_key_fn)load_sym(handle, "dfhooks_ncurses_key");
57+
preinit = (dfhooks_preinit_fn*)load_sym(handle, "dfhooks_preinit");
58+
init = (dfhooks_init_fn*)load_sym(handle, "dfhooks_init");
59+
shutdown = (dfhooks_shutdown_fn*)load_sym(handle, "dfhooks_shutdown");
60+
update = (dfhooks_update_fn*)load_sym(handle, "dfhooks_update");
61+
prerender = (dfhooks_prerender_fn*)load_sym(handle, "dfhooks_prerender");
62+
sdl_event = (dfhooks_sdl_event_fn*)load_sym(handle, "dfhooks_sdl_event");
63+
sdl_loop = (dfhooks_sdl_loop_fn*)load_sym(handle, "dfhooks_sdl_loop");
64+
ncurses_key = (dfhooks_ncurses_key_fn*)load_sym(handle, "dfhooks_ncurses_key");
65+
66+
if (preinit)
67+
preinit(fname);
68+
69+
if (init == &dfhooks_init)
70+
{
71+
// if the library exports the same init function as the main dfhooks, initing the library will can an infinite recursion. disallow this.
72+
close_library(handle);
73+
handle = nullptr;
74+
}
5375
}
5476

5577
LibWrapper::~LibWrapper() {

LibWrapper.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
#pragma once
22

33
#include <string>
4+
#include <filesystem>
45

56
union SDL_Event;
67

7-
typedef void (*dfhooks_init_fn)();
8-
typedef void (*dfhooks_shutdown_fn)();
9-
typedef void (*dfhooks_update_fn)();
10-
typedef void (*dfhooks_prerender_fn)();
11-
typedef bool (*dfhooks_sdl_event_fn)(SDL_Event* event);
12-
typedef void (*dfhooks_sdl_loop_fn)();
13-
typedef bool (*dfhooks_ncurses_key_fn)(int key);
8+
using dfhooks_preinit_fn = auto (std::filesystem::path) -> void;
9+
using dfhooks_init_fn = auto () -> void;
10+
using dfhooks_shutdown_fn = auto () -> void;
11+
using dfhooks_update_fn = auto () -> void;
12+
using dfhooks_prerender_fn = auto () -> void;
13+
using dfhooks_sdl_event_fn = auto (SDL_Event* event) -> bool;
14+
using dfhooks_sdl_loop_fn = auto () -> void;
15+
using dfhooks_ncurses_key_fn = auto (int key) -> bool;
1416

1517
struct LibWrapper {
1618
void* handle = nullptr;
1719
int32_t priority = 0;
18-
dfhooks_init_fn init = nullptr;
19-
dfhooks_shutdown_fn shutdown = nullptr;
20-
dfhooks_update_fn update = nullptr;
21-
dfhooks_prerender_fn prerender = nullptr;
22-
dfhooks_sdl_event_fn sdl_event = nullptr;
23-
dfhooks_sdl_loop_fn sdl_loop = nullptr;
24-
dfhooks_ncurses_key_fn ncurses_key = nullptr;
20+
dfhooks_preinit_fn* preinit = nullptr;
21+
dfhooks_init_fn* init = nullptr;
22+
dfhooks_shutdown_fn* shutdown = nullptr;
23+
dfhooks_update_fn* update = nullptr;
24+
dfhooks_prerender_fn* prerender = nullptr;
25+
dfhooks_sdl_event_fn* sdl_event = nullptr;
26+
dfhooks_sdl_loop_fn* sdl_loop = nullptr;
27+
dfhooks_ncurses_key_fn* ncurses_key = nullptr;
2528

26-
LibWrapper(const std::string& fname);
29+
LibWrapper(const std::filesystem::path& fname);
2730
virtual ~LibWrapper();
2831
};

0 commit comments

Comments
 (0)