diff --git a/README.md b/README.md index 5947adb..6b6de12 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ Backport overlay behavior: - If multiple scan paths provide the same title backport, the game's own scan path wins; otherwise scan path order is used, followed by the internal fallback. - ShadowMount+ checks the selected backport for `fakelib2` and then `fakelib`; if neither exists, it checks the original game source in the same order. The selected directory is mounted into the running game's sandbox `common/lib`. A selected `fakelib2` is always mounted directly and exclusively: neither emulator updates nor the global fakelib can replace or supplement it. - For folder/image games, `update_emulators=1` makes matching files from `emulators_path` replace files in the selected game fakelib, except when `fakelib2` is selected. Installed PKGs never compose `emulators_path` into their fakelib. The cache is refreshed when its sources change and expires after seven days without a game launch. +- Before preparing and mounting fakelib for an installed PPR PKG, ShadowMount+ deletes `libSceAmpr.sprx`, `libScePlayGo.sprx` and `libkernel.sprx` from the selected `/data/homebrew/backports//fakelib`. Missing files are harmless skips; a deletion failure stops the fakelib mount. - 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`, unless the selected backport contains `fakelib2`. Without a per-game fakelib, the global folder is mounted directly. diff --git a/src/sm_fakelib.c b/src/sm_fakelib.c index dde1ef1..29a4882 100644 --- a/src/sm_fakelib.c +++ b/src/sm_fakelib.c @@ -1323,6 +1323,79 @@ static bool cleanup_fakelib_mount(void) { return true; } +static bool cleanup_ppr_backport_fakelib(const char *title_id, + const char *sandbox_app0_path) { + char backport_path[MAX_PATH]; + char expected_path[MAX_PATH]; + (void)snprintf(expected_path, sizeof(expected_path), + DEFAULT_BACKPORT_SCAN_PATH "/backports/%s", title_id); + if (!resolve_backport_path_for_title(title_id, NULL, backport_path) || + strcmp(backport_path, expected_path) != 0) { + return true; + } + + struct statfs fs; + if (statfs(sandbox_app0_path, &fs) != 0 || + strcmp(fs.f_fstypename, "nsfs") != 0 || + strcmp(fs.f_mntonname, sandbox_app0_path) != 0) { + return true; + } + char package_path[MAX_PATH]; + (void)snprintf(package_path, sizeof(package_path), + "/mnt/sandbox/pfsmnt/%s-app0", title_id); + if (statfs(package_path, &fs) != 0 || + strcmp(fs.f_fstypename, "ppr_pfs") != 0 || + strcmp(fs.f_mntonname, package_path) != 0) { + return true; + } + + int backport_fd = open(backport_path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); + if (backport_fd < 0) { + int saved_errno = errno; + if (saved_errno == ENOENT) + return true; + log_debug(" [FAKELIB] cannot open PPR backport %s: %s", + backport_path, strerror(saved_errno)); + errno = saved_errno; + return false; + } + int fakelib_fd = openat(backport_fd, "fakelib", + O_RDONLY | O_DIRECTORY | O_NOFOLLOW); + int saved_errno = errno; + close(backport_fd); + if (fakelib_fd < 0) { + errno = saved_errno; + if (errno == ENOENT) + return true; + log_debug(" [FAKELIB] cannot open PPR fakelib %s/fakelib: %s", + backport_path, strerror(saved_errno)); + errno = saved_errno; + return false; + } + + static const char *const filenames[] = { + "libSceAmpr.sprx", "libScePlayGo.sprx", "libkernel.sprx"}; + size_t removed = 0; + for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); ++i) { + if (unlinkat(fakelib_fd, filenames[i], 0) == 0) { + removed++; + } else if (errno != ENOENT) { + saved_errno = errno; + close(fakelib_fd); + log_debug(" [FAKELIB] cannot remove PPR library %s/fakelib/%s: %s", + backport_path, filenames[i], strerror(saved_errno)); + errno = saved_errno; + return false; + } + } + close(fakelib_fd); + if (removed > 0) { + log_debug(" [FAKELIB] removed %zu PPR libraries from %s/fakelib", + removed, backport_path); + } + return true; +} + static bool mount_fakelib_for_game_locked(pid_t pid, const char *title_id, bool notify_user) { if (!title_id || !is_supported_game_title_id(title_id)) { @@ -1375,6 +1448,12 @@ static bool mount_fakelib_for_game_locked(pid_t pid, const char *title_id, // app0. Prefer the external backport source directly and never add files // from emulators_path for installed packages. pthread_mutex_lock(&g_fakelib_cache_mutex); + if (!cleanup_ppr_backport_fakelib(title_id, sandbox_app0_path)) { + int saved_errno = errno; + pthread_mutex_unlock(&g_fakelib_cache_mutex); + errno = saved_errno; + return false; + } prepare_title_cache(title_id, sandbox_app0_path, true, false); pthread_mutex_unlock(&g_fakelib_cache_mutex); }