The variant menu in main.c and the RAM / Hekate-AMS menus in install.c were three copies of the same input loop; install_menu_select() replaces them with identical behaviour (D-pad/Vol to move, A/Power to confirm, Vol+ and Vol- to abort, Capture for screenshots).
1178 lines
38 KiB
C
1178 lines
38 KiB
C
/*
|
|
* OmniNX Installer - Installation Logic
|
|
*/
|
|
|
|
#include "install.h"
|
|
#include "backup.h"
|
|
#include "fs.h"
|
|
#include "screenshot.h"
|
|
#include "version.h"
|
|
#include "gfx.h"
|
|
#include <libs/fatfs/ff.h>
|
|
#include <input/joycon.h>
|
|
#include <mem/heap.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <utils/btn.h>
|
|
#include <utils/ini.h>
|
|
#include <utils/list.h>
|
|
#include <utils/sprintf.h>
|
|
#include <utils/util.h>
|
|
|
|
#define GROUPED_DELETE_MAX_ENTRIES 512
|
|
|
|
static bool install_path_blocked_by_user_preserve(const char *path);
|
|
|
|
// 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);
|
|
}
|
|
|
|
/* ---- 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);
|
|
}
|
|
|
|
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 {
|
|
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);
|
|
}
|
|
|
|
static const char *install_basename(const char *path) {
|
|
const char *slash = strrchr(path, '/');
|
|
return slash ? slash + 1 : path;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Move staging/<name> into dst_parent/<name>. 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;
|
|
}
|
|
|
|
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(" Quelle: %s\n Ziel: %s\n", src, dst_parent);
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* 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;
|
|
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 ((res = f_readdir(&dir, &fno)) == FR_OK && fno.fname[0]) {
|
|
if (fno.fattrib & (AM_DIR | AM_VOL))
|
|
continue;
|
|
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);
|
|
return res;
|
|
}
|
|
|
|
#define LARGE_DIR_ITEMS 50
|
|
|
|
// 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;
|
|
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 (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);
|
|
|
|
if (n_entries == 0)
|
|
return FR_OK;
|
|
|
|
gfx_con_getpos(&start_x, &start_y);
|
|
install_set_color(COLOR_CYAN);
|
|
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++) {
|
|
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 {
|
|
res = fs_delete_tree(entries[i], NULL, NULL);
|
|
}
|
|
|
|
if (res == FR_OK)
|
|
deleted++;
|
|
else
|
|
failed++;
|
|
|
|
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, n_entries);
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf(" Bereinige: %s (%d/%d) - %d Fehler\n", folder_display_name, deleted, n_entries, failed);
|
|
}
|
|
install_set_color(COLOR_WHITE);
|
|
return (failed == 0) ? FR_OK : FR_DISK_ERR;
|
|
}
|
|
|
|
#define EXTRAS_DIR "sd:/extras"
|
|
#define HEKATE_8GB_SRC EXTRAS_DIR "/hekate_8gb.bin"
|
|
#define HEKATE_PRO_8GB_SRC EXTRAS_DIR "/hekate-pro_8gb.bin"
|
|
#define HEKATE_AMS_PRO_SRC EXTRAS_DIR "/hekate-ams-pro"
|
|
#define PAYLOAD_BIN_DST "sd:/payload.bin"
|
|
#define UPDATE_BIN_DST "sd:/bootloader/update.bin"
|
|
#define INSTALL_CONFIG_INI "sd:/config/omninx/config.ini"
|
|
#define INSTALL_CONFIG_INI_LEGACY "sd:/config/omninx/ram_config.ini"
|
|
|
|
enum {
|
|
INSTALL_CONFIG_OK = 0,
|
|
INSTALL_CONFIG_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 install_config_ensure_parent_dirs(void)
|
|
{
|
|
f_mkdir("sd:/config");
|
|
f_mkdir("sd:/config/omninx");
|
|
}
|
|
|
|
static void install_ini_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);
|
|
}
|
|
}
|
|
|
|
static bool install_config_read_key_bool(const char *path, const char *section, const char *key, bool *out)
|
|
{
|
|
FIL f;
|
|
if (f_open(&f, path, FA_READ) != FR_OK)
|
|
return false;
|
|
|
|
char line[96];
|
|
bool in_section = false;
|
|
bool found = false;
|
|
|
|
while (f_gets(line, sizeof(line), &f)) {
|
|
size_t n = strlen(line);
|
|
while (n > 0 && (line[n - 1] == '\n' || line[n - 1] == '\r'))
|
|
line[--n] = '\0';
|
|
if (n == 0)
|
|
continue;
|
|
|
|
if (line[0] == '[') {
|
|
in_section = false;
|
|
char *end = strchr(line, ']');
|
|
if (!end)
|
|
continue;
|
|
*end = '\0';
|
|
if (!strcmp(line + 1, section))
|
|
in_section = true;
|
|
continue;
|
|
}
|
|
|
|
if (!in_section)
|
|
continue;
|
|
|
|
char *eq = strchr(line, '=');
|
|
if (!eq)
|
|
continue;
|
|
*eq = '\0';
|
|
if (strcmp(line, key) != 0)
|
|
continue;
|
|
|
|
const char *val = eq + 1;
|
|
if (!strcmp(val, "0")) {
|
|
*out = false;
|
|
found = true;
|
|
} else if (!strcmp(val, "1")) {
|
|
*out = true;
|
|
found = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
f_close(&f);
|
|
return found;
|
|
}
|
|
|
|
/* Valid [Ram] 8gb=0|1 in config.ini → silent; else menu. */
|
|
static int install_config_read_ram(bool *use_8gb)
|
|
{
|
|
if (!install_path_exists(INSTALL_CONFIG_INI))
|
|
return INSTALL_CONFIG_NEED_MENU;
|
|
if (install_config_read_key_bool(INSTALL_CONFIG_INI, "Ram", "8gb", use_8gb))
|
|
return INSTALL_CONFIG_OK;
|
|
return INSTALL_CONFIG_NEED_MENU;
|
|
}
|
|
|
|
/* Valid [HekateAms] pro=0|1 in config.ini → silent; else menu. */
|
|
static int install_config_read_hekate_ams(bool *use_pro)
|
|
{
|
|
if (!install_path_exists(INSTALL_CONFIG_INI))
|
|
return INSTALL_CONFIG_NEED_MENU;
|
|
if (install_config_read_key_bool(INSTALL_CONFIG_INI, "HekateAms", "pro", use_pro))
|
|
return INSTALL_CONFIG_OK;
|
|
return INSTALL_CONFIG_NEED_MENU;
|
|
}
|
|
|
|
static int install_config_write_extras(bool need_ram, bool use_8gb, bool need_hekate_ams, bool use_pro)
|
|
{
|
|
install_config_ensure_parent_dirs();
|
|
|
|
FIL f;
|
|
if (f_open(&f, INSTALL_CONFIG_INI, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
|
|
return FR_DISK_ERR;
|
|
|
|
char line[32];
|
|
UINT bw;
|
|
|
|
if (need_ram) {
|
|
f_write(&f, "[Ram]\n", 6, &bw);
|
|
s_printf(line, "8gb=%d\n\n", use_8gb ? 1 : 0);
|
|
f_write(&f, line, (UINT)strlen(line), &bw);
|
|
}
|
|
if (need_hekate_ams) {
|
|
f_write(&f, "[HekateAms]\n", 12, &bw);
|
|
s_printf(line, "pro=%d\n", use_pro ? 1 : 0);
|
|
f_write(&f, line, (UINT)strlen(line), &bw);
|
|
}
|
|
|
|
f_close(&f);
|
|
return FR_OK;
|
|
}
|
|
|
|
/* If legacy ram_config.ini exists: port [Ram] into config.ini, then remove legacy. */
|
|
static void install_config_migrate_legacy_ram(void)
|
|
{
|
|
if (!install_path_exists(INSTALL_CONFIG_INI_LEGACY))
|
|
return;
|
|
|
|
bool use_8gb = false;
|
|
bool have_ram = install_config_read_key_bool(INSTALL_CONFIG_INI_LEGACY, "Ram", "8gb", &use_8gb);
|
|
|
|
if (have_ram) {
|
|
bool use_pro = false;
|
|
bool have_pro = install_path_exists(INSTALL_CONFIG_INI) &&
|
|
install_config_read_key_bool(INSTALL_CONFIG_INI, "HekateAms", "pro", &use_pro);
|
|
install_config_write_extras(true, use_8gb, have_pro, use_pro);
|
|
}
|
|
|
|
unlink_ignore_err(INSTALL_CONFIG_INI_LEGACY);
|
|
}
|
|
|
|
#define INSTALL_PRESERVE_INI "sd:/config/omninx/preserve.ini"
|
|
#define INSTALL_PRESERVE_MAX 96
|
|
#define INSTALL_PRESERVE_PATH_BYTES 224
|
|
|
|
static bool install_ini_kv_truth(const char *val)
|
|
{
|
|
if (!val || !val[0])
|
|
return false;
|
|
char c = val[0];
|
|
return c == '1' || c == 't' || c == 'T' || c == 'y' || c == 'Y';
|
|
}
|
|
|
|
static void preserve_strip_outer_quotes(char *s)
|
|
{
|
|
size_t n = strlen(s);
|
|
if (n >= 2 &&
|
|
((s[0] == '\'' && s[n - 1] == '\'') || (s[0] == '"' && s[n - 1] == '"'))) {
|
|
memmove(s, s + 1, n - 2);
|
|
s[n - 2] = '\0';
|
|
}
|
|
}
|
|
|
|
static void preserve_normalize_trailing_slashes(char *s)
|
|
{
|
|
size_t n = strlen(s);
|
|
while (n > 1 && s[n - 1] == '/')
|
|
s[--n] = '\0';
|
|
}
|
|
|
|
static bool path_is_same_or_descendant(const char *base, const char *path)
|
|
{
|
|
size_t lb = strlen(base);
|
|
if (strncmp(path, base, lb) != 0)
|
|
return false;
|
|
if (path[lb] == '\0')
|
|
return true;
|
|
if (path[lb] == '/')
|
|
return true;
|
|
if (lb == 4 && base[0] == 's' && base[1] == 'd' && base[2] == ':' && base[3] == '/')
|
|
return path[lb] != '\0';
|
|
return false;
|
|
}
|
|
|
|
static bool path_delete_overlaps_preserve(const char *del, const char *pres)
|
|
{
|
|
return path_is_same_or_descendant(pres, del) || path_is_same_or_descendant(del, pres);
|
|
}
|
|
|
|
static bool g_preserve_update_active;
|
|
/** Rows stored in DRAM (heap); avoids ~21KB BSS in tight IPL IRAM around 0x40008000. */
|
|
static char *g_preserve_block;
|
|
static int g_preserve_count;
|
|
|
|
static const char *preserve_get_row(int i)
|
|
{
|
|
return g_preserve_block + (size_t)i * INSTALL_PRESERVE_PATH_BYTES;
|
|
}
|
|
|
|
static bool install_path_blocked_by_user_preserve(const char *delete_path)
|
|
{
|
|
if (!g_preserve_update_active || !g_preserve_block || !delete_path)
|
|
return false;
|
|
for (int i = 0; i < g_preserve_count; i++) {
|
|
if (path_delete_overlaps_preserve(delete_path, preserve_get_row(i)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void install_preserve_update_cleanup_begin(void)
|
|
{
|
|
if (g_preserve_block) {
|
|
free(g_preserve_block);
|
|
g_preserve_block = NULL;
|
|
}
|
|
g_preserve_count = 0;
|
|
g_preserve_update_active = false;
|
|
|
|
if (!install_path_exists(INSTALL_PRESERVE_INI))
|
|
return;
|
|
|
|
g_preserve_block = calloc(INSTALL_PRESERVE_MAX, INSTALL_PRESERVE_PATH_BYTES);
|
|
if (!g_preserve_block)
|
|
return;
|
|
|
|
link_t sections;
|
|
list_init(§ions);
|
|
if (ini_parse(§ions, (char *)INSTALL_PRESERVE_INI, false) != 1) {
|
|
install_ini_free_sections(§ions);
|
|
free(g_preserve_block);
|
|
g_preserve_block = NULL;
|
|
return;
|
|
}
|
|
|
|
LIST_FOREACH_ENTRY(ini_sec_t, sec, §ions, link) {
|
|
if (!sec->name || strcmp(sec->name, "Preserve") != 0)
|
|
continue;
|
|
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
|
|
if (!kv->key || !kv->val || g_preserve_count >= INSTALL_PRESERVE_MAX)
|
|
continue;
|
|
if (!install_ini_kv_truth(kv->val))
|
|
continue;
|
|
char buf[INSTALL_PRESERVE_PATH_BYTES];
|
|
strncpy(buf, kv->key, sizeof(buf) - 1);
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
preserve_strip_outer_quotes(buf);
|
|
preserve_normalize_trailing_slashes(buf);
|
|
if (strlen(buf) < 5 || strncmp(buf, "sd:/", 4) != 0)
|
|
continue;
|
|
if (strcmp(buf, "sd:/") == 0)
|
|
continue;
|
|
|
|
bool dup = false;
|
|
for (int j = 0; j < g_preserve_count; j++) {
|
|
if (strcmp(preserve_get_row(j), buf) == 0) {
|
|
dup = true;
|
|
break;
|
|
}
|
|
}
|
|
if (dup)
|
|
continue;
|
|
|
|
char *row = (char *)preserve_get_row(g_preserve_count);
|
|
strncpy(row, buf, INSTALL_PRESERVE_PATH_BYTES - 1);
|
|
row[INSTALL_PRESERVE_PATH_BYTES - 1] = '\0';
|
|
g_preserve_count++;
|
|
}
|
|
break;
|
|
}
|
|
install_ini_free_sections(§ions);
|
|
|
|
if (g_preserve_count == 0) {
|
|
free(g_preserve_block);
|
|
g_preserve_block = NULL;
|
|
return;
|
|
}
|
|
|
|
g_preserve_update_active = true;
|
|
install_set_color(COLOR_CYAN);
|
|
gfx_printf(" preserve.ini: %d Pfad(e) von Update-Bereinigung ausgenommen\n", g_preserve_count);
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
void install_preserve_update_cleanup_end(void)
|
|
{
|
|
if (g_preserve_block) {
|
|
free(g_preserve_block);
|
|
g_preserve_block = NULL;
|
|
}
|
|
g_preserve_count = 0;
|
|
g_preserve_update_active = false;
|
|
}
|
|
|
|
/* Clear Joy-Con input and wait briefly so a held A press cannot leak into the next menu. */
|
|
static void install_joycon_reconnect(void)
|
|
{
|
|
jc_clear_input();
|
|
|
|
/* Wait until A/Power are actually released (no full power cycle). */
|
|
while (1) {
|
|
jc_gamepad_rpt_t *jc = joycon_poll();
|
|
if ((!jc || !jc->a) && !(btn_read() & BTN_POWER))
|
|
break;
|
|
jc_clear_input();
|
|
msleep(20);
|
|
}
|
|
|
|
msleep(100);
|
|
}
|
|
|
|
/* Selection menu shared by main.c and the extras menus: draws the labels at the
|
|
* current cursor plus the key hints and returns the chosen index, or -1 when
|
|
* Vol+ and Vol- are pressed together. Only the two changed lines are redrawn. */
|
|
int install_menu_select(const char *const *labels, int n)
|
|
{
|
|
jc_init_hw();
|
|
while (btn_read() & BTN_POWER)
|
|
msleep(50);
|
|
|
|
int selected = 0;
|
|
int prev_selected = 0;
|
|
u32 menu_x, menu_y;
|
|
gfx_con_getpos(&menu_x, &menu_y);
|
|
for (int i = 0; i < n; i++) {
|
|
install_set_color(i == selected ? COLOR_GREEN : COLOR_WHITE);
|
|
gfx_printf(i == selected ? " > %s\n" : " %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;
|
|
while (1) {
|
|
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))
|
|
return -1;
|
|
|
|
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) || (btn & BTN_POWER))
|
|
return selected;
|
|
|
|
prev_up = cur_up;
|
|
prev_down = cur_down;
|
|
msleep(50);
|
|
}
|
|
}
|
|
|
|
/* Extras menu wrapper: abort launches Hekate. */
|
|
static bool install_extras_choice(const char *const *labels, int n, int *choice)
|
|
{
|
|
int sel = install_menu_select(labels, n);
|
|
if (sel < 0) {
|
|
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;
|
|
}
|
|
|
|
*choice = sel;
|
|
gfx_clear_grey(0x1B);
|
|
gfx_con_setpos(0, 0);
|
|
install_print_main_header();
|
|
return true;
|
|
}
|
|
|
|
static bool install_ram_config_menu(bool *use_8gb)
|
|
{
|
|
static const char *const labels[] = {
|
|
"4GB Konsole (Standard)",
|
|
"8GB Konsole",
|
|
};
|
|
|
|
gfx_clear_grey(0x1B);
|
|
gfx_con_setpos(0, 0);
|
|
install_print_main_header();
|
|
|
|
install_set_color(COLOR_YELLOW);
|
|
gfx_printf("Hekate Payload Auswahl:\n\n");
|
|
install_set_color(COLOR_WHITE);
|
|
gfx_printf("Wenn du einen 8GB RAM-Umbau hast, waehle 8GB Konsole aus.\n");
|
|
gfx_printf("Alle anderen waehlen 4GB Konsole aus.\n\n");
|
|
install_set_color(COLOR_RED);
|
|
gfx_printf("Warnung: Solltest du keinen RAM-Umbau haben, waehle auf keinen\n");
|
|
gfx_printf("Fall 8GB aus, da du sonst Boot Probleme bekommst!!!\n\n");
|
|
install_set_color(COLOR_WHITE);
|
|
|
|
int choice;
|
|
if (!install_extras_choice(labels, 2, &choice))
|
|
return false;
|
|
*use_8gb = (choice == 1);
|
|
return true;
|
|
}
|
|
|
|
static bool install_hekate_ams_menu(bool *use_pro)
|
|
{
|
|
static const char *const labels[] = {
|
|
"Hekate / Atmosphere (Standard)",
|
|
"Hekate-Pro / Atmosphere-Pro",
|
|
};
|
|
|
|
gfx_clear_grey(0x1B);
|
|
gfx_con_setpos(0, 0);
|
|
install_print_main_header();
|
|
|
|
install_set_color(COLOR_YELLOW);
|
|
gfx_printf("Hekate / Atmosphere Auswahl:\n\n");
|
|
install_set_color(COLOR_RED);
|
|
gfx_printf("Waehle Hekate / Atmosphere (Standard) sofern du nicht weisst, was du tust.\n\n");
|
|
install_set_color(COLOR_WHITE);
|
|
|
|
int choice;
|
|
if (!install_extras_choice(labels, 2, &choice))
|
|
return false;
|
|
*use_pro = (choice == 1);
|
|
return true;
|
|
}
|
|
|
|
static bool install_extras_ram_available(void)
|
|
{
|
|
return install_path_exists(HEKATE_8GB_SRC) || install_path_exists(HEKATE_PRO_8GB_SRC);
|
|
}
|
|
|
|
static bool install_extras_hekate_ams_pro_available(void)
|
|
{
|
|
FILINFO fno;
|
|
if (f_stat(HEKATE_AMS_PRO_SRC, &fno) != FR_OK)
|
|
return false;
|
|
return (fno.fattrib & AM_DIR) != 0;
|
|
}
|
|
|
|
static bool install_extras_post_copy_needed(void)
|
|
{
|
|
return install_extras_ram_available() || install_extras_hekate_ams_pro_available();
|
|
}
|
|
|
|
static int install_apply_hekate_ams_pro(void)
|
|
{
|
|
DIR dir;
|
|
FILINFO fno;
|
|
int res = f_opendir(&dir, HEKATE_AMS_PRO_SRC);
|
|
if (res != FR_OK)
|
|
return res;
|
|
|
|
while (1) {
|
|
res = f_readdir(&dir, &fno);
|
|
if (res != FR_OK)
|
|
break;
|
|
if (fno.fname[0] == 0)
|
|
break;
|
|
if (fno.fname[0] == '.')
|
|
continue;
|
|
|
|
// 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 = fs_move_tree(src, "sd:/", NULL, NULL);
|
|
else {
|
|
char dst[FS_PATH_MAX];
|
|
s_printf(dst, "sd:/%s", fno.fname);
|
|
res = fs_move_file(src, dst);
|
|
}
|
|
if (res != FR_OK)
|
|
break;
|
|
}
|
|
|
|
f_closedir(&dir);
|
|
return res;
|
|
}
|
|
|
|
static void install_apply_8gb_payload(bool use_8gb, bool use_pro)
|
|
{
|
|
if (!use_8gb) {
|
|
unlink_ignore_err(HEKATE_8GB_SRC);
|
|
unlink_ignore_err(HEKATE_PRO_8GB_SRC);
|
|
return;
|
|
}
|
|
|
|
const char *src = use_pro ? HEKATE_PRO_8GB_SRC : HEKATE_8GB_SRC;
|
|
if (!install_path_exists(src))
|
|
return;
|
|
|
|
file_copy(src, PAYLOAD_BIN_DST);
|
|
file_copy(src, UPDATE_BIN_DST);
|
|
unlink_ignore_err(HEKATE_8GB_SRC);
|
|
unlink_ignore_err(HEKATE_PRO_8GB_SRC);
|
|
}
|
|
|
|
/* After pack copy: RAM + Hekate/AMS menus from extras; choices stored in config.ini. */
|
|
static void install_extras_post_copy(void)
|
|
{
|
|
install_config_migrate_legacy_ram();
|
|
|
|
if (!install_extras_post_copy_needed())
|
|
return;
|
|
|
|
bool need_ram = install_extras_ram_available();
|
|
bool need_hekate_ams = install_extras_hekate_ams_pro_available();
|
|
bool use_8gb = false;
|
|
bool use_pro = false;
|
|
|
|
if (need_ram) {
|
|
if (install_config_read_ram(&use_8gb) != INSTALL_CONFIG_OK) {
|
|
if (!install_ram_config_menu(&use_8gb))
|
|
return;
|
|
if (need_hekate_ams)
|
|
install_joycon_reconnect();
|
|
}
|
|
}
|
|
|
|
if (need_hekate_ams) {
|
|
if (install_config_read_hekate_ams(&use_pro) != INSTALL_CONFIG_OK) {
|
|
if (!install_hekate_ams_menu(&use_pro))
|
|
return;
|
|
}
|
|
}
|
|
|
|
install_set_color(COLOR_YELLOW);
|
|
gfx_printf("Extras: Speichere Auswahl in config.ini...\n");
|
|
install_set_color(COLOR_WHITE);
|
|
install_config_write_extras(need_ram, use_8gb, need_hekate_ams, use_pro);
|
|
|
|
if (use_pro && need_hekate_ams) {
|
|
install_set_color(COLOR_CYAN);
|
|
gfx_printf("Extras: Installiere Hekate-Pro / Atmosphere-Pro...\n");
|
|
install_set_color(COLOR_WHITE);
|
|
int res = install_apply_hekate_ams_pro();
|
|
if (res != FR_OK) {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf(" [WARN] Hekate-Pro Installation fehlgeschlagen (err=%d)\n", res);
|
|
install_set_color(COLOR_WHITE);
|
|
} else {
|
|
install_set_color(COLOR_GREEN);
|
|
gfx_printf(" [OK] Hekate-Pro / Atmosphere-Pro installiert\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
} else if (need_hekate_ams) {
|
|
install_set_color(COLOR_GREEN);
|
|
gfx_printf("Extras: Standard Hekate / Atmosphere beibehalten\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (need_ram) {
|
|
install_set_color(COLOR_CYAN);
|
|
gfx_printf("Extras: Wende RAM-Einstellung an...\n");
|
|
install_set_color(COLOR_WHITE);
|
|
install_apply_8gb_payload(use_8gb, use_pro);
|
|
install_set_color(COLOR_GREEN);
|
|
gfx_printf(" [OK] RAM-Einstellung angewendet\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (install_path_exists(EXTRAS_DIR)) {
|
|
install_set_color(COLOR_CYAN);
|
|
gfx_printf("Extras: Entferne extras/\n");
|
|
install_set_color(COLOR_WHITE);
|
|
int res = folder_delete(EXTRAS_DIR);
|
|
if (res == FR_OK) {
|
|
install_set_color(COLOR_GREEN);
|
|
gfx_printf(" [OK] extras/ entfernt\n");
|
|
install_set_color(COLOR_WHITE);
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf(" [WARN] extras/ konnte nicht entfernt werden (err=%d)\n", res);
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define INSTALL_DEBUG_INI "sd:/config/omninx/debug.ini"
|
|
#define INSTALL_LOG_PATH "sd:/config/omninx/install.log"
|
|
|
|
typedef struct {
|
|
bool active;
|
|
bool skip_clean_backup;
|
|
bool skip_clean_wipe;
|
|
bool skip_clean_restore;
|
|
bool skip_clean_install;
|
|
bool skip_clean_staging_cleanup;
|
|
bool skip_update_cleanup;
|
|
bool skip_update_install;
|
|
bool skip_update_staging_cleanup;
|
|
bool skip_update_horizon_oc;
|
|
bool skip_hekate_8gb_post_copy;
|
|
bool log;
|
|
} install_debug_opts_t;
|
|
|
|
static void install_debug_load(install_debug_opts_t *d)
|
|
{
|
|
memset(d, 0, sizeof(*d));
|
|
if (!install_path_exists(INSTALL_DEBUG_INI))
|
|
return;
|
|
|
|
link_t sections;
|
|
list_init(§ions);
|
|
if (ini_parse(§ions, (char *)INSTALL_DEBUG_INI, false) != 1) {
|
|
install_ini_free_sections(§ions);
|
|
return;
|
|
}
|
|
|
|
LIST_FOREACH_ENTRY(ini_sec_t, sec, §ions, link) {
|
|
if (!sec->name || strcmp(sec->name, "Debug") != 0)
|
|
continue;
|
|
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
|
|
if (!kv->key || !kv->val)
|
|
continue;
|
|
if (strcmp(kv->key, "skip_clean_backup") == 0)
|
|
d->skip_clean_backup = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_clean_wipe") == 0)
|
|
d->skip_clean_wipe = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_clean_restore") == 0)
|
|
d->skip_clean_restore = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_clean_install") == 0)
|
|
d->skip_clean_install = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_clean_staging_cleanup") == 0)
|
|
d->skip_clean_staging_cleanup = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_update_cleanup") == 0)
|
|
d->skip_update_cleanup = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_update_install") == 0)
|
|
d->skip_update_install = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_update_staging_cleanup") == 0)
|
|
d->skip_update_staging_cleanup = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_update_horizon_oc") == 0)
|
|
d->skip_update_horizon_oc = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "skip_hekate_8gb_post_copy") == 0)
|
|
d->skip_hekate_8gb_post_copy = install_ini_kv_truth(kv->val);
|
|
else if (strcmp(kv->key, "log") == 0)
|
|
d->log = install_ini_kv_truth(kv->val);
|
|
}
|
|
break;
|
|
}
|
|
install_ini_free_sections(§ions);
|
|
|
|
d->active = d->skip_clean_backup || d->skip_clean_wipe || d->skip_clean_restore
|
|
|| d->skip_clean_install || d->skip_clean_staging_cleanup || d->skip_update_cleanup
|
|
|| d->skip_update_install || d->skip_update_staging_cleanup || d->skip_update_horizon_oc
|
|
|| d->skip_hekate_8gb_post_copy || d->log;
|
|
}
|
|
|
|
static void install_debug_print_banner(const install_debug_opts_t *d)
|
|
{
|
|
if (!d->active)
|
|
return;
|
|
install_set_color(COLOR_RED);
|
|
gfx_printf("*** DEBUG-MODUS: %s ***\n", INSTALL_DEBUG_INI);
|
|
install_set_color(COLOR_ORANGE);
|
|
if (d->skip_clean_backup)
|
|
gfx_printf(" ueberspringe: Clean Schritt 1 (Backup)\n");
|
|
if (d->skip_clean_wipe)
|
|
gfx_printf(" ueberspringe: Clean Schritt 2 (Bereinigung)\n");
|
|
if (d->skip_clean_restore)
|
|
gfx_printf(" ueberspringe: Clean Schritt 3 (Restore)\n");
|
|
if (d->skip_clean_install)
|
|
gfx_printf(" ueberspringe: Clean Schritt 4 (Kopieren)\n");
|
|
if (d->skip_clean_staging_cleanup)
|
|
gfx_printf(" ueberspringe: Clean Staging-Aufraeumen\n");
|
|
if (d->skip_update_cleanup)
|
|
gfx_printf(" ueberspringe: Update Schritt 1 (Bereinigung)\n");
|
|
if (d->skip_update_install)
|
|
gfx_printf(" ueberspringe: Update Schritt 2 (Kopieren)\n");
|
|
if (d->skip_update_staging_cleanup)
|
|
gfx_printf(" ueberspringe: Update Staging-Aufraeumen\n");
|
|
if (d->skip_update_horizon_oc)
|
|
gfx_printf(" ueberspringe: HorizonOC Update (Backup/Restore)\n");
|
|
if (d->skip_hekate_8gb_post_copy)
|
|
gfx_printf(" ueberspringe: extras RAM / Hekate-Auswahl / Post-Copy\n");
|
|
if (d->log)
|
|
gfx_printf(" Log: %s\n", INSTALL_LOG_PATH);
|
|
install_set_color(COLOR_WHITE);
|
|
gfx_printf("\n");
|
|
}
|
|
|
|
static int perform_installation_steps(omninx_variant_t pack_variant, install_mode_t mode, const install_debug_opts_t *d);
|
|
|
|
// Main installation function
|
|
int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
|
|
install_debug_opts_t dbg;
|
|
install_debug_load(&dbg);
|
|
install_debug_print_banner(&dbg);
|
|
|
|
if (dbg.log) {
|
|
log_init(INSTALL_LOG_PATH);
|
|
log_write("Version: %s\nModus: %s\nVariante: %d\n\n", VERSION,
|
|
mode == INSTALL_MODE_UPDATE ? "Update" : "Clean", (u32)pack_variant);
|
|
}
|
|
|
|
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));
|
|
log_close();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static int perform_installation_steps(omninx_variant_t pack_variant, install_mode_t mode, const install_debug_opts_t *d) {
|
|
int res;
|
|
const install_debug_opts_t dbg = *d;
|
|
|
|
if (mode == INSTALL_MODE_UPDATE) {
|
|
if (pack_variant == VARIANT_OC && !dbg.skip_update_horizon_oc) {
|
|
bool had_horizon_cfg = install_path_exists(HORIZON_OC_CONFIG_PATH);
|
|
install_set_color(COLOR_YELLOW);
|
|
gfx_printf("HorizonOC: Sichere config.ini...\n");
|
|
install_set_color(COLOR_WHITE);
|
|
res = backup_horizon_oc_config_for_oc_update();
|
|
if (res != FR_OK)
|
|
return res;
|
|
if (had_horizon_cfg) {
|
|
install_set_color(COLOR_GREEN);
|
|
gfx_printf(" [OK] Bestehende config.ini gesichert.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
} else if (pack_variant == VARIANT_OC && dbg.skip_update_horizon_oc) {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] HorizonOC Backup uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (!dbg.skip_update_cleanup) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 1 (Bereinigung) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
gfx_printf("\n");
|
|
|
|
if (!dbg.skip_update_install) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 2 (Kopieren) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (!dbg.skip_hekate_8gb_post_copy)
|
|
install_extras_post_copy();
|
|
else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] extras Post-Copy uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (pack_variant == VARIANT_OC && !dbg.skip_update_horizon_oc) {
|
|
bool had_horizon_bak = install_path_exists(HORIZON_OC_UPDATE_BACKUP_INI);
|
|
install_check_and_clear_screen_if_needed();
|
|
install_set_color(COLOR_YELLOW);
|
|
gfx_printf("HorizonOC: Stelle config.ini wieder her...\n");
|
|
install_set_color(COLOR_WHITE);
|
|
res = restore_horizon_oc_config_after_oc_update();
|
|
if (res != FR_OK)
|
|
return res;
|
|
install_set_color(COLOR_GREEN);
|
|
if (had_horizon_bak)
|
|
gfx_printf(" [OK] Deine HorizonOC-Einstellungen wurden wiederhergestellt.\n");
|
|
else
|
|
gfx_printf(" [OK] Keine fruehere config.ini — Standard aus dem Paket bleibt.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
res = cleanup_horizon_oc_update_backup();
|
|
if (res != FR_OK)
|
|
return res;
|
|
} else if (pack_variant == VARIANT_OC && dbg.skip_update_horizon_oc) {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] HorizonOC Restore uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
if (!dbg.skip_update_staging_cleanup) {
|
|
res = cleanup_staging_directory(pack_variant);
|
|
if (res != FR_OK) return res;
|
|
res = cleanup_other_staging_directories(pack_variant);
|
|
return res;
|
|
}
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Staging-Aufraeumen uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
return FR_OK;
|
|
}
|
|
|
|
if (!dbg.skip_clean_backup) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 1 (Backup) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
gfx_printf("\n");
|
|
|
|
if (!dbg.skip_clean_wipe) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 2 (Bereinigung) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
gfx_printf("\n");
|
|
|
|
if (!dbg.skip_clean_restore) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 3 (Restore) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
gfx_printf("\n");
|
|
|
|
if (!dbg.skip_clean_install) {
|
|
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;
|
|
} else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Schritt 4 (Kopieren) uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
if (!dbg.skip_hekate_8gb_post_copy)
|
|
install_extras_post_copy();
|
|
else {
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] extras Post-Copy uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
}
|
|
|
|
install_check_and_clear_screen_if_needed();
|
|
if (!dbg.skip_clean_staging_cleanup) {
|
|
res = cleanup_staging_directory(pack_variant);
|
|
if (res != FR_OK) return res;
|
|
res = cleanup_other_staging_directories(pack_variant);
|
|
return res;
|
|
}
|
|
install_set_color(COLOR_ORANGE);
|
|
gfx_printf("[DEBUG] Staging-Aufraeumen uebersprungen.\n");
|
|
install_set_color(COLOR_WHITE);
|
|
return FR_OK;
|
|
}
|