Files
spice2x.github.io/src/spice2x/avs/game.cpp
T
Will 37218e7fe0 Try and preserve the wideness of std::filesystem::path more (#660)
As noted in #567, a filesystem path that contains non-ascii will break a
lot if using a clang toolchain.

Luckily, fmtlib has a lossy utf8 convert when you use it to print a path
(after including `fmt/std.h`). The vast majority of this diff is just
removing `.string()` from paths inside loggings calls.

There are some callsites I _didn't_ touch, mainly the options, because
it would be an ABI break to change those to be wide strings and I cbf
looking into settings upgrades. There are also some spots (avs mountpath
remapping, for example) where the path is guaranteed to be ascii, so I
didn't modify them.

ImGui doesn't appear to easily support wide strings (I mean, surely it
does, but I'm not gonna look too far into it) so I mostly just left
those alone too, with a few spots modified to re-use fmtlib's lossy
utf8.

Some of the changes are basically never gonna be hit IRL, like who would
put a file with a non-ascii _extension_ along with their modules? But
the diff is (I hope) pretty easy to validate as OK.

Testing has been somewhat minimal, I fired up the GCC build of spice2x
in a dodgy folder name, got mojibake (running via wine in linux so take
that as you will), ran the unmodified clang spice and crashed the same
way the reporter did. After modification, I get the exact same mojibake
so I assume if the terminal enjoys utf8 it'll display OK.

Claude (only used for review) thinks the commit is fine but is annoyed
that I use `fmt::detail` in the appdata censoring, which is part of the
private API; personally I don't care because it's pretty stable.
2026-04-27 20:07:15 -07:00

153 lines
5.7 KiB
C++

#include "game.h"
#include "launcher/launcher.h"
#include "util/fileutils.h"
#include "util/libutils.h"
#include "util/logging.h"
namespace avs {
namespace game {
// function names
const char ENTRY_INIT_NAME[] = "dll_entry_init";
const char ENTRY_MAIN_NAME[] = "dll_entry_main";
// functions
typedef bool (*ENTRY_INIT_T)(char *, void *);
typedef void (*ENTRY_MAIN_T)(void);
ENTRY_INIT_T dll_entry_init;
ENTRY_MAIN_T dll_entry_main;
// properties
char MODEL[4] = {'0', '0', '0', '\x00'};
char DEST[2] = {'0', '\x00'};
char SPEC[2] = {'0', '\x00'};
char REV[2] = {'0', '\x00'};
char EXT[11] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '\x00'};
// handle
HINSTANCE DLL_INSTANCE;
std::string DLL_NAME;
bool is_model(const char *model) {
return _stricmp(MODEL, model) == 0;
}
bool is_model(const char *model, const char *ext) {
return is_model(model) && is_ext(ext);
}
bool is_model(const std::initializer_list<const char *> model_list) {
for (auto &model : model_list) {
if (is_model(model)) {
return true;
}
}
return false;
}
bool is_ext(const char *ext) {
return _stricmp(EXT, ext) == 0;
}
bool is_ext(int datecode_min, int datecode_max) {
// range check
long datecode = strtol(EXT, NULL, 10);
return datecode_min <= datecode && datecode <= datecode_max;
}
std::string get_identifier() {
return fmt::format("{}:{}:{}:{}:{}",
avs::game::MODEL,
avs::game::DEST,
avs::game::SPEC,
avs::game::REV,
avs::game::EXT);
}
void load_dll() {
log_info("avs-game", "loading DLL '{}'", DLL_NAME);
// load game instance
const auto dll_path = MODULE_PATH / DLL_NAME;
log_info("avs-game", "DLL path: {}", dll_path);
// MAX_PATH is 260
if (const auto dll_path_len = dll_path.wstring().length(); 130 <= dll_path_len) {
log_warning(
"avs-game",
"PATH TOO LONG WARNING\n\n"
"-------------------------------------------------------------------\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
" PATH TOO LONG \n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"The path '{}'\n"
" has a length of {}\n"
"Most of these games may behave unexpectedly when the path is too\n"
"long, often resulting in random crashes. Move the game contents to\n"
"a directory with shorter path.\n"
"-------------------------------------------------------------------\n\n",
dll_path, dll_path_len);
}
// ddr gamemdx.dll user error
if (avs::game::is_model("MDX") && DLL_NAME == "gamemdx.dll") {
log_warning(
"ddr",
"BAD GAME DLL ERROR\n\n"
"!!! !!!\n"
"!!! -exec gamemdx.dll was specified !!!\n"
"!!! this is the wrong DLL; the game will not load !!!\n"
"!!! remove -exec argument and try again. !!!\n"
"!!! !!!\n"
);
log_fatal("ddr", "BAD GAME DLL ERROR (don't use -exec gamemdx.dll, that's the wrong DLL)");
}
// file not found on disk
if (!fileutils::file_exists(dll_path)) {
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path);
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
}
if (fileutils::verify_header_pe(dll_path)) {
DLL_INSTANCE = libutils::load_library(dll_path);
}
// load entry points
dll_entry_init = (ENTRY_INIT_T) libutils::get_proc(DLL_INSTANCE, ENTRY_INIT_NAME);
dll_entry_main = (ENTRY_MAIN_T) libutils::get_proc(DLL_INSTANCE, ENTRY_MAIN_NAME);
log_info("avs-game", "loaded {} successfully ({})", DLL_NAME, fmt::ptr(DLL_INSTANCE));
}
bool entry_init(char *sid_code, void *app_param) {
auto current_entry_init = (ENTRY_INIT_T) libutils::get_proc(DLL_INSTANCE, ENTRY_INIT_NAME);
if (dll_entry_init != current_entry_init) {
log_info("avs-game", "dll_entry_init is hooked");
dll_entry_init = current_entry_init;
}
return dll_entry_init(sid_code, app_param);
}
void entry_main() {
auto current_entry_main = (ENTRY_MAIN_T) libutils::get_proc(DLL_INSTANCE, ENTRY_MAIN_NAME);
if (dll_entry_main != current_entry_main) {
log_info("avs-game", "dll_entry_main is hooked");
dll_entry_main = current_entry_main;
}
dll_entry_main();
}
}
}