diff --git a/source/fs.c b/source/fs.c index 7002a34..10a7bb0 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,6 +1,10 @@ /* * OmniNX Installer - Filesystem operations with file logging * Based on HATS Installer + * + * All tree walkers share one path buffer per call (no malloc per entry) and + * visit every entry exactly once. Moving within the SD card uses f_rename and + * only falls back to copying when a rename fails. */ #include "fs.h" @@ -10,8 +14,8 @@ #include #include -#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer -#define LOG_BUFFER_SIZE 1024 // fits two 255-char paths plus text +#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer, allocated once +#define LOG_BUFFER_SIZE 1024 // fits two 255-char paths plus text #define LOG_SYNC_EVERY 16 // Log file handle @@ -20,6 +24,9 @@ static bool log_enabled = false; static u32 log_lines; static char log_buf[LOG_BUFFER_SIZE]; +// Shared copy buffer (lazy, released with fs_release_buffers) +static u8 *copy_buf; + // Initialize log file void log_init(const char *path) { int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS); @@ -61,6 +68,13 @@ bool log_is_enabled(void) { return log_enabled; } +void fs_release_buffers(void) { + if (copy_buf) { + free(copy_buf); + copy_buf = NULL; + } +} + // Convert FatFS error code to string const char *fs_error_str(int err) { switch (err) { @@ -88,270 +102,359 @@ const char *fs_error_str(int err) { } } -// Combine two paths -static char *combine_paths(const char *base, const char *add) { - size_t base_len = strlen(base); - size_t add_len = strlen(add); - char *result = malloc(base_len + add_len + 2); +/* ---- path helpers ---------------------------------------------------- */ - if (!result) return NULL; - - if (base_len > 0 && base[base_len - 1] == '/') { - s_printf(result, "%s%s", base, add); - } else { - s_printf(result, "%s/%s", base, add); - } - - return result; +// Append "/name" to path (len = current length). Returns new length or 0 if too long. +static size_t path_push(char *path, size_t len, const char *name) { + size_t nlen = strlen(name); + size_t slash = (len > 0 && path[len - 1] != '/') ? 1 : 0; + if (len + slash + nlen + 1 > FS_PATH_MAX) + return 0; + if (slash) + path[len++] = '/'; + memcpy(path + len, name, nlen + 1); + return len + nlen; } +static size_t path_set(char *path, const char *src) { + size_t len = strlen(src); + if (len + 1 > FS_PATH_MAX) + return 0; + memcpy(path, src, len + 1); + // Normalise "sd:/foo/" -> "sd:/foo" (keep "sd:/") + while (len > 4 && path[len - 1] == '/') + path[--len] = '\0'; + return len; +} + +static const char *path_basename(const char *path) { + const char *slash = strrchr(path, '/'); + return slash ? slash + 1 : path; +} + +static bool is_dot_entry(const char *name) { + return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +} + +static void clear_readonly(const char *path, BYTE attr) { + if (attr & AM_RDO) + f_chmod(path, attr & ~AM_RDO, AM_RDO); +} + +/* ---- single file ----------------------------------------------------- */ + // Copy a single file with logging int file_copy(const char *src, const char *dst) { FIL fin, fout; FILINFO fno; int res; - log_write("COPY: %s -> %s\n", src, dst); - res = f_open(&fin, src, FA_READ | FA_OPEN_EXISTING); if (res != FR_OK) { - log_write(" ERROR open src: %s\n", fs_error_str(res)); + log_write("COPY %s: open src %s\n", src, fs_error_str(res)); return res; } - f_stat(src, &fno); - u64 file_size = f_size(&fin); - log_write(" Size: %d bytes\n", (u32)file_size); + if (f_stat(src, &fno) != FR_OK) + fno.fattrib = 0; + u64 remaining = f_size(&fin); res = f_open(&fout, dst, FA_WRITE | FA_CREATE_ALWAYS); if (res != FR_OK) { f_close(&fin); - log_write(" ERROR open dst: %s\n", fs_error_str(res)); + log_write("COPY %s: open dst %s\n", dst, fs_error_str(res)); return res; } - u8 *buf = malloc(FS_BUFFER_SIZE); - if (!buf) { + if (!copy_buf) + copy_buf = malloc(FS_BUFFER_SIZE); + if (!copy_buf) { f_close(&fin); f_close(&fout); - log_write(" ERROR: Out of memory for buffer\n"); + log_write("COPY %s: out of memory\n", src); return FR_NOT_ENOUGH_CORE; } - u64 remaining = file_size; UINT br, bw; - while (remaining > 0) { UINT to_copy = (remaining > FS_BUFFER_SIZE) ? FS_BUFFER_SIZE : (UINT)remaining; - res = f_read(&fin, buf, to_copy, &br); - if (res != FR_OK) { - log_write(" ERROR read: %s\n", fs_error_str(res)); - break; - } - if (br != to_copy) { - log_write(" ERROR: Read %d bytes, expected %d\n", br, to_copy); + res = f_read(&fin, copy_buf, to_copy, &br); + if (res == FR_OK && br != to_copy) res = FR_DISK_ERR; + if (res != FR_OK) break; - } - res = f_write(&fout, buf, to_copy, &bw); - if (res != FR_OK) { - log_write(" ERROR write: %s\n", fs_error_str(res)); - break; - } - if (bw != to_copy) { - log_write(" ERROR: Wrote %d bytes, expected %d\n", bw, to_copy); + res = f_write(&fout, copy_buf, to_copy, &bw); + if (res == FR_OK && bw != to_copy) res = FR_DISK_ERR; + if (res != FR_OK) break; - } remaining -= to_copy; } - free(buf); f_close(&fin); f_close(&fout); - if (res == FR_OK) { + if (res == FR_OK) f_chmod(dst, fno.fattrib, 0x3A); - log_write(" OK\n"); - } - + log_write("COPY %s -> %s: %s\n", src, dst, fs_error_str(res)); return res; } -// Recursively delete a folder with logging -int folder_delete(const char *path) { - DIR dir; +// Move a single file; replaces dst. Falls back to copy + delete if rename fails. +int fs_move_file(const char *src, const char *dst) { FILINFO fno; - int res; - - log_write("DELETE: %s\n", path); - - res = f_opendir(&dir, path); - if (res != FR_OK) { - // Maybe it's a file, try to delete it - log_write(" Not a dir, trying as file...\n"); - res = f_unlink(path); - if (res != FR_OK) { - log_write(" ERROR unlink: %s\n", fs_error_str(res)); - } else { - log_write(" OK (file deleted)\n"); + if (f_stat(dst, &fno) == FR_OK) { + if (fno.fattrib & AM_DIR) { + log_write("MOVE %s: destination is a folder\n", dst); + return FR_DENIED; } - return res; + clear_readonly(dst, fno.fattrib); + f_unlink(dst); } - int file_count = 0; - int dir_count = 0; + int res = f_rename(src, dst); + if (res == FR_OK) { + log_write("MOVE %s -> %s\n", src, dst); + return FR_OK; + } - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK) { - log_write(" ERROR readdir: %s\n", fs_error_str(res)); - break; - } - if (fno.fname[0] == 0) break; // End of directory + res = file_copy(src, dst); + if (res == FR_OK) + f_unlink(src); + return res; +} - char *full_path = combine_paths(path, fno.fname); - if (!full_path) { - res = FR_NOT_ENOUGH_CORE; +/* ---- trees ----------------------------------------------------------- */ + +static int delete_tree_rec(char *path, size_t len, fs_progress_cb cb, void *ctx) { + DIR dir; + FILINFO fno; + + int res = f_opendir(&dir, path); + if (res != FR_OK) + return res; + + while ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0]) { + if (is_dot_entry(fno.fname)) + continue; + size_t sub = path_push(path, len, fno.fname); + if (!sub) { + res = FR_INVALID_NAME; break; } if (fno.fattrib & AM_DIR) { - dir_count++; - res = folder_delete(full_path); + res = delete_tree_rec(path, sub, cb, ctx); + if (res == FR_OK) { + clear_readonly(path, fno.fattrib); + res = f_unlink(path); + } } else { - file_count++; - log_write(" DEL: %s\n", fno.fname); - - // Clear read-only attribute if set (common with some CFW packs) - if (fno.fattrib & AM_RDO) { - f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO); - } - - res = f_unlink(full_path); - if (res != FR_OK) { - log_write(" ERROR: %s\n", fs_error_str(res)); - } + clear_readonly(path, fno.fattrib); + res = f_unlink(path); } + log_write(" DEL %s: %s\n", path, fs_error_str(res)); + path[len] = '\0'; - free(full_path); - if (res != FR_OK) break; + if (res != FR_OK) + break; + if (cb) + cb(ctx); } f_closedir(&dir); - - if (res == FR_OK || res == FR_NO_FILE) { - log_write(" Removing dir: %s (%d files, %d subdirs)\n", path, file_count, dir_count); - - // Check and clear read-only attribute on directory if set - FILINFO dir_info; - if (f_stat(path, &dir_info) == FR_OK && (dir_info.fattrib & AM_RDO)) { - f_chmod(path, dir_info.fattrib & ~AM_RDO, AM_RDO); - } - - res = f_unlink(path); - if (res != FR_OK) { - log_write(" ERROR rmdir: %s\n", fs_error_str(res)); - } else { - log_write(" OK\n"); - } - } - return res; } -// Recursively copy a folder with logging -int folder_copy(const char *src, const char *dst) { +// Delete a file or a whole folder. A missing path counts as success. +int fs_delete_tree(const char *target, fs_progress_cb cb, void *ctx) { + char path[FS_PATH_MAX]; + FILINFO fno; + size_t len = path_set(path, target); + if (!len) + return FR_INVALID_NAME; + + int res = f_stat(path, &fno); + if (res == FR_NO_FILE || res == FR_NO_PATH) + return FR_OK; + if (res != FR_OK) + return res; + + if (fno.fattrib & AM_DIR) { + res = delete_tree_rec(path, len, cb, ctx); + if (res != FR_OK) { + log_write("DELETE %s: %s\n", target, fs_error_str(res)); + return res; + } + } + clear_readonly(path, fno.fattrib); + res = f_unlink(path); + log_write("DELETE %s: %s\n", target, fs_error_str(res)); + return res; +} + +int folder_delete(const char *path) { + return fs_delete_tree(path, NULL, NULL); +} + +static int copy_tree_rec(char *src, size_t slen, char *dst, size_t dlen) { DIR dir; FILINFO fno; - int res; - log_write("FOLDER COPY: %s -> %s\n", src, dst); + int res = f_mkdir(dst); + if (res != FR_OK && res != FR_EXIST) + return res; res = f_opendir(&dir, src); - if (res != FR_OK) { - log_write(" ERROR opendir src: %s\n", fs_error_str(res)); + if (res != FR_OK) return res; - } - // Get folder name from src path - const char *folder_name = strrchr(src, '/'); - if (folder_name) { - folder_name++; - } else { - folder_name = src; - } - - // Create destination folder - char *dst_path = combine_paths(dst, folder_name); - if (!dst_path) { - f_closedir(&dir); - return FR_NOT_ENOUGH_CORE; - } - - log_write(" Creating: %s\n", dst_path); - - res = f_mkdir(dst_path); - if (res == FR_EXIST) { - log_write(" (already exists)\n"); - res = FR_OK; - } else if (res != FR_OK) { - log_write(" ERROR mkdir: %s\n", fs_error_str(res)); - f_closedir(&dir); - free(dst_path); - return res; - } - - int file_count = 0; - int dir_count = 0; - - // Copy contents - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK) { - log_write(" ERROR readdir: %s\n", fs_error_str(res)); - break; - } - if (fno.fname[0] == 0) break; // End of directory - - char *src_full = combine_paths(src, fno.fname); - char *dst_full = combine_paths(dst_path, fno.fname); - - if (!src_full || !dst_full) { - if (src_full) free(src_full); - if (dst_full) free(dst_full); - res = FR_NOT_ENOUGH_CORE; + while ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0]) { + if (is_dot_entry(fno.fname)) + continue; + size_t s = path_push(src, slen, fno.fname); + size_t d = path_push(dst, dlen, fno.fname); + if (!s || !d) { + res = FR_INVALID_NAME; break; } - if (fno.fattrib & AM_DIR) { - dir_count++; - res = folder_copy(src_full, dst_path); - } else { - file_count++; - res = file_copy(src_full, dst_full); - } - - free(src_full); - free(dst_full); - - if (res != FR_OK) break; + if (fno.fattrib & AM_DIR) + res = copy_tree_rec(src, s, dst, d); + else + res = file_copy(src, dst); + src[slen] = '\0'; + dst[dlen] = '\0'; + if (res != FR_OK) + break; } - f_closedir(&dir); - - // Copy folder attributes - if (res == FR_OK) { - FILINFO src_info; - if (f_stat(src, &src_info) == FR_OK) { - f_chmod(dst_path, src_info.fattrib, 0x3A); - } - log_write(" Done: %d files, %d subdirs\n", file_count, dir_count); - } - - free(dst_path); return res; } + +// Recursively copy folder src into dst_parent (creates dst_parent/) +int folder_copy(const char *src_dir, const char *dst_parent) { + char src[FS_PATH_MAX]; + char dst[FS_PATH_MAX]; + size_t slen = path_set(src, src_dir); + size_t dlen = path_set(dst, dst_parent); + if (!slen || !dlen || !(dlen = path_push(dst, dlen, path_basename(src)))) + return FR_INVALID_NAME; + + int res = copy_tree_rec(src, slen, dst, dlen); + log_write("COPY TREE %s -> %s: %s\n", src_dir, dst, fs_error_str(res)); + return res; +} + +static int move_tree_rec(char *src, size_t slen, char *dst, size_t dlen, fs_progress_cb cb, void *ctx) { + DIR dir; + FILINFO fno; + FILINFO dst_info; + + int res = f_opendir(&dir, src); + if (res != FR_OK) + return res; + + while ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0]) { + if (is_dot_entry(fno.fname)) + continue; + size_t s = path_push(src, slen, fno.fname); + size_t d = path_push(dst, dlen, fno.fname); + if (!s || !d) { + res = FR_INVALID_NAME; + break; + } + + bool dst_exists = f_stat(dst, &dst_info) == FR_OK; + if (fno.fattrib & AM_DIR) { + if (!dst_exists) { + // Whole subtree moves in one step + res = f_rename(src, dst); + if (res != FR_OK) + res = copy_tree_rec(src, s, dst, d); + log_write("MOVE DIR %s -> %s: %s\n", src, dst, fs_error_str(res)); + } else if (dst_info.fattrib & AM_DIR) { + res = move_tree_rec(src, s, dst, d, cb, ctx); + } else { + log_write("MOVE %s: destination is a file\n", dst); + res = FR_DENIED; + } + } else { + res = fs_move_file(src, dst); + } + src[slen] = '\0'; + dst[dlen] = '\0'; + + if (res != FR_OK) + break; + if (cb) + cb(ctx); + } + f_closedir(&dir); + return res; +} + +// Move folder src_dir into dst_parent (dst_parent/), merging with an existing folder. +int fs_move_tree(const char *src_dir, const char *dst_parent, fs_progress_cb cb, void *ctx) { + char src[FS_PATH_MAX]; + char dst[FS_PATH_MAX]; + FILINFO fno; + size_t slen = path_set(src, src_dir); + size_t dlen = path_set(dst, dst_parent); + if (!slen || !dlen || !(dlen = path_push(dst, dlen, path_basename(src)))) + return FR_INVALID_NAME; + + int res = f_stat(dst, &fno); + if (res == FR_NO_FILE) { + // Target missing: one rename moves everything + res = f_rename(src, dst); + log_write("MOVE DIR %s -> %s: %s\n", src, dst, fs_error_str(res)); + if (res == FR_OK) + return FR_OK; + res = copy_tree_rec(src, slen, dst, dlen); + log_write("COPY TREE %s -> %s: %s\n", src, dst, fs_error_str(res)); + return res; + } + if (res != FR_OK) + return res; + if (!(fno.fattrib & AM_DIR)) + return FR_DENIED; + + return move_tree_rec(src, slen, dst, dlen, cb, ctx); +} + +// Count entries below path, stopping once limit is reached (limit 0 = no limit) +static u32 count_rec(char *path, size_t len, u32 count, u32 limit) { + DIR dir; + FILINFO fno; + if (f_opendir(&dir, path) != FR_OK) + return count; + + while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0]) { + if (is_dot_entry(fno.fname)) + continue; + count++; + if (limit && count >= limit) + break; + if (fno.fattrib & AM_DIR) { + size_t sub = path_push(path, len, fno.fname); + if (sub) { + count = count_rec(path, sub, count, limit); + path[len] = '\0'; + } + if (limit && count >= limit) + break; + } + } + f_closedir(&dir); + return count; +} + +u32 fs_count_items(const char *target, u32 limit) { + char path[FS_PATH_MAX]; + size_t len = path_set(path, target); + return len ? count_rec(path, len, 0, limit) : 0; +} diff --git a/source/fs.h b/source/fs.h index 2fb1309..357bed6 100644 --- a/source/fs.h +++ b/source/fs.h @@ -6,15 +6,31 @@ #pragma once #include +#define FS_PATH_MAX 512 + +// Called once per processed entry (file or folder) by the tree walkers. +typedef void (*fs_progress_cb)(void *ctx); + // Error code to string const char *fs_error_str(int err); -// File/folder operations - returns 0 on success +// File/folder operations - return FR_OK (0) on success int file_copy(const char *src, const char *dst); -int folder_copy(const char *src, const char *dst); +int folder_copy(const char *src_dir, const char *dst_parent); // copies into dst_parent/ int folder_delete(const char *path); -// File logging +// Delete a file or folder tree (missing path = FR_OK). +int fs_delete_tree(const char *path, fs_progress_cb cb, void *ctx); +// Move a file, replacing dst (rename, copy as fallback). +int fs_move_file(const char *src, const char *dst); +// Move folder src_dir into dst_parent/, merging into an existing folder. +int fs_move_tree(const char *src_dir, const char *dst_parent, fs_progress_cb cb, void *ctx); +// Count entries below path; stops at limit (0 = count everything). +u32 fs_count_items(const char *path, u32 limit); +// Free the shared copy buffer. +void fs_release_buffers(void); + +// File logging (enabled via debug.ini log=1) void log_init(const char *path); void log_close(void); void log_write(const char *fmt, ...); diff --git a/source/install.c b/source/install.c index 8dec1b9..e095586 100644 --- a/source/install.c +++ b/source/install.c @@ -80,390 +80,133 @@ bool install_path_exists(const char *path) { return (f_stat(path, &fno) == FR_OK); } -// Count total items (files + directories) in a directory tree recursively -int install_count_directory_items(const char *path) { - DIR dir; - FILINFO fno; - int res; - int count = 0; - - res = f_opendir(&dir, path); - if (res != FR_OK) { - return 0; - } - - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) break; - - // Skip . and .. - if (fno.fname[0] == '.' && (fno.fname[1] == '\0' || (fno.fname[1] == '.' && fno.fname[2] == '\0'))) { - continue; - } - - count++; // Count this item - - // If it's a directory, recursively count its contents - if (fno.fattrib & AM_DIR) { - char sub_path[256]; - s_printf(sub_path, "%s/%s", path, fno.fname); - count += install_count_directory_items(sub_path); - } - } - - f_closedir(&dir); - return count; +/* ---- progress line --------------------------------------------------- + * One status line per operation. Counts entries as they are processed, so no + * extra pass over the tree is needed just to compute a total. */ +typedef struct { + const char *verb; + const char *name; + u32 done; + u32 x, y; +} install_progress_t; + +static void progress_draw(install_progress_t *p) { + gfx_con_setpos(p->x, p->y); + install_set_color(COLOR_CYAN); + gfx_printf(" %s: %s (%d)", p->verb, p->name, p->done); + install_set_color(COLOR_WHITE); } -// Helper to combine paths (handles trailing slashes properly) -void install_combine_path(char *result, size_t size, const char *base, const char *add) { - size_t base_len = strlen(base); - if (base_len > 0 && base[base_len - 1] == '/') { - // Base has trailing slash, just append - s_printf(result, "%s%s", base, add); +static void progress_begin(install_progress_t *p, const char *verb, const char *name) { + p->verb = verb; + p->name = name; + p->done = 0; + gfx_con_getpos(&p->x, &p->y); + progress_draw(p); +} + +static void progress_tick(void *ctx) { + install_progress_t *p = ctx; + p->done++; + if (p->done % 25 == 0) + progress_draw(p); +} + +static void progress_end(install_progress_t *p, int res) { + gfx_con_setpos(p->x, p->y); + if (res == FR_OK) { + install_set_color(COLOR_GREEN); + gfx_printf(" %s: %s (%d) - Fertig!\n", p->verb, p->name, p->done); } else { - // No trailing slash, add one - s_printf(result, "%s/%s", base, add); + install_set_color(COLOR_ORANGE); + gfx_printf(" %s: %s - Fehler: %s\n", p->verb, p->name, fs_error_str(res)); } + install_set_color(COLOR_WHITE); } -/* Copy every regular file at staging root to dst_root; subdirs and volume labels skipped. */ -int install_copy_staging_root_files(const char *staging, const char *dst_root) { - DIR dir; - FILINFO fno; - char src_full[256]; - char dst_full[256]; - int res = f_opendir(&dir, staging); - if (res != FR_OK) - return res; - - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) - break; - if (fno.fname[0] == '.' && (fno.fname[1] == '\0' || (fno.fname[1] == '.' && fno.fname[2] == '\0'))) - continue; - if (fno.fattrib & (AM_DIR | AM_VOL)) - continue; - - install_combine_path(src_full, sizeof(src_full), staging, fno.fname); - install_combine_path(dst_full, sizeof(dst_full), dst_root, fno.fname); - res = file_copy(src_full, dst_full); - if (res != FR_OK) { - f_closedir(&dir); - return res; - } - } - - f_closedir(&dir); - return FR_OK; +static const char *install_basename(const char *path) { + const char *slash = strrchr(path, '/'); + return slash ? slash + 1 : path; } -// Recursive folder copy with progress tracking -static int folder_copy_progress_recursive(const char *src, const char *dst, int *copied, int total, u32 start_x, u32 start_y, const char *display_name, int *last_percent) { - DIR dir; - FILINFO fno; - int res; - char src_full[256]; - char dst_full[256]; - char dst_dir[256]; - - res = f_opendir(&dir, src); - if (res != FR_OK) { - return res; - } - - // Get folder name from src path - const char *folder_name = strrchr(src, '/'); - if (folder_name) { - folder_name++; - } else { - folder_name = src; - } - - // Create destination folder path (handle trailing slash in dst) - install_combine_path(dst_dir, sizeof(dst_dir), dst, folder_name); - - // Try to create the directory (ignore if it already exists) - res = f_mkdir(dst_dir); - if (res == FR_EXIST) { - res = FR_OK; // Directory already exists, that's fine - } else if (res != FR_OK) { - // If mkdir fails, check if it's actually a file (shouldn't happen, but be safe) - FILINFO fno; - if (f_stat(dst_dir, &fno) == FR_OK && !(fno.fattrib & AM_DIR)) { - // Destination exists but is a file, not a directory - this is an error - f_closedir(&dir); - return FR_DENIED; - } - // If it's a directory, continue (might have been created between check and mkdir) - if (f_stat(dst_dir, &fno) == FR_OK && (fno.fattrib & AM_DIR)) { - res = FR_OK; - } else { - f_closedir(&dir); - return res; - } - } - - // Copy contents - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) break; - - // Skip . and .. - if (fno.fname[0] == '.' && (fno.fname[1] == '\0' || (fno.fname[1] == '.' && fno.fname[2] == '\0'))) { - continue; - } - - // Build source and destination paths - install_combine_path(src_full, sizeof(src_full), src, fno.fname); - install_combine_path(dst_full, sizeof(dst_full), dst_dir, fno.fname); - - if (fno.fattrib & AM_DIR) { - res = folder_copy_progress_recursive(src_full, dst_dir, copied, total, start_x, start_y, display_name, last_percent); - // Increment counter for directory (it was counted in total) - (*copied)++; - } else { - res = file_copy(src_full, dst_full); - (*copied)++; - } - - // Update progress every 10 items or when percentage changes - if ((*copied % 10 == 0 || total == 0) && res == FR_OK) { - int percent = total > 0 ? (*copied * 100) / total : 0; - if (percent != *last_percent || *copied % 50 == 0) { - gfx_con_setpos(start_x, start_y); - install_set_color(COLOR_CYAN); - gfx_printf(" Kopiere: %s [%3d%%] (%d/%d)", display_name, percent, *copied, total); - install_set_color(COLOR_WHITE); - *last_percent = percent; - } - } - - if (res != FR_OK) break; - } - - f_closedir(&dir); +// Delete one folder tree with its own progress line (staging cleanup, large folders). +int install_delete_tree(const char *path) { + install_progress_t p; + progress_begin(&p, "Loesche", install_basename(path)); + int res = fs_delete_tree(path, progress_tick, &p); + progress_end(&p, res); return res; } -// Progress-aware folder copy (improved version) -int folder_copy_with_progress_v2(const char *src, const char *dst, const char *display_name) { - int copied = 0; - int total = 0; - int last_percent = -1; - u32 start_x, start_y; - int res; - - // Check if source exists +// Move staging/ into dst_parent/. FR_NO_FILE if the pack has no such folder. +int install_move_tree(const char *src, const char *dst_parent, const char *display_name) { if (!install_path_exists(src)) { install_set_color(COLOR_ORANGE); gfx_printf(" Ueberspringe: %s (nicht gefunden)\n", display_name); install_set_color(COLOR_WHITE); return FR_NO_FILE; } - - // Count total items first - total = install_count_directory_items(src); - - if (total == 0) { - // Empty directory, just create destination - const char *folder_name = strrchr(src, '/'); - if (folder_name) folder_name++; - else folder_name = src; - char dst_path[256]; - install_combine_path(dst_path, sizeof(dst_path), dst, folder_name); - res = f_mkdir(dst_path); - if (res == FR_OK || res == FR_EXIST) { - return FR_OK; - } - return res; - } - - // Save cursor position - gfx_con_getpos(&start_x, &start_y); - - // Show initial status - install_set_color(COLOR_CYAN); - gfx_printf(" Kopiere: %s [ 0%%] (0/%d)", display_name, total); - install_set_color(COLOR_WHITE); - - // Perform the copy with progress updates - log_write("COPY TREE %s -> %s (%d Eintraege)\n", src, dst, (u32)total); - res = folder_copy_progress_recursive(src, dst, &copied, total, start_x, start_y, display_name, &last_percent); - log_write("COPY TREE %s: %s\n", src, fs_error_str(res)); - - // Final update - overwrite the same line - gfx_con_setpos(start_x, start_y); - if (res == FR_OK) { - install_set_color(COLOR_GREEN); - gfx_printf(" Kopiere: %s [100%%] (%d/%d) - Fertig!\n", display_name, copied, total); - install_set_color(COLOR_WHITE); - } else { + + install_progress_t p; + progress_begin(&p, "Installiere", display_name); + int res = fs_move_tree(src, dst_parent, progress_tick, &p); + progress_end(&p, res); + if (res != FR_OK) { install_set_color(COLOR_RED); - gfx_printf(" Kopiere: %s - Fehlgeschlagen!\n", display_name); - gfx_printf(" Fehler: %s (Code=%d)\n", fs_error_str(res), res); - gfx_printf(" Quelle: %s\n", src); - gfx_printf(" Ziel: %s\n", dst); + gfx_printf(" Quelle: %s\n Ziel: %s\n", src, dst_parent); install_set_color(COLOR_WHITE); - - // Fallback: try using the original folder_copy function - install_set_color(COLOR_ORANGE); - gfx_printf(" Versuche alternative Kopiermethode...\n"); - install_set_color(COLOR_WHITE); - int fallback_res = folder_copy(src, dst); - if (fallback_res == FR_OK) { - install_set_color(COLOR_GREEN); - gfx_printf(" Alternative Kopie erfolgreich!\n"); - install_set_color(COLOR_WHITE); - return FR_OK; - } } - return res; } -// Recursive folder delete with progress tracking (shared implementation) -int folder_delete_progress_recursive(const char *path, int *deleted, int total, u32 start_x, u32 start_y, const char *display_name, int *last_percent) { +/* Move every regular file at staging root to dst_root; subdirs and volume labels skipped. */ +int install_move_staging_root_files(const char *staging, const char *dst_root) { DIR dir; FILINFO fno; - int res; - - res = f_opendir(&dir, path); - if (res != FR_OK) { - // Maybe it's a file, try to delete it - res = f_unlink(path); - if (res == FR_OK || res == FR_NO_FILE) { - (*deleted)++; - } + char src_full[FS_PATH_MAX]; + char dst_full[FS_PATH_MAX]; + int res = f_opendir(&dir, staging); + if (res != FR_OK) return res; - } - - while (1) { - res = f_readdir(&dir, &fno); - if (res != FR_OK || fno.fname[0] == 0) break; - - // Skip . and .. - if (fno.fname[0] == '.' && (fno.fname[1] == '\0' || (fno.fname[1] == '.' && fno.fname[2] == '\0'))) { + + while ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0]) { + if (fno.fattrib & (AM_DIR | AM_VOL)) continue; - } - - char full_path[256]; - install_combine_path(full_path, sizeof(full_path), path, fno.fname); - - if (fno.fattrib & AM_DIR) { - res = folder_delete_progress_recursive(full_path, deleted, total, start_x, start_y, display_name, last_percent); - (*deleted)++; - } else { - // Clear read-only attribute if set - if (fno.fattrib & AM_RDO) { - f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO); - } - res = f_unlink(full_path); - if (res == FR_OK || res == FR_NO_FILE) { - (*deleted)++; - } - } - - // Update progress every 10 items or when percentage changes - if ((*deleted % 10 == 0 || total == 0) && (res == FR_OK || res == FR_NO_FILE)) { - int percent = total > 0 ? (*deleted * 100) / total : 0; - if (percent != *last_percent || *deleted % 50 == 0) { - gfx_con_setpos(start_x, start_y); - install_set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [%3d%%] (%d/%d)", display_name, percent, *deleted, total); - install_set_color(COLOR_WHITE); - *last_percent = percent; - } - } - - if (res != FR_OK && res != FR_NO_FILE) break; + s_printf(src_full, "%s/%s", staging, fno.fname); + s_printf(dst_full, "%s%s", dst_root, fno.fname); + res = fs_move_file(src_full, dst_full); + if (res != FR_OK) + break; } - + f_closedir(&dir); - - if (res == FR_OK || res == FR_NO_FILE) { - // Check and clear read-only attribute on directory if set - FILINFO dir_info; - if (f_stat(path, &dir_info) == FR_OK && (dir_info.fattrib & AM_RDO)) { - f_chmod(path, dir_info.fattrib & ~AM_RDO, AM_RDO); - } - res = f_unlink(path); - } - return res; } -// Progress-aware delete for a single directory (used for large directories) -int folder_delete_single_with_progress(const char *path, const char *display_name) { - int deleted = 0; - int total = 0; - int last_percent = -1; - u32 start_x, start_y; - int res; - - // Count total items first - total = install_count_directory_items(path); - - if (total == 0) { - // Empty directory, just delete it - FILINFO fno; - if (f_stat(path, &fno) == FR_OK) { - if (fno.fattrib & AM_RDO) { - f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO); - } - res = f_unlink(path); - } else { - res = FR_NO_FILE; - } - return res; - } - - // Save cursor position - gfx_con_getpos(&start_x, &start_y); - - // Show initial status - install_set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [ 0%%] (0/%d)", display_name, total); - install_set_color(COLOR_WHITE); - - // Use the shared recursive progress function - res = folder_delete_progress_recursive(path, &deleted, total, start_x, start_y, display_name, &last_percent); - - // Final update - gfx_con_setpos(start_x, start_y); - if (res == FR_OK || res == FR_NO_FILE) { - install_set_color(COLOR_GREEN); - gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", display_name, deleted, total); - install_set_color(COLOR_WHITE); - } else { - install_set_color(COLOR_RED); - gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", display_name); - install_set_color(COLOR_WHITE); - } - - return res; -} +#define LARGE_DIR_ITEMS 50 -// Delete a list of paths with progress tracking // Delete multiple path lists under one label; one progress line "Bereinige: folder/ [p%] (d/t)". // Varargs: path lists (const char**), terminated by NULL. int delete_path_lists_grouped(const char *folder_display_name, ...) { va_list ap; const char *entries[GROUPED_DELETE_MAX_ENTRIES]; + bool is_dir[GROUPED_DELETE_MAX_ENTRIES]; int n_entries = 0; int deleted = 0; int failed = 0; u32 start_x, start_y; - int last_percent = -1; - int res; + FILINFO fno; + // One f_stat per entry: existence + type in a single call. va_start(ap, folder_display_name); const char **list; while (n_entries < GROUPED_DELETE_MAX_ENTRIES && (list = va_arg(ap, const char **)) != NULL) { for (int i = 0; list[i] != NULL && n_entries < GROUPED_DELETE_MAX_ENTRIES; i++) { - if (install_path_exists(list[i]) && !install_path_blocked_by_user_preserve(list[i])) - entries[n_entries++] = list[i]; + if (f_stat(list[i], &fno) != FR_OK || install_path_blocked_by_user_preserve(list[i])) + continue; + entries[n_entries] = list[i]; + is_dir[n_entries] = (fno.fattrib & AM_DIR) != 0; + n_entries++; } } va_end(ap); @@ -471,167 +214,42 @@ int delete_path_lists_grouped(const char *folder_display_name, ...) { if (n_entries == 0) return FR_OK; - int total_paths = n_entries; gfx_con_getpos(&start_x, &start_y); install_set_color(COLOR_CYAN); - gfx_printf(" Bereinige: %s [ 0%%] (0/%d)", folder_display_name, total_paths); + gfx_printf(" Bereinige: %s [ 0%%] (0/%d)", folder_display_name, n_entries); install_set_color(COLOR_WHITE); for (int i = 0; i < n_entries; i++) { - const char *path = entries[i]; - FILINFO fno; - if (f_stat(path, &fno) != FR_OK) - continue; - - const char *item_name = strrchr(path, '/'); - if (item_name) item_name++; - else item_name = path; - - if (fno.fattrib & AM_DIR) { - int item_count = install_count_directory_items(path); - if (item_count > 50) { - gfx_printf("\n"); - res = folder_delete_single_with_progress(path, item_name); - } else { - res = folder_delete(path); - } + int res; + // Big folders get their own progress line; the probe stops after 51 entries. + if (is_dir[i] && fs_count_items(entries[i], LARGE_DIR_ITEMS + 1) > LARGE_DIR_ITEMS) { + gfx_printf("\n"); + res = install_delete_tree(entries[i]); } else { - if (fno.fattrib & AM_RDO) - f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO); - res = f_unlink(path); + res = fs_delete_tree(entries[i], NULL, NULL); } - log_write("DELETE %s: %s\n", path, fs_error_str(res)); - if (res == FR_OK || res == FR_NO_FILE) + if (res == FR_OK) deleted++; else failed++; - int percent = (deleted * 100) / total_paths; - if (percent != last_percent || deleted % 5 == 0) { - gfx_con_setpos(start_x, start_y); - install_set_color(COLOR_CYAN); - gfx_printf(" Bereinige: %s [%3d%%] (%d/%d)", folder_display_name, percent, deleted, total_paths); - install_set_color(COLOR_WHITE); - last_percent = percent; - } + gfx_con_setpos(start_x, start_y); + install_set_color(COLOR_CYAN); + gfx_printf(" Bereinige: %s [%3d%%] (%d/%d)", folder_display_name, + ((deleted + failed) * 100) / n_entries, deleted, n_entries); + install_set_color(COLOR_WHITE); } gfx_con_setpos(start_x, start_y); if (failed == 0) { install_set_color(COLOR_GREEN); - gfx_printf(" Bereinige: %s [100%%] (%d/%d) - Fertig!\n", folder_display_name, deleted, total_paths); - install_set_color(COLOR_WHITE); + gfx_printf(" Bereinige: %s [100%%] (%d/%d) - Fertig!\n", folder_display_name, deleted, n_entries); } else { install_set_color(COLOR_ORANGE); - gfx_printf(" Bereinige: %s [%3d%%] (%d/%d) - %d Fehler\n", folder_display_name, last_percent, deleted, total_paths, failed); - install_set_color(COLOR_WHITE); + gfx_printf(" Bereinige: %s (%d/%d) - %d Fehler\n", folder_display_name, deleted, n_entries, failed); } - return (failed == 0) ? FR_OK : FR_DISK_ERR; -} - -int delete_path_list(const char* paths[], const char* description) { - int res; - int deleted = 0; - int failed = 0; - int total_paths = 0; - u32 start_x, start_y; - int last_percent = -1; - - // Count total paths first (only those we will delete) - for (int i = 0; paths[i] != NULL; i++) { - if (install_path_exists(paths[i]) && !install_path_blocked_by_user_preserve(paths[i])) { - total_paths++; - } - } - - if (total_paths == 0) { - return FR_OK; // Nothing to delete - } - - // Save cursor position - gfx_con_getpos(&start_x, &start_y); - - // Show initial status - install_set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [ 0%%] (0/%d)", description, total_paths); install_set_color(COLOR_WHITE); - - for (int i = 0; paths[i] != NULL; i++) { - if (install_path_exists(paths[i]) && !install_path_blocked_by_user_preserve(paths[i])) { - FILINFO fno; - f_stat(paths[i], &fno); - - // Extract just the folder/file name for display - const char *item_name = strrchr(paths[i], '/'); - if (item_name) { - item_name++; // Skip the '/' - } else { - item_name = paths[i]; - } - - if (fno.fattrib & AM_DIR) { - // Check if directory has many items - if so, show detailed progress - int item_count = install_count_directory_items(paths[i]); - if (item_count > 50) { - // Large directory - show individual progress - // Move to new line for the detailed progress - gfx_printf("\n"); - res = folder_delete_single_with_progress(paths[i], item_name); - } else { - // Small directory - use regular delete - res = folder_delete(paths[i]); - } - } else { - // Clear read-only attribute if set - if (fno.fattrib & AM_RDO) { - f_chmod(paths[i], fno.fattrib & ~AM_RDO, AM_RDO); - } - res = f_unlink(paths[i]); - } - - if (res == FR_OK || res == FR_NO_FILE) { - deleted++; - } else { - failed++; - } - - // Update list-level progress (only if not showing detailed progress) - if (fno.fattrib & AM_DIR && install_count_directory_items(paths[i]) <= 50) { - int percent = (deleted * 100) / total_paths; - if (percent != last_percent || deleted % 5 == 0) { - gfx_con_setpos(start_x, start_y); - install_set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [%3d%%] (%d/%d)", description, percent, deleted, total_paths); - install_set_color(COLOR_WHITE); - last_percent = percent; - } - } else if (!(fno.fattrib & AM_DIR)) { - // Update for files - int percent = (deleted * 100) / total_paths; - if (percent != last_percent || deleted % 5 == 0) { - gfx_con_setpos(start_x, start_y); - install_set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [%3d%%] (%d/%d)", description, percent, deleted, total_paths); - install_set_color(COLOR_WHITE); - last_percent = percent; - } - } - } - } - - // Final update - gfx_con_setpos(start_x, start_y); - if (failed == 0) { - install_set_color(COLOR_GREEN); - gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", description, deleted, total_paths); - install_set_color(COLOR_WHITE); - } else { - install_set_color(COLOR_ORANGE); - gfx_printf(" Loesche: %s [%3d%%] (%d/%d) - %d Fehler\n", description, last_percent, deleted, total_paths, failed); - install_set_color(COLOR_WHITE); - } - return (failed == 0) ? FR_OK : FR_DISK_ERR; } @@ -1228,15 +846,16 @@ static int install_apply_hekate_ams_pro(void) if (fno.fname[0] == '.') continue; - char src[256]; + // extras/ is deleted afterwards, so moving is enough. + char src[FS_PATH_MAX]; s_printf(src, "%s/%s", HEKATE_AMS_PRO_SRC, fno.fname); if (fno.fattrib & AM_DIR) - res = folder_copy(src, "sd:/"); + res = fs_move_tree(src, "sd:/", NULL, NULL); else { - char dst[256]; + char dst[FS_PATH_MAX]; s_printf(dst, "sd:/%s", fno.fname); - res = file_copy(src, dst); + res = fs_move_file(src, dst); } if (res != FR_OK) break; @@ -1463,6 +1082,7 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { } int res = perform_installation_steps(pack_variant, mode, &dbg); + fs_release_buffers(); if (log_is_enabled()) { log_write("\n=== Ende: res=%d (%s) ===\n", (u32)res, fs_error_str(res)); diff --git a/source/install.h b/source/install.h index b63363b..5d39790 100644 --- a/source/install.h +++ b/source/install.h @@ -39,11 +39,7 @@ void install_preserve_update_cleanup_end(void); void install_set_color(u32 color); void install_check_and_clear_screen_if_needed(void); bool install_path_exists(const char *path); -int install_count_directory_items(const char *path); -void install_combine_path(char *result, size_t size, const char *base, const char *add); -int install_copy_staging_root_files(const char *staging, const char *dst_root); -int delete_path_list(const char* paths[], const char* description); +int install_move_staging_root_files(const char *staging, const char *dst_root); +int install_move_tree(const char *src, const char *dst_parent, const char *display_name); +int install_delete_tree(const char *path); int delete_path_lists_grouped(const char *folder_display_name, ...); -int folder_delete_single_with_progress(const char *path, const char *display_name); -int folder_delete_progress_recursive(const char *path, int *deleted, int total, u32 start_x, u32 start_y, const char *display_name, int *last_percent); -int folder_copy_with_progress_v2(const char *src, const char *dst, const char *display_name); diff --git a/source/install_clean.c b/source/install_clean.c index 09b7837..f230eb5 100644 --- a/source/install_clean.c +++ b/source/install_clean.c @@ -29,7 +29,6 @@ #define set_color install_set_color #define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed #define path_exists install_path_exists -#define count_directory_items install_count_directory_items // Clean mode: Backup user data int clean_mode_backup(void) { @@ -102,7 +101,7 @@ int clean_mode_restore(void) { set_color(COLOR_GREEN); gfx_printf(" [OK] Wiederherstellung abgeschlossen\n"); set_color(COLOR_WHITE); - delete_path_list(clean_post_restore_dirs_to_delete, "switch/ (nach Restore)"); + delete_path_lists_grouped("switch/ (nach Restore)", clean_post_restore_dirs_to_delete, NULL); } cleanup_backup(); return res; @@ -132,30 +131,9 @@ int cleanup_other_staging_directories(omninx_variant_t installed_variant) { gfx_printf("\nEntferne weiteren Installationsordner...\n"); set_color(COLOR_WHITE); - int total = count_directory_items(staging); - u32 start_x, start_y; - gfx_con_getpos(&start_x, &start_y); - - const char *folder_name = strrchr(staging, '/'); - if (folder_name) folder_name++; - else folder_name = staging; - - set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [ 0%%] (0/%d)", folder_name, total); - set_color(COLOR_WHITE); - - int deleted = 0; - int last_percent = -1; - int res = folder_delete_progress_recursive(staging, &deleted, total, start_x, start_y, folder_name, &last_percent); - - gfx_con_setpos(start_x, start_y); - if (res == FR_OK) { - set_color(COLOR_GREEN); - gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", folder_name, deleted, total); - set_color(COLOR_WHITE); - } else { + int res = install_delete_tree(staging); + if (res != FR_OK) { set_color(COLOR_ORANGE); - gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", folder_name); gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res); set_color(COLOR_WHITE); } diff --git a/source/install_update.c b/source/install_update.c index 28a87bc..2fdeef0 100644 --- a/source/install_update.c +++ b/source/install_update.c @@ -28,7 +28,6 @@ #define set_color install_set_color #define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed #define path_exists install_path_exists -#define count_directory_items install_count_directory_items // Update mode: Cleanup specific directories/files int update_mode_cleanup(omninx_variant_t variant) { @@ -73,11 +72,18 @@ int update_mode_cleanup(omninx_variant_t variant) { return FR_OK; } -// Update mode: Copy files from staging +// Pack folders installed from staging (SaltySD only ships with OC). +static const char *const pack_folders[] = { + "atmosphere", "bootloader", "config", "switch", "themes", + "warmboot_mariko", "hwtest", "SaltySD", "extras", +}; + +// Update mode: Move files from staging (rename on the same SD card, copy only as fallback) int update_mode_install(omninx_variant_t variant) { int res; const char* staging = get_staging_path(variant); - char src_path[256]; + char src_path[FS_PATH_MAX]; + char label[32]; if (!staging) { return FR_INVALID_PARAMETER; @@ -86,63 +92,35 @@ int update_mode_install(omninx_variant_t variant) { check_and_clear_screen_if_needed(); set_color(COLOR_YELLOW); - gfx_printf("Dateien werden kopiert...\n"); + gfx_printf("Dateien werden installiert...\n"); set_color(COLOR_WHITE); - s_printf(src_path, "%s/atmosphere", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "atmosphere/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/bootloader", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "bootloader/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/config", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "config/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/switch", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "switch/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/themes", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "themes/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/warmboot_mariko", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "warmboot_mariko/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - s_printf(src_path, "%s/hwtest", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "hwtest/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - - if (variant == VARIANT_OC) { - s_printf(src_path, "%s/SaltySD", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "SaltySD/"); - if (res != FR_OK && res != FR_NO_FILE) return res; + for (u32 i = 0; i < sizeof(pack_folders) / sizeof(pack_folders[0]); i++) { + if (!strcmp(pack_folders[i], "SaltySD") && variant != VARIANT_OC) + continue; + s_printf(src_path, "%s/%s", staging, pack_folders[i]); + s_printf(label, "%s/", pack_folders[i]); + res = install_move_tree(src_path, "sd:/", label); + if (res != FR_OK && res != FR_NO_FILE) + return res; } - s_printf(src_path, "%s/extras", staging); - res = folder_copy_with_progress_v2(src_path, "sd:/", "extras/"); - if (res != FR_OK && res != FR_NO_FILE) return res; - set_color(COLOR_CYAN); - gfx_printf(" Kopiere Root-Dateien...\n"); + gfx_printf(" Installiere Root-Dateien...\n"); set_color(COLOR_WHITE); - res = install_copy_staging_root_files(staging, "sd:/"); + res = install_move_staging_root_files(staging, "sd:/"); if (res != FR_OK) return res; set_color(COLOR_GREEN); - gfx_printf(" Kopie abgeschlossen!\n"); + gfx_printf(" Installation der Dateien abgeschlossen!\n"); set_color(COLOR_WHITE); return FR_OK; } -// Remove staging directory after installation +// Remove staging directory after installation (mostly empty folders after the move) int cleanup_staging_directory(omninx_variant_t pack_variant) { const char* staging = get_staging_path(pack_variant); if (!staging) { @@ -151,40 +129,18 @@ int cleanup_staging_directory(omninx_variant_t pack_variant) { check_and_clear_screen_if_needed(); - if (path_exists(staging)) { - set_color(COLOR_YELLOW); - gfx_printf("\nEntferne Installationsordner...\n"); + if (!path_exists(staging)) + return FR_OK; + + set_color(COLOR_YELLOW); + gfx_printf("\nEntferne Installationsordner...\n"); + set_color(COLOR_WHITE); + + int res = install_delete_tree(staging); + if (res != FR_OK) { + set_color(COLOR_ORANGE); + gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res); set_color(COLOR_WHITE); - - int total = count_directory_items(staging); - u32 start_x, start_y; - gfx_con_getpos(&start_x, &start_y); - - const char *folder_name = strrchr(staging, '/'); - if (folder_name) folder_name++; - else folder_name = staging; - - set_color(COLOR_CYAN); - gfx_printf(" Loesche: %s [ 0%%] (0/%d)", folder_name, total); - set_color(COLOR_WHITE); - - int deleted = 0; - int last_percent = -1; - int res = folder_delete_progress_recursive(staging, &deleted, total, start_x, start_y, folder_name, &last_percent); - - gfx_con_setpos(start_x, start_y); - if (res == FR_OK) { - set_color(COLOR_GREEN); - gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", folder_name, deleted, total); - set_color(COLOR_WHITE); - } else { - set_color(COLOR_ORANGE); - gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", folder_name); - gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res); - set_color(COLOR_WHITE); - } - return res; } - - return FR_OK; + return res; }