mirror of
https://gitea.tendokyu.moe/TeamTofuShop/segatools.git
synced 2026-09-22 22:37:59 +03:00
IO: Resolve IO DLL paths from environment variables (#119)
## Summary Adds a unified mechanism for locating third-party IO DLLs through environment variables, on top of the existing per-game `[xxxio] path=` INI setting. ## Changes For each IO DLL, the path is resolved in this order: 1. The section-specific INI value, e.g. `[mai2io] path=`. 2. The section-specific environment variable, e.g. `SEGATOOLS_MAI2IO_PATH`. 3. `SEGATOOLS_IO_ROOT` joined with the default IO DLL filename, e.g. `%SEGATOOLS_IO_ROOT%\mai2io.dll`. 4. Empty path, which keeps the built-in IO implementation (unchanged default). INI and environment-variable paths are run through Windows environment-variable expansion, so values such as `%SEGATOOLS_IO_ROOT%\mai2io.dll` are valid in the INI directly. Reviewed-on: https://gitea.tendokyu.moe/TeamTofuShop/segatools/pulls/119 Co-authored-by: グローランプ <130208311+Gl0w1amp@users.noreply.github.com> Co-committed-by: グローランプ <130208311+Gl0w1amp@users.noreply.github.com>
This commit is contained in:
+14
-11
@@ -11,6 +11,7 @@
|
||||
#include "board/vfd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/io-path.h"
|
||||
|
||||
// Check windows
|
||||
#if _WIN32 || _WIN64
|
||||
@@ -44,24 +45,26 @@ static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *fil
|
||||
// Always empty, due to amdaemon being 64 bit in 32 bit mode
|
||||
memset(cfg->path, 0, sizeof(cfg->path));
|
||||
#elif defined(ENV64BIT)
|
||||
GetPrivateProfileStringW(
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"",
|
||||
io_path_config_load(
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"SEGATOOLS_AIMEIO_PATH",
|
||||
L"aimeio.dll",
|
||||
filename);
|
||||
#else
|
||||
#error "Unknown environment"
|
||||
#endif
|
||||
} else {
|
||||
GetPrivateProfileStringW(
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
io_path_config_load(
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
L"aimeio",
|
||||
L"path",
|
||||
L"SEGATOOLS_AIMEIO_PATH",
|
||||
L"aimeio.dll",
|
||||
filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/y3.h"
|
||||
#include "hooklib/y3-dll.h"
|
||||
#include "util/io-path.h"
|
||||
|
||||
void dvd_config_load(struct dvd_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
@@ -158,12 +159,13 @@ void y3_dll_config_load(
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"y3io",
|
||||
L"path",
|
||||
L"",
|
||||
io_path_config_load(
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
L"y3io",
|
||||
L"path",
|
||||
L"SEGATOOLS_Y3IO_PATH",
|
||||
L"y3io.dll",
|
||||
filename);
|
||||
}
|
||||
|
||||
@@ -243,4 +245,4 @@ void y3_config_load(
|
||||
filename);
|
||||
|
||||
wcstombs(cfg->target_code_printer, tmpstr, sizeof(cfg->target_code_printer) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/io-path.h"
|
||||
|
||||
static bool io_path_expand(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *path,
|
||||
const wchar_t *source);
|
||||
static bool io_path_from_root(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *root_filename);
|
||||
static bool io_path_is_separator(wchar_t ch);
|
||||
|
||||
bool io_path_config_load(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *section,
|
||||
const wchar_t *key,
|
||||
const wchar_t *env_name,
|
||||
const wchar_t *root_filename,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
DWORD count;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(out_count > 0);
|
||||
assert(section != NULL);
|
||||
assert(key != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
out[0] = L'\0';
|
||||
|
||||
count = GetPrivateProfileStringW(
|
||||
section,
|
||||
key,
|
||||
L"",
|
||||
path,
|
||||
_countof(path),
|
||||
filename);
|
||||
|
||||
if (count >= _countof(path) - 1 && path[0] != L'\0') {
|
||||
dprintf("IO path: INI value [%S] %S may be truncated "
|
||||
"(exceeds %d characters)\n",
|
||||
section,
|
||||
key,
|
||||
(int) _countof(path) - 1);
|
||||
}
|
||||
|
||||
if (path[0] != L'\0') {
|
||||
return io_path_expand(out, out_count, path, key);
|
||||
}
|
||||
|
||||
if (env_name != NULL) {
|
||||
count = GetEnvironmentVariableW(env_name, path, _countof(path));
|
||||
|
||||
if (count >= _countof(path)) {
|
||||
dprintf("IO path: Environment variable too long: %S\n", env_name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count != 0 && path[0] != L'\0') {
|
||||
return io_path_expand(out, out_count, path, env_name);
|
||||
}
|
||||
}
|
||||
|
||||
return io_path_from_root(out, out_count, root_filename);
|
||||
}
|
||||
|
||||
static bool io_path_expand(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *path,
|
||||
const wchar_t *source)
|
||||
{
|
||||
DWORD expanded;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(out_count > 0);
|
||||
assert(path != NULL);
|
||||
assert(source != NULL);
|
||||
|
||||
expanded = ExpandEnvironmentStringsW(path, out, (DWORD) out_count);
|
||||
|
||||
if (expanded == 0) {
|
||||
dprintf("IO path: Failed to expand path from %S: %lx\n",
|
||||
source,
|
||||
GetLastError());
|
||||
out[0] = L'\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expanded > out_count) {
|
||||
dprintf("IO path: Expanded path from %S is too long: %S\n",
|
||||
source,
|
||||
path);
|
||||
out[0] = L'\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool io_path_from_root(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *root_filename)
|
||||
{
|
||||
wchar_t root[MAX_PATH];
|
||||
wchar_t expanded_root[MAX_PATH];
|
||||
size_t root_len;
|
||||
size_t file_len;
|
||||
DWORD count;
|
||||
|
||||
assert(out != NULL);
|
||||
assert(out_count > 0);
|
||||
|
||||
if (root_filename == NULL || root_filename[0] == L'\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
count = GetEnvironmentVariableW(
|
||||
L"SEGATOOLS_IO_ROOT",
|
||||
root,
|
||||
_countof(root));
|
||||
|
||||
if (count >= _countof(root)) {
|
||||
dprintf("IO path: SEGATOOLS_IO_ROOT is too long\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count == 0 || root[0] == L'\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!io_path_expand(
|
||||
expanded_root,
|
||||
_countof(expanded_root),
|
||||
root,
|
||||
L"SEGATOOLS_IO_ROOT")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
root_len = wcslen(expanded_root);
|
||||
file_len = wcslen(root_filename);
|
||||
|
||||
if (root_len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root_len + file_len + 2 > out_count) {
|
||||
dprintf("IO path: SEGATOOLS_IO_ROOT path is too long: %S\\%S\n",
|
||||
expanded_root,
|
||||
root_filename);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wcscpy_s(out, out_count, expanded_root);
|
||||
|
||||
if (!io_path_is_separator(out[root_len - 1])) {
|
||||
out[root_len++] = L'\\';
|
||||
out[root_len] = L'\0';
|
||||
}
|
||||
|
||||
wcscat_s(out, out_count, root_filename);
|
||||
|
||||
/* The SEGATOOLS_IO_ROOT fallback is broadcast to every hook at once, so a
|
||||
single global setting is shared by games that may not each ship an IO
|
||||
DLL in that directory. Unlike an explicit INI/env path (direct user
|
||||
intent, allowed to fail loudly at load time), a synthesized root path
|
||||
that does not exist should quietly fall back to the built-in IO
|
||||
implementation instead of aborting hook startup. */
|
||||
if (GetFileAttributesW(out) == INVALID_FILE_ATTRIBUTES) {
|
||||
dprintf("IO path: SEGATOOLS_IO_ROOT DLL not found, "
|
||||
"using built-in IO: %S\n",
|
||||
out);
|
||||
out[0] = L'\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool io_path_is_separator(wchar_t ch)
|
||||
{
|
||||
return ch == L'\\' || ch == L'/';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
bool io_path_config_load(
|
||||
wchar_t *out,
|
||||
size_t out_count,
|
||||
const wchar_t *section,
|
||||
const wchar_t *key,
|
||||
const wchar_t *env_name,
|
||||
const wchar_t *root_filename,
|
||||
const wchar_t *filename);
|
||||
@@ -21,6 +21,8 @@ util_lib = static_library(
|
||||
'fg-detect.h',
|
||||
'get_function_ordinal.c',
|
||||
'get_function_ordinal.h',
|
||||
'io-path.c',
|
||||
'io-path.h',
|
||||
'lib.c',
|
||||
'lib.h',
|
||||
'slurp.c',
|
||||
|
||||
Reference in New Issue
Block a user