Resolve relative bezel artwork paths against the exe directory

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-09-25 22:27:47 -07:00
parent 4d7311c8cc
commit f9109e21c6
4 changed files with 40 additions and 8 deletions
+7
View File
@@ -603,6 +603,13 @@ remain black. Enabling the feature without choosing artwork is also a no-op.
The package supplies only the declaration and trusted plugin selection; archives
still cannot load native code.
A title's own trusted plugin can call `psx_mod_set_bezel_artwork(path)` directly
to ship its artwork. An absolute path (such as the built-in package's selected
resource) is used as-is; a relative path like `"bezels/qirex.png"` is resolved
against the executable's directory when the artwork loads, never the current
working directory, so artwork the title stages beside its binary is found
however the game was launched.
### Retained-scene loading presentation (native-wide opt-in)
A trusted game plugin can register
+4
View File
@@ -231,6 +231,10 @@ int psx_mod_set_auto_skip_fmv(int enabled);
* Draw still artwork behind the game image in OpenGL letterbox/pillarbox
* margins. The image path is an owner-selected mod resource; with no enabled
* mod/resource path, the margins remain the historical black clear.
* An absolute path is used unchanged. A relative path (e.g. "bezels/x.png"
* for artwork a title stages beside its binary) is resolved against the
* executable's directory, never the current working directory, when the
* artwork is loaded.
*/
int psx_mod_set_bezel_artwork(const char* path);
+15 -7
View File
@@ -15240,18 +15240,26 @@ session_reboot:
/* Bezel artwork (Mods): load after the GL context exists. */
if (!g_bezel_path.empty() && g_gl_active) {
std::filesystem::path bp(g_bezel_path);
/* A relative artwork path names a file shipped beside the
* executable (e.g. a title's staged bezels/), so anchor it on the
* exe directory -- never cwd (see exe_dir_from_argv). An absolute
* path, such as the builtin bezel's owner-selected resource, is
* used unchanged. */
const std::filesystem::path bp = PSXRecompV4::host_resolve(
exe_dir_from_argv(argv[0]), std::filesystem::path(g_bezel_path));
std::vector<unsigned char> file;
if (FILE *bf = std::fopen(bp.string().c_str(), "rb")) {
std::fseek(bf, 0, SEEK_END);
const long len = std::ftell(bf);
std::fseek(bf, 0, SEEK_SET);
/* fs::path stream: the exe directory is a wide Windows path, which
* a narrow fopen() of bp.string() cannot open when it is non-ASCII. */
std::ifstream bf(bp, std::ios::binary | std::ios::ate);
if (bf.is_open()) {
const std::streamoff len = bf.tellg();
if (len > 0) {
file.resize((size_t)len);
if (std::fread(file.data(), 1, file.size(), bf) != file.size())
bf.seekg(0, std::ios::beg);
if (!bf.read(reinterpret_cast<char*>(file.data()),
(std::streamsize)file.size()))
file.clear();
}
std::fclose(bf);
}
int bw = 0, bh = 0, bc = 0;
unsigned char *px = file.empty() ? nullptr
+14 -1
View File
@@ -9,7 +9,8 @@ MAIN = (ROOT / "runtime/src/main.cpp").read_text(encoding="utf-8")
HEADER = (ROOT / "runtime/include/mod_plugins.h").read_text(encoding="utf-8")
MOD_RUNTIME = (ROOT / "runtime/src/mod_runtime.cpp").read_text(encoding="utf-8")
BUILTIN = (ROOT / "runtime/src/mod_builtin_bezel.c").read_text(encoding="utf-8")
CONFIG_H = (ROOT / "recompiler/src/config_loader.h").read_text(encoding="utf-8")
HOST_PATH = (ROOT / "recompiler/include/host_path.h").read_text(encoding="utf-8")
CONFIG_H =(ROOT / "recompiler/src/config_loader.h").read_text(encoding="utf-8")
CONFIG_CPP = (ROOT / "recompiler/src/config_loader.cpp").read_text(
encoding="utf-8"
)
@@ -26,6 +27,18 @@ assert "psx_mod_current_resource_path" in HEADER
assert "psx_mod_set_bezel_artwork" in MAIN
assert "g_video_renderer = 1;" in MAIN
assert "mod-owned OpenGL margin artwork" in MAIN
# Relative artwork paths resolve against the exe directory at load time, never
# cwd; absolute paths (the builtin resource) pass through host_resolve as-is.
LOADER = MAIN[MAIN.index("/* Bezel artwork (Mods): load after the GL context exists. */"):]
LOADER = LOADER[: LOADER.index("gl_renderer_set_bezel(")]
assert (
"PSXRecompV4::host_resolve(\n"
" exe_dir_from_argv(argv[0]), std::filesystem::path(g_bezel_path));"
) in LOADER
assert "host_path_is_absolute(path) ? path : root / path" in HOST_PATH
assert "std::fopen" not in LOADER
assert "std::filesystem::path bp(g_bezel_path)" not in MAIN
assert "resolved against the\n * executable's directory, never the current working directory" in HEADER
assert "current_plugin" in MOD_RUNTIME
assert 'psx_mod_current_resource_path("artwork"' in BUILTIN
assert 'psx_mod_register_activation_plugin("psx.bezel"' in BUILTIN