diff --git a/include/sm_scan_tree.h b/include/sm_scan_tree.h new file mode 100644 index 0000000..3227b3f --- /dev/null +++ b/include/sm_scan_tree.h @@ -0,0 +1,31 @@ +#ifndef SM_SCAN_TREE_H +#define SM_SCAN_TREE_H + +#include + +typedef enum { + SM_SCAN_TREE_DIR_DESCEND = 0, + SM_SCAN_TREE_DIR_SKIP_DESCEND, + SM_SCAN_TREE_DIR_ABORT, +} sm_scan_tree_dir_visit_t; + +typedef sm_scan_tree_dir_visit_t (*sm_scan_tree_dir_fn)(const char *dir_path, + unsigned int depth_from_root, + void *ctx); +typedef bool (*sm_scan_tree_image_fn)(const char *image_path, + const char *image_name, + unsigned int depth_from_root, + void *ctx); + +typedef struct { + sm_scan_tree_dir_fn on_directory; + sm_scan_tree_image_fn on_image_file; +} sm_scan_tree_callbacks_t; + +// Walk a managed scan subtree using the shared scan-depth and path filtering rules. +bool sm_scan_tree_walk(const char *scan_root, const char *dir_path, + unsigned int depth_from_root, + unsigned int remaining_depth, + const sm_scan_tree_callbacks_t *callbacks, void *ctx); + +#endif diff --git a/src/sm_scan.c b/src/sm_scan.c index 26ec036..5226bc7 100644 --- a/src/sm_scan.c +++ b/src/sm_scan.c @@ -1,6 +1,7 @@ #include "sm_platform.h" #include "sm_runtime.h" #include "sm_scan.h" +#include "sm_scan_tree.h" #include "sm_types.h" #include "sm_game_cache.h" #include "sm_gameinfo.h" @@ -31,43 +32,6 @@ static bool is_under_discovered_param_root( return false; } -static void classify_scan_entry(const char *full_path, unsigned char d_type, - bool *is_dir_out, bool *is_regular_out) { - bool is_dir = false; - bool is_regular = false; - - if (d_type == DT_DIR) { - is_dir = true; - } else if (d_type == DT_REG) { - is_regular = true; - } else if (d_type == DT_UNKNOWN) { - struct stat st; - if (lstat(full_path, &st) == 0) { - is_dir = S_ISDIR(st.st_mode); - is_regular = S_ISREG(st.st_mode); - } - } - - if (is_dir_out) - *is_dir_out = is_dir; - if (is_regular_out) - *is_regular_out = is_regular; -} - -static bool is_distinct_configured_scan_root(const char *current_scan_root, - const char *path) { - for (int i = 0; i < get_scan_path_count(); i++) { - const char *scan_path = get_scan_path(i); - if (strcmp(scan_path, path) != 0) - continue; - if (current_scan_root && strcmp(current_scan_root, path) == 0) - return false; - return true; - } - - return false; -} - // --- Candidate Discovery --- static bool try_collect_candidate_for_directory( const char *full_path, scan_candidate_t *candidates, int max_candidates, @@ -200,65 +164,43 @@ static bool try_collect_candidate_for_directory( return true; } -static void collect_candidates_with_depth( - const char *current_scan_root, const char *dir_path, - unsigned int remaining_depth, scan_candidate_t *candidates, - int max_candidates, int *candidate_count, - const struct AppDbTitleList *app_db_titles, bool app_db_titles_ready, - char discovered_param_roots[][MAX_PATH], - int *discovered_param_root_count, bool *unstable_found_out) { - if (should_stop_requested()) - return; +typedef struct { + scan_candidate_t *candidates; + int max_candidates; + int *candidate_count; + const struct AppDbTitleList *app_db_titles; + bool app_db_titles_ready; + char (*discovered_param_roots)[MAX_PATH]; + int *discovered_param_root_count; + bool *unstable_found_out; +} collect_candidates_walk_ctx_t; - if (is_distinct_configured_scan_root(current_scan_root, dir_path)) - return; +static sm_scan_tree_dir_visit_t collect_candidate_directory_visit( + const char *dir_path, unsigned int depth_from_root, void *ctx_ptr) { + if (depth_from_root == 0u) + return SM_SCAN_TREE_DIR_DESCEND; - // Once a directory has sce_sys/param.json (valid or not), - // it is treated as a terminal game root and descendants are skipped. + collect_candidates_walk_ctx_t *ctx = (collect_candidates_walk_ctx_t *)ctx_ptr; if (try_collect_candidate_for_directory( - dir_path, candidates, max_candidates, candidate_count, app_db_titles, - app_db_titles_ready, discovered_param_roots, - discovered_param_root_count, unstable_found_out)) { - return; + dir_path, ctx->candidates, ctx->max_candidates, ctx->candidate_count, + ctx->app_db_titles, ctx->app_db_titles_ready, + ctx->discovered_param_roots, ctx->discovered_param_root_count, + ctx->unstable_found_out)) { + return SM_SCAN_TREE_DIR_SKIP_DESCEND; } - if (remaining_depth == 0) - return; + return SM_SCAN_TREE_DIR_DESCEND; +} - DIR *d = opendir(dir_path); - if (!d) - return; +static bool collect_candidate_image_visit(const char *image_path, + const char *image_name, + unsigned int depth_from_root, + void *ctx_ptr) { + (void)depth_from_root; - struct dirent *entry; - while ((entry = readdir(d)) != NULL) { - if (should_stop_requested()) - break; - if (entry->d_name[0] == '.') - continue; - - char full_path[MAX_PATH]; - snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); - - bool is_dir = false; - bool is_regular = false; - classify_scan_entry(full_path, entry->d_type, &is_dir, &is_regular); - - if (!path_matches_root_or_child(current_scan_root, IMAGE_MOUNT_BASE) && - is_regular && is_supported_image_file_name(entry->d_name)) { - maybe_mount_image_file(full_path, entry->d_name, unstable_found_out); - } - - if (!is_dir) - continue; - - collect_candidates_with_depth( - current_scan_root, full_path, remaining_depth - 1u, candidates, - max_candidates, candidate_count, app_db_titles, app_db_titles_ready, - discovered_param_roots, discovered_param_root_count, - unstable_found_out); - } - - closedir(d); + collect_candidates_walk_ctx_t *ctx = (collect_candidates_walk_ctx_t *)ctx_ptr; + maybe_mount_image_file(image_path, image_name, ctx->unstable_found_out); + return true; } static bool build_backport_mount_context(const char *title_id, @@ -366,47 +308,24 @@ static void collect_scan_candidates_from_root( return; unsigned int scan_depth = runtime_config()->scan_depth; - bool has_backports_root = !is_under_image_mount_base(scan_path); - if (scan_depth < MIN_SCAN_DEPTH) scan_depth = MIN_SCAN_DEPTH; - DIR *d = opendir(scan_path); - if (!d) - return; - - struct dirent *entry; - while ((entry = readdir(d)) != NULL) { - if (should_stop_requested()) - break; - if (entry->d_name[0] == '.') - continue; - if (has_backports_root && - strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) { - continue; - } - - char full_path[MAX_PATH]; - snprintf(full_path, sizeof(full_path), "%s/%s", scan_path, entry->d_name); - - bool is_dir = false; - bool is_regular = false; - classify_scan_entry(full_path, entry->d_type, &is_dir, &is_regular); - - if (!path_matches_root_or_child(scan_path, IMAGE_MOUNT_BASE) && is_regular && - is_supported_image_file_name(entry->d_name)) - maybe_mount_image_file(full_path, entry->d_name, unstable_found_out); - if (!is_dir) - continue; - - collect_candidates_with_depth( - scan_path, full_path, scan_depth - 1u, candidates, max_candidates, - candidate_count, app_db_titles, app_db_titles_ready, - discovered_param_roots, discovered_param_root_count, - unstable_found_out); - } - - closedir(d); + collect_candidates_walk_ctx_t ctx = { + .candidates = candidates, + .max_candidates = max_candidates, + .candidate_count = candidate_count, + .app_db_titles = app_db_titles, + .app_db_titles_ready = app_db_titles_ready, + .discovered_param_roots = discovered_param_roots, + .discovered_param_root_count = discovered_param_root_count, + .unstable_found_out = unstable_found_out, + }; + sm_scan_tree_callbacks_t callbacks = { + .on_directory = collect_candidate_directory_visit, + .on_image_file = collect_candidate_image_visit, + }; + (void)sm_scan_tree_walk(scan_path, scan_path, 0u, scan_depth, &callbacks, &ctx); } int collect_scan_candidates_for_scan_root(const char *scan_root, diff --git a/src/sm_scan_tree.c b/src/sm_scan_tree.c new file mode 100644 index 0000000..162f266 --- /dev/null +++ b/src/sm_scan_tree.c @@ -0,0 +1,121 @@ +#include "sm_platform.h" +#include "sm_scan_tree.h" + +#include "sm_config_mount.h" +#include "sm_filesystem.h" +#include "sm_image.h" +#include "sm_path_utils.h" +#include "sm_paths.h" +#include "sm_runtime.h" + +static void classify_scan_tree_entry(const char *full_path, unsigned char d_type, + bool *is_dir_out, bool *is_regular_out) { + bool is_dir = false; + bool is_regular = false; + + if (d_type == DT_DIR) { + is_dir = true; + } else if (d_type == DT_REG) { + is_regular = true; + } else if (d_type == DT_UNKNOWN) { + struct stat st; + if (lstat(full_path, &st) == 0) { + is_dir = S_ISDIR(st.st_mode); + is_regular = S_ISREG(st.st_mode); + } + } + + *is_dir_out = is_dir; + *is_regular_out = is_regular; +} + +static bool is_distinct_configured_scan_root(const char *current_scan_root, + const char *path) { + for (int i = 0; i < get_scan_path_count(); i++) { + const char *scan_path = get_scan_path(i); + if (strcmp(scan_path, path) != 0) + continue; + if (strcmp(current_scan_root, path) == 0) + return false; + return true; + } + + return false; +} + +bool sm_scan_tree_walk(const char *scan_root, const char *dir_path, + unsigned int depth_from_root, + unsigned int remaining_depth, + const sm_scan_tree_callbacks_t *callbacks, void *ctx) { + if (should_stop_requested()) + return true; + + if (depth_from_root > 0u && + is_distinct_configured_scan_root(scan_root, dir_path)) { + return true; + } + + sm_scan_tree_dir_visit_t dir_visit = SM_SCAN_TREE_DIR_DESCEND; + if (callbacks->on_directory) { + dir_visit = callbacks->on_directory(dir_path, depth_from_root, ctx); + if (dir_visit == SM_SCAN_TREE_DIR_ABORT) + return false; + if (dir_visit == SM_SCAN_TREE_DIR_SKIP_DESCEND) + return true; + } + + if (remaining_depth == 0u) + return true; + + DIR *d = opendir(dir_path); + if (!d) + return true; + + bool skip_backports_root = + (depth_from_root == 0u && !is_under_image_mount_base(scan_root)); + bool allow_image_file_visits = + callbacks->on_image_file && + !path_matches_root_or_child(scan_root, IMAGE_MOUNT_BASE); + + struct dirent *entry; + while ((entry = readdir(d)) != NULL) { + if (should_stop_requested()) { + closedir(d); + return true; + } + if (entry->d_name[0] == '.') + continue; + if (skip_backports_root && + strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) { + continue; + } + + char full_path[MAX_PATH]; + snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); + + bool is_dir = false; + bool is_regular = false; + classify_scan_tree_entry(full_path, entry->d_type, &is_dir, &is_regular); + + if (allow_image_file_visits && is_regular && + is_supported_image_file_name(entry->d_name)) { + if (!callbacks->on_image_file(full_path, entry->d_name, depth_from_root + 1u, + ctx)) { + closedir(d); + return false; + } + } + + if (!is_dir) + continue; + + if (!sm_scan_tree_walk(scan_root, full_path, depth_from_root + 1u, + remaining_depth - 1u, callbacks, ctx)) { + closedir(d); + return false; + } + } + + closedir(d); + return true; +} diff --git a/src/sm_scanner.c b/src/sm_scanner.c index 112f8a4..e2909dd 100644 --- a/src/sm_scanner.c +++ b/src/sm_scanner.c @@ -14,6 +14,7 @@ #include "sm_paths.h" #include "sm_runtime.h" #include "sm_scan.h" +#include "sm_scan_tree.h" #include "sm_scanner.h" #include "sm_time.h" #include "sm_types.h" @@ -225,41 +226,6 @@ static bool rebuild_scanner_watch_fd_index(void) { return rebuild_scanner_watch_fd_index_with_count(g_scanner_watch_count); } -static bool is_distinct_configured_scan_root(const char *current_scan_root, - const char *path) { - for (int i = 0; i < get_scan_path_count(); i++) { - const char *scan_path = get_scan_path(i); - if (strcmp(scan_path, path) != 0) - continue; - if (current_scan_root && strcmp(current_scan_root, path) == 0) - return false; - return true; - } - - return false; -} - -static void classify_watch_entry(const char *full_path, unsigned char d_type, - bool *is_dir_out, bool *is_regular_out) { - bool is_dir = false; - bool is_regular = false; - - if (d_type == DT_DIR) { - is_dir = true; - } else if (d_type == DT_REG) { - is_regular = true; - } else if (d_type == DT_UNKNOWN) { - struct stat st; - if (lstat(full_path, &st) == 0) { - is_dir = S_ISDIR(st.st_mode); - is_regular = S_ISREG(st.st_mode); - } - } - - *is_dir_out = is_dir; - *is_regular_out = is_regular; -} - static void link_scanner_watch_entry_to_root(size_t index) { scanner_watch_entry_t *entry = &g_scanner_watch_entries[index]; size_t head = g_scanner_root_watch_heads[entry->scan_root_index]; @@ -464,76 +430,35 @@ static bool resolve_watch_tree_rebuild_target(const scanner_watch_entry_t *entry } } -static bool register_scan_dir_tree(int kq, int scan_root_index, - const char *current_scan_root, - const char *dir_path, - unsigned int depth_from_root, - unsigned int remaining_depth) { - if (depth_from_root > 0 && - is_distinct_configured_scan_root(current_scan_root, dir_path)) { - return true; - } +typedef struct { + int kq; + int scan_root_index; +} register_watch_tree_ctx_t; +static sm_scan_tree_dir_visit_t register_watch_directory_visit( + const char *dir_path, unsigned int depth_from_root, void *ctx_ptr) { + register_watch_tree_ctx_t *ctx = (register_watch_tree_ctx_t *)ctx_ptr; scanner_watch_kind_t kind = (depth_from_root == 0u) ? SCANNER_WATCH_SCAN_ROOT : SCANNER_WATCH_SCAN_SUBDIR; - if (!register_scanner_watch_entry(kq, scan_root_index, dir_path, kind, + if (!register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, dir_path, kind, (uint8_t)depth_from_root)) { - return false; + return SM_SCAN_TREE_DIR_ABORT; } - if (remaining_depth == 0u) - return true; + return SM_SCAN_TREE_DIR_DESCEND; +} - DIR *d = opendir(dir_path); - if (!d) - return true; +static bool register_watch_image_visit(const char *image_path, + const char *image_name, + unsigned int depth_from_root, + void *ctx_ptr) { + (void)image_name; - bool skip_backports_root = - (depth_from_root == 0u && !is_under_image_mount_base(current_scan_root)); - - struct dirent *entry; - while ((entry = readdir(d)) != NULL) { - if (should_stop_requested()) { - closedir(d); - return true; - } - if (entry->d_name[0] == '.') - continue; - if (skip_backports_root && - strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) { - continue; - } - - char full_path[MAX_PATH]; - snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); - - bool is_dir = false; - bool is_regular = false; - classify_watch_entry(full_path, entry->d_type, &is_dir, &is_regular); - - if (!path_matches_root_or_child(current_scan_root, IMAGE_MOUNT_BASE) && - is_regular && is_supported_image_file_name(entry->d_name)) { - if (!register_scanner_watch_entry(kq, scan_root_index, full_path, - SCANNER_WATCH_SCAN_IMAGE_FILE, - (uint8_t)(depth_from_root + 1u))) { - closedir(d); - return false; - } - } - if (!is_dir) - continue; - - if (!register_scan_dir_tree(kq, scan_root_index, current_scan_root, - full_path, depth_from_root + 1u, - remaining_depth - 1u)) { - closedir(d); - return false; - } - } - - closedir(d); - return true; + register_watch_tree_ctx_t *ctx = (register_watch_tree_ctx_t *)ctx_ptr; + return register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, image_path, + SCANNER_WATCH_SCAN_IMAGE_FILE, + (uint8_t)depth_from_root); } static bool rebuild_scan_root_watch_tree(int kq, int scan_root_index) { @@ -545,8 +470,16 @@ static bool rebuild_scan_root_watch_tree(int kq, int scan_root_index) { if (scan_depth < MIN_SCAN_DEPTH) scan_depth = MIN_SCAN_DEPTH; - if (!register_scan_dir_tree(kq, scan_root_index, scan_root, scan_root, 0u, - scan_depth)) { + register_watch_tree_ctx_t walk_ctx = { + .kq = kq, + .scan_root_index = scan_root_index, + }; + sm_scan_tree_callbacks_t callbacks = { + .on_directory = register_watch_directory_visit, + .on_image_file = register_watch_image_visit, + }; + if (!sm_scan_tree_walk(scan_root, scan_root, 0u, scan_depth, &callbacks, + &walk_ctx)) { return false; } @@ -596,8 +529,16 @@ static bool rebuild_scan_root_watch_subtree(int kq, int scan_root_index, return true; } - if (!register_scan_dir_tree(kq, scan_root_index, scan_root, rebuild_path, - rebuild_depth, scan_depth - rebuild_depth)) { + register_watch_tree_ctx_t walk_ctx = { + .kq = kq, + .scan_root_index = scan_root_index, + }; + sm_scan_tree_callbacks_t callbacks = { + .on_directory = register_watch_directory_visit, + .on_image_file = register_watch_image_visit, + }; + if (!sm_scan_tree_walk(scan_root, rebuild_path, rebuild_depth, + scan_depth - rebuild_depth, &callbacks, &walk_ctx)) { return false; }