mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-22 06:32:25 -05:00
199 lines
5.9 KiB
C
199 lines
5.9 KiB
C
#include "sm_platform.h"
|
|
#include "sm_game_cache.h"
|
|
#include "sm_config_mount.h"
|
|
#include "sm_filesystem.h"
|
|
#include "sm_image_cache.h"
|
|
#include "sm_limits.h"
|
|
#include "sm_log.h"
|
|
#include "sm_path_utils.h"
|
|
#include "sm_title_state.h"
|
|
|
|
struct GameCache {
|
|
char path[MAX_PATH];
|
|
char title_id[MAX_TITLE_ID];
|
|
char title_name[MAX_TITLE_NAME];
|
|
char owning_scan_root[MAX_PATH];
|
|
bool valid;
|
|
};
|
|
|
|
static struct GameCache g_game_cache[MAX_PENDING];
|
|
|
|
static bool resolve_game_cache_owning_scan_root(const char *path,
|
|
char owning_scan_root[MAX_PATH]) {
|
|
char resolved_source_path[MAX_PATH];
|
|
const char *match_path = path;
|
|
owning_scan_root[0] = '\0';
|
|
|
|
if (is_under_image_mount_base(path) &&
|
|
resolve_image_source_from_mount_cache(path, resolved_source_path,
|
|
sizeof(resolved_source_path))) {
|
|
match_path = resolved_source_path;
|
|
}
|
|
|
|
size_t best_match_len = 0;
|
|
for (int i = 0; i < get_scan_path_count(); i++) {
|
|
const char *scan_path = get_scan_path(i);
|
|
if (!path_matches_root_or_child(match_path, scan_path))
|
|
continue;
|
|
|
|
size_t scan_path_len = strlen(scan_path);
|
|
if (scan_path_len <= best_match_len)
|
|
continue;
|
|
|
|
(void)strlcpy(owning_scan_root, scan_path, MAX_PATH);
|
|
best_match_len = scan_path_len;
|
|
}
|
|
|
|
return owning_scan_root[0] != '\0';
|
|
}
|
|
|
|
static void write_game_cache_slot(struct GameCache *entry, const char *path,
|
|
const char *title_id,
|
|
const char *title_name,
|
|
const char *owning_scan_root) {
|
|
(void)strlcpy(entry->path, path, sizeof(entry->path));
|
|
(void)strlcpy(entry->title_id, title_id, sizeof(entry->title_id));
|
|
(void)strlcpy(entry->title_name, title_name, sizeof(entry->title_name));
|
|
(void)strlcpy(entry->owning_scan_root, owning_scan_root,
|
|
sizeof(entry->owning_scan_root));
|
|
entry->valid = true;
|
|
}
|
|
|
|
static bool ensure_game_cache_owning_scan_root(struct GameCache *entry) {
|
|
if (entry->owning_scan_root[0] != '\0')
|
|
return true;
|
|
|
|
return resolve_game_cache_owning_scan_root(entry->path, entry->owning_scan_root);
|
|
}
|
|
|
|
static void clear_game_cache_slot(int index, const char *reason) {
|
|
if (index < 0 || index >= MAX_PENDING || !g_game_cache[index].valid)
|
|
return;
|
|
|
|
if (reason && reason[0] != '\0') {
|
|
if (g_game_cache[index].title_id[0] != '\0')
|
|
log_debug(" [CACHE] %s: %s (%s)", reason, g_game_cache[index].title_id,
|
|
g_game_cache[index].path);
|
|
else
|
|
log_debug(" [CACHE] %s: %s", reason, g_game_cache[index].path);
|
|
}
|
|
|
|
if (g_game_cache[index].title_id[0] != '\0')
|
|
clear_duplicate_title_notification(g_game_cache[index].title_id);
|
|
|
|
memset(&g_game_cache[index], 0, sizeof(g_game_cache[index]));
|
|
}
|
|
|
|
void cache_game_entry(const char *path, const char *title_id,
|
|
const char *title_name) {
|
|
char owning_scan_root[MAX_PATH];
|
|
(void)resolve_game_cache_owning_scan_root(path, owning_scan_root);
|
|
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
if (strcmp(g_game_cache[k].path, path) != 0 &&
|
|
strcmp(g_game_cache[k].title_id, title_id) != 0) {
|
|
continue;
|
|
}
|
|
write_game_cache_slot(&g_game_cache[k], path, title_id, title_name,
|
|
owning_scan_root);
|
|
return;
|
|
}
|
|
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid) {
|
|
write_game_cache_slot(&g_game_cache[k], path, title_id, title_name,
|
|
owning_scan_root);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void prune_game_cache(void) {
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
if (path_exists(g_game_cache[k].path))
|
|
continue;
|
|
clear_game_cache_slot(k, "source removed");
|
|
}
|
|
}
|
|
|
|
void prune_game_cache_for_root(const char *root) {
|
|
if (!root || root[0] == '\0') {
|
|
prune_game_cache();
|
|
return;
|
|
}
|
|
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
const char *entry_root =
|
|
ensure_game_cache_owning_scan_root(&g_game_cache[k])
|
|
? g_game_cache[k].owning_scan_root
|
|
: g_game_cache[k].path;
|
|
if (!path_matches_root_or_child(entry_root, root))
|
|
continue;
|
|
if (path_exists(g_game_cache[k].path))
|
|
continue;
|
|
clear_game_cache_slot(k, "source removed");
|
|
}
|
|
}
|
|
|
|
void for_each_cached_game_entry(const char *root, game_cache_iter_fn fn,
|
|
void *ctx) {
|
|
if (!fn)
|
|
return;
|
|
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
bool has_owning_scan_root = ensure_game_cache_owning_scan_root(&g_game_cache[k]);
|
|
const char *entry_root =
|
|
has_owning_scan_root ? g_game_cache[k].owning_scan_root : g_game_cache[k].path;
|
|
if (root && root[0] != '\0' && strcmp(entry_root, root) != 0) {
|
|
continue;
|
|
}
|
|
if (!fn(g_game_cache[k].path, g_game_cache[k].title_id,
|
|
g_game_cache[k].title_name,
|
|
has_owning_scan_root ? g_game_cache[k].owning_scan_root : NULL, ctx)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool find_cached_game(const char *path, const char *title_id,
|
|
const char **existing_path_out) {
|
|
if (existing_path_out)
|
|
*existing_path_out = NULL;
|
|
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
if (path && strcmp(g_game_cache[k].path, path) == 0) {
|
|
if (existing_path_out)
|
|
*existing_path_out = g_game_cache[k].path;
|
|
return true;
|
|
}
|
|
if (title_id && title_id[0] != '\0' &&
|
|
strcmp(g_game_cache[k].title_id, title_id) == 0) {
|
|
if (existing_path_out)
|
|
*existing_path_out = g_game_cache[k].path;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void clear_cached_game(const char *path) {
|
|
for (int k = 0; k < MAX_PENDING; k++) {
|
|
if (!g_game_cache[k].valid)
|
|
continue;
|
|
if (strcmp(g_game_cache[k].path, path) != 0)
|
|
continue;
|
|
clear_game_cache_slot(k, "removed from duplicate tracking");
|
|
}
|
|
}
|