Keep fakelib2 exclusive from global and emulator overlays

This commit is contained in:
Roman Tarasov committed 2026-08-24 22:06:20 +03:00
1 parent 757f4514cc
commit 3190100de8
3 files changed
+43 -25

No files matched your search

+5 -5
View File
@@ -79,11 +79,11 @@ Supported keys (all optional):
- `legacy_mount_pfsc=1|0` (outer `.ffpfsc`; `1` selects the version 1.6 mount profile; default: `1`)
- `nested_pfs_index_cache=1|0` (cache the containing PFS compressed-file index before attaching a nested image in legacy mode; default: `0`; optimized mode always requests it; the old `legacy_gddr5_cache` name remains accepted)
- `backport_fakelib=1|0` (`1` mounts sandbox `fakelib` overlays for running games; default: `1`)
- `update_emulators=1|0` (`1` updates all emulators with matching files in a game's own fakelib; default: `1`)
- `update_emulators=1|0` (`1` updates all emulators with matching files in a game's own fakelib; `fakelib2` is excluded; default: `1`)
- `emulators_path=<absolute_path>` (folder containing emulator update files; default: `/data/shadowmount/emus`)
- `auto_update_ampr=1|0` (check for a new `libSceAmpr.sprx` 30 seconds after startup and every four hours; default: `0`)
- `ampr_update_url=<http_or_https_url>` (AMPR emulator download URL; default: `https://github.com/drakmor/ampr_emu/releases/latest/download/libSceAmpr.sprx`)
- `global_fakelib=1|0` (`1` enables the global fakelib overlay when the folder exists; default: `1`)
- `global_fakelib=1|0` (`1` enables the global fakelib overlay when the folder exists; `fakelib2` remains exclusive; default: `1`)
- `global_fakelib_path=<absolute_path>` (global fakelib folder; default: `/data/shadowmount/fakelib`)
- `global_fakelib_priority=game|global` (overlay priority when both global and game fakelib exist; default: `game`)
- `global_fakelib_exclude=<TITLE_ID>` (repeatable; disables the global fakelib overlay for matching titles)
@@ -187,11 +187,11 @@ Backport overlay behavior:
- The `backports` folder is ignored during normal game scanning.
- A backport is applied automatically to the matching mounted game from any configured scan path.
- If multiple scan paths provide the same title backport, the game's own scan path wins; otherwise scan path order is used.
- ShadowMount+ checks the selected backport for `fakelib2` and then `fakelib`; if neither exists, it uses only `fakelib` from the original game source. The selected directory is mounted into the running game's sandbox `common/lib`.
- If `update_emulators=1`, matching files from `emulators_path` replace files in the selected game fakelib. The cache is refreshed when its sources change and expires after seven days without a game launch.
- ShadowMount+ checks the selected backport for `fakelib2` and then `fakelib`; if neither exists, it uses only `fakelib` from the original game source. The selected directory is mounted into the running game's sandbox `common/lib`. A backport `fakelib2` is always mounted directly and exclusively: neither emulator updates nor the global fakelib can replace or supplement it.
- If `update_emulators=1`, matching files from `emulators_path` replace files in the selected game fakelib, except when `fakelib2` is selected. The cache is refreshed when its sources change and expires after seven days without a game launch.
- With `auto_update_ampr=1`, ShadowMount+ checks for AMPR updates 30 seconds after startup and every four hours. It downloads a missing or newer emulator and displays a notification after a successful update.
- The backport notification adds `Emulators updated` when emulator files are updated for the launched game.
- If both global and per-game fakelib exist, they are combined in the game cache according to `global_fakelib_priority`. Without a per-game fakelib, the global folder is mounted directly.
- If both global and per-game fakelib exist, they are combined in the game cache according to `global_fakelib_priority`, unless the selected backport contains `fakelib2`. Without a per-game fakelib, the global folder is mounted directly.
- Use repeatable `global_fakelib_exclude=<TITLE_ID>` entries to skip the global fakelib for specific games without disabling per-game fakelib.
- `backport_fakelib=0` disables the sandbox `fakelib` watcher, including global fakelib and emulator updates.
- For `backport_fakelib` to work correctly, the standalone `BackPork` payload must be disabled. Running both at the same time will conflict.
+3 -2
View File
@@ -135,7 +135,7 @@
# Updates for multiple emulators in the game fakelib:
# 1/true/yes/on -> cache the game's fakelib before launch and replace matching
# cached files from emulators_path
# cached files from emulators_path; fakelib2 is never changed
# 0/false/no/off -> disable emulator updates
# Default: 1
# update_emulators=1
@@ -159,7 +159,8 @@
# ampr_update_url=https://github.com/drakmor/ampr_emu/releases/latest/download/libSceAmpr.sprx
# Global fakelib overlay:
# 1/true/yes/on -> use global fakelib for every tracked game when present
# 1/true/yes/on -> use global fakelib for every tracked game when present;
# fakelib2 always remains exclusive when present
# 0/false/no/off -> do not use the global fakelib folder
# Default: 1
# global_fakelib=1
+35 -18
View File
@@ -67,6 +67,12 @@ typedef struct {
char emulators_path[MAX_PATH];
} fakelib_cache_context_t;
typedef enum {
FAKELIB_SOURCE_NONE = 0,
FAKELIB_SOURCE_COMPOSABLE,
FAKELIB_SOURCE_FAKELIB2,
} fakelib_source_kind_t;
static fakelib_session_t g_fakelib_mount;
static pthread_mutex_t g_fakelib_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_fakelib_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -207,7 +213,7 @@ static bool resolve_sandbox_mount_path(const char *title_id,
return found;
}
static bool resolve_game_fakelib_source_for_path(
static fakelib_source_kind_t resolve_game_fakelib_source_for_path(
const char *title_id, const char *game_path,
char source_path[MAX_PATH]) {
source_path[0] = '\0';
@@ -219,10 +225,13 @@ static bool resolve_game_fakelib_source_for_path(
const struct {
const char *root;
const char *directory;
fakelib_source_kind_t kind;
} candidates[] = {
{has_backport ? backport_path : NULL, "fakelib2"},
{has_backport ? backport_path : NULL, "fakelib"},
{has_game ? game_path : NULL, "fakelib"},
{has_backport ? backport_path : NULL, "fakelib2",
FAKELIB_SOURCE_FAKELIB2},
{has_backport ? backport_path : NULL, "fakelib",
FAKELIB_SOURCE_COMPOSABLE},
{has_game ? game_path : NULL, "fakelib", FAKELIB_SOURCE_COMPOSABLE},
};
struct stat st;
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
@@ -232,16 +241,17 @@ static bool resolve_game_fakelib_source_for_path(
candidates[i].root, candidates[i].directory);
if (written > 0 && (size_t)written < MAX_PATH &&
stat(source_path, &st) == 0 && S_ISDIR(st.st_mode)) {
return true;
return candidates[i].kind;
}
}
source_path[0] = '\0';
return false;
return FAKELIB_SOURCE_NONE;
}
static bool resolve_game_fakelib_source(const char *title_id,
char source_path[MAX_PATH]) {
static fakelib_source_kind_t
resolve_game_fakelib_source(const char *title_id,
char source_path[MAX_PATH]) {
char game_path[MAX_PATH];
if (!read_mount_link(title_id, game_path, sizeof(game_path)))
game_path[0] = '\0';
@@ -994,8 +1004,9 @@ static void prepare_title_cache(const char *title_id, const char *game_path) {
return;
char game_source_path[MAX_PATH];
if (!resolve_game_fakelib_source_for_path(title_id, game_path,
game_source_path)) {
fakelib_source_kind_t source_kind = resolve_game_fakelib_source_for_path(
title_id, game_path, game_source_path);
if (source_kind != FAKELIB_SOURCE_COMPOSABLE) {
remove_title_cache(title_id);
return;
}
@@ -1151,12 +1162,16 @@ void sm_fakelib_game_on_exec(pid_t pid, const char *title_id,
}
}
char game_source_path[MAX_PATH];
char global_source_path[MAX_PATH];
char mount_path[MAX_PATH];
bool has_game = resolve_game_fakelib_source(title_id, game_source_path);
bool has_global =
resolve_global_fakelib_source(title_id, global_source_path);
char game_source_path[MAX_PATH] = {0};
char global_source_path[MAX_PATH] = {0};
char mount_path[MAX_PATH] = {0};
fakelib_source_kind_t source_kind =
resolve_game_fakelib_source(title_id, game_source_path);
bool has_game = source_kind != FAKELIB_SOURCE_NONE;
bool allows_composition = source_kind != FAKELIB_SOURCE_FAKELIB2;
bool has_global = allows_composition &&
resolve_global_fakelib_source(title_id,
global_source_path);
if (!has_global && !has_game) {
pthread_mutex_unlock(&g_fakelib_mutex);
return;
@@ -1168,7 +1183,7 @@ void sm_fakelib_game_on_exec(pid_t pid, const char *title_id,
size_t emulator_file_count = 0;
bool cache_resolved = false;
bool cache_includes_global = false;
if (has_game) {
if (source_kind == FAKELIB_SOURCE_COMPOSABLE) {
char cache_path[MAX_PATH];
cache_resolved = resolve_cached_fakelib(
title_id, game_source_path, cache_path, &emulator_file_count,
@@ -1198,7 +1213,9 @@ void sm_fakelib_game_on_exec(pid_t pid, const char *title_id,
sizeof(g_fakelib_mount.mount_path));
const char *source_path = has_game ? game_source_path : global_source_path;
const char *label = has_game ? "game" : "global";
const char *label = !allows_composition
? "fakelib2"
: (has_game ? "game" : "global");
if (!track_fakelib_overlay(title_id, source_path, mount_path, label)) {
(void)cleanup_fakelib_mount();
pthread_mutex_unlock(&g_fakelib_mutex);