/* * OmniNX Installer - Installation Logic */ #include "install.h" #include "fs.h" #include "screenshot.h" #include "version.h" #include "gfx.h" #include #include #include #include #include #include #include #include #include #include #define GROUPED_DELETE_MAX_ENTRIES 512 #ifndef VERSION #define VERSION "1.0.0" #endif // Color definitions (some already defined in types.h, but we override for consistency) #undef COLOR_CYAN #undef COLOR_WHITE #undef COLOR_GREEN #undef COLOR_YELLOW #undef COLOR_ORANGE #undef COLOR_RED #define COLOR_CYAN 0xFF00FFFF #define COLOR_WHITE 0xFFFFFFFF #define COLOR_GREEN 0xFF00FF00 #define COLOR_YELLOW 0xFFFFDD00 #define COLOR_ORANGE 0xFF00A5FF #define COLOR_RED 0xFFFF0000 static inline bool cancel_combo_pressed(u8 btn) { return (btn & (BTN_VOL_UP | BTN_VOL_DOWN)) == (BTN_VOL_UP | BTN_VOL_DOWN); } void install_set_color(u32 color) { gfx_con_setcol(color, gfx_con.fillbg, gfx_con.bgcol); } /* Same top bar as main.c print_header() (without full screen clear). */ static void install_print_main_header(void) { install_set_color(COLOR_CYAN); gfx_printf("===================================\n"); gfx_printf(" OmniNX Installer Payload v%s\n", VERSION); gfx_printf("===================================\n\n"); install_set_color(COLOR_WHITE); } // Check if we need to clear screen (when getting close to bottom) void install_check_and_clear_screen_if_needed(void) { // In the gfx system: // - gfx_con.x is the vertical position (line number, increments by 16 per line) // - gfx_con.y is the horizontal position (pixels from left) // - Screen is 720px wide, 1280px tall // - Font is 16px, so we have 720/16 = 45 lines before wrapping // - When gfx_con.x > 720-16, it wraps to x=0, causing overwrite // Clear when we're past ~35 lines (35 * 16 = 560 pixels) to leave room if (gfx_con.x > 35 * 16) { // Clear screen and reset position gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); install_print_main_header(); } } // Check if file/directory exists bool install_path_exists(const char *path) { FILINFO fno; 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; } // 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); } else { // No trailing slash, add one s_printf(result, "%s/%s", base, add); } } /* 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; } // 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); 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 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 res = folder_copy_progress_recursive(src, dst, &copied, total, start_x, start_y, display_name, &last_percent); // 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_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); 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) { 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)++; } 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'))) { 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; } 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; } // 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]; int n_entries = 0; int deleted = 0; int failed = 0; u32 start_x, start_y; int last_percent = -1; int res; 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])) entries[n_entries++] = list[i]; } } va_end(ap); 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); 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); } } else { if (fno.fattrib & AM_RDO) f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO); res = f_unlink(path); } if (res == FR_OK || res == FR_NO_FILE) 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); 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); } 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); } 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 for (int i = 0; paths[i] != NULL; i++) { if (install_path_exists(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])) { 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; } #define HEKATE_8GB_SRC "sd:/bootloader/hekate_8gb.bin" #define PAYLOAD_BIN_DST "sd:/payload.bin" #define UPDATE_BIN_DST "sd:/bootloader/update.bin" #define RAM_CONFIG_PATH "sd:/config/omninx/ram_config.ini" enum { RAM_READ_OK = 0, RAM_READ_NEED_MENU = 1 }; static void unlink_ignore_err(const char *path) { FILINFO fno; if (f_stat(path, &fno) != FR_OK) return; if (fno.fattrib & AM_RDO) f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO); f_unlink(path); } static void ram_config_ensure_parent_dirs(void) { f_mkdir("sd:/config"); f_mkdir("sd:/config/omninx"); } static int ram_config_write(bool use_8gb) { ram_config_ensure_parent_dirs(); FIL f; if (f_open(&f, RAM_CONFIG_PATH, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) return FR_DISK_ERR; static const char sec[] = "[Ram]\n"; static const char line0[] = "8gb=0\n"; static const char line1[] = "8gb=1\n"; UINT bw; f_write(&f, sec, sizeof(sec) - 1, &bw); f_write(&f, use_8gb ? line1 : line0, sizeof(line0) - 1, &bw); f_close(&f); return FR_OK; } static void ram_config_free_sections(link_t *sections) { LIST_FOREACH_ENTRY(ini_sec_t, sec, sections, link) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) { if (kv->key) free(kv->key); if (kv->val) free(kv->val); } if (sec->name) free(sec->name); } } /* Valid [Ram] 8gb=0|1 → silent; missing file or invalid → menu. */ static int read_ram_config_silent(const char *path, bool *use_8gb) { if (!install_path_exists(path)) return RAM_READ_NEED_MENU; link_t sections; list_init(§ions); if (ini_parse(§ions, (char *)path, false) != 1) { ram_config_free_sections(§ions); return RAM_READ_NEED_MENU; } bool found = false; bool eight = false; LIST_FOREACH_ENTRY(ini_sec_t, sec, §ions, link) { if (sec->name && !strcmp(sec->name, "Ram")) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) { if (kv->key && kv->val && !strcmp(kv->key, "8gb")) { if (!strcmp(kv->val, "0")) { found = true; eight = false; } else if (!strcmp(kv->val, "1")) { found = true; eight = true; } break; } } break; } } ram_config_free_sections(§ions); if (!found) return RAM_READ_NEED_MENU; *use_8gb = eight; return RAM_READ_OK; } /* Same navigation as main.c multi-variant menu; returns false if user aborts (payload/reboot). */ static bool install_ram_config_menu(bool *use_8gb) { jc_init_hw(); while (btn_read() & BTN_POWER) msleep(50); int selected = 0; int prev_selected = 0; bool confirmed = false; const int n = 2; const char *labels[2] = { "4 GB Konsole (Standard)", "8 GB Konsole", }; gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); install_print_main_header(); install_set_color(COLOR_YELLOW); gfx_printf("RAM / Hekate: Konsole-Typ\n\n"); install_set_color(COLOR_RED); gfx_printf("Wenn du dir absolut unsicher bist, waehle unbedingt 4 GB!\n"); gfx_printf("Eine falsche 8-GB-Wahl kann Probleme verursachen.\n\n"); install_set_color(COLOR_WHITE); u32 menu_x, menu_y; gfx_con_getpos(&menu_x, &menu_y); for (int i = 0; i < n; i++) { if (i == selected) { install_set_color(COLOR_GREEN); gfx_printf(" > %s\n", labels[i]); install_set_color(COLOR_WHITE); } else { gfx_printf(" %s\n", labels[i]); } } gfx_printf("\n"); install_set_color(COLOR_CYAN); gfx_printf("D-Pad / Vol+/-: Auswahl | A oder Power: Bestaetigen\n"); gfx_printf("Vol+ und Vol- gleichzeitig: Abbrechen (Hekate)\n"); install_set_color(COLOR_WHITE); bool prev_up = false, prev_down = false; bool menu_aborted = false; while (!confirmed && !menu_aborted) { if (selected != prev_selected) { gfx_con_setpos(menu_x, menu_y + (u32)prev_selected * 16); install_set_color(COLOR_WHITE); gfx_printf(" %s\n", labels[prev_selected]); gfx_con_setpos(menu_x, menu_y + (u32)selected * 16); install_set_color(COLOR_GREEN); gfx_printf(" > %s\n", labels[selected]); install_set_color(COLOR_WHITE); prev_selected = selected; } jc_gamepad_rpt_t *jc = joycon_poll(); u8 btn = btn_read(); if (jc && jc->cap) take_screenshot(); if (cancel_combo_pressed(btn)) { menu_aborted = true; break; } bool cur_up = false, cur_down = false; if (jc) { cur_up = (jc->up != 0) && !jc->down; cur_down = (jc->down != 0) && !jc->up; } if (btn & BTN_VOL_UP) cur_up = true; if (btn & BTN_VOL_DOWN) cur_down = true; if (cur_up && !prev_up) selected = (selected - 1 + n) % n; else if (cur_down && !prev_down) selected = (selected + 1) % n; else if (jc && jc->a) confirmed = true; prev_up = cur_up; prev_down = cur_down; if (btn & BTN_POWER) confirmed = true; msleep(50); } if (menu_aborted) { gfx_printf("\n"); install_set_color(COLOR_YELLOW); gfx_printf("Abgebrochen. Starte Hekate...\n"); install_set_color(COLOR_WHITE); msleep(500); installer_launch_hekate_payload(); return false; } *use_8gb = (selected == 1); ram_config_write(*use_8gb); gfx_clear_grey(0x1B); gfx_con_setpos(0, 0); install_print_main_header(); return true; } /* After pack copy: use ram_config.ini [Ram] 8gb=0|1 if present; else menu, write file, then apply. No fuse autodetect. */ static void install_hekate_8gb_post_copy(void) { if (!install_path_exists(HEKATE_8GB_SRC)) return; bool use_8gb; if (read_ram_config_silent(RAM_CONFIG_PATH, &use_8gb) != RAM_READ_OK) { if (!install_ram_config_menu(&use_8gb)) return; } if (use_8gb) { int r1 = file_copy(HEKATE_8GB_SRC, PAYLOAD_BIN_DST); int r2 = file_copy(HEKATE_8GB_SRC, UPDATE_BIN_DST); if (r1 == FR_OK && r2 == FR_OK) unlink_ignore_err(HEKATE_8GB_SRC); } else { unlink_ignore_err(HEKATE_8GB_SRC); } } // Main installation function int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) { int res; if (mode == INSTALL_MODE_UPDATE) { // Update mode: selective cleanup then install install_set_color(COLOR_YELLOW); gfx_printf("Schritt 1: Bereinigung...\n"); install_set_color(COLOR_WHITE); res = update_mode_cleanup(pack_variant); if (res != FR_OK) return res; install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); gfx_printf("Schritt 2: Dateien kopieren...\n"); install_set_color(COLOR_WHITE); res = update_mode_install(pack_variant); if (res != FR_OK) return res; install_hekate_8gb_post_copy(); install_check_and_clear_screen_if_needed(); // Remove staging directory (installed pack) res = cleanup_staging_directory(pack_variant); if (res != FR_OK) return res; // Remove other detected install directories (Standard/Light/OC) that were on SD res = cleanup_other_staging_directories(pack_variant); return res; } else { // Clean mode: backup, wipe, restore, install install_set_color(COLOR_YELLOW); gfx_printf("Schritt 1: Sichere Benutzerdaten...\n"); install_set_color(COLOR_WHITE); res = clean_mode_backup(); if (res != FR_OK) return res; install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); gfx_printf("Schritt 2: Bereinige alte Installation...\n"); install_set_color(COLOR_WHITE); res = clean_mode_wipe(); if (res != FR_OK) return res; install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); gfx_printf("Schritt 3: Stelle Benutzerdaten wieder her...\n"); install_set_color(COLOR_WHITE); res = clean_mode_restore(); if (res != FR_OK) return res; install_check_and_clear_screen_if_needed(); gfx_printf("\n"); install_set_color(COLOR_YELLOW); gfx_printf("Schritt 4: Dateien kopieren...\n"); install_set_color(COLOR_WHITE); res = clean_mode_install(pack_variant); if (res != FR_OK) return res; install_hekate_8gb_post_copy(); install_check_and_clear_screen_if_needed(); // Remove staging directory (installed pack) res = cleanup_staging_directory(pack_variant); if (res != FR_OK) return res; // Remove other detected install directories (Standard/Light/OC) that were on SD res = cleanup_other_staging_directories(pack_variant); return res; } }