Preserve.ini for update cleanup, Hekate UI strings, docs
All checks were successful
Build / Build (push) Successful in 17s
All checks were successful
Build / Build (push) Successful in 17s
- Load sd:/config/omninx/preserve.ini during update mode cleanup to skip delete paths that overlap user-preserved prefixes (heap-backed path list). - Document preserve.ini in PRESERVE_INI.md, README, and INSTALLATION_PROCESS. - Prefer Hekate wording in main.c user messages for bootloader launch/errors. - Extend update deletion list for exefs_patches (audio_mastervolume, SaltyNX_Fixes, logo).
This commit is contained in:
@@ -90,7 +90,9 @@ The OmniNX Installer Payload operates in two modes:
|
||||
**Trigger**: `INSTALL_MODE_UPDATE` (OmniNX already installed)
|
||||
|
||||
### Step 1: Cleanup (Selective Deletion)
|
||||
**Location**: `install.c:321-380`
|
||||
**Location**: `install_update.c` (`update_mode_cleanup`), `install.c` (`delete_path_lists_grouped`)
|
||||
|
||||
Before deletion, the payload reads optional **`sd:/config/omninx/preserve.ini`** (section `[Preserve]`). Paths enabled there are skipped, as are delete targets that would remove them (parent/child overlap). See **[PRESERVE_INI.md](PRESERVE_INI.md)**. Clean install does not use this file.
|
||||
|
||||
#### 10. Clean Atmosphere Subdirectories
|
||||
**Location**: `install.c:325-327`, `deletion_lists.h:9-34`
|
||||
|
||||
59
PRESERVE_INI.md
Normal file
59
PRESERVE_INI.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# User preserve list (`preserve.ini`)
|
||||
|
||||
**Update mode only.** During OmniNX update cleanup (selective deletion), any path listed here with a truthy value is **not** deleted. Paths that would remove a preserved path (parent directory) are skipped as well.
|
||||
|
||||
- **File:** `sd:/config/omninx/preserve.ini`
|
||||
- If missing or invalid, behavior is unchanged (full update deletion list applies).
|
||||
|
||||
## Format
|
||||
|
||||
Section **`[Preserve]`** (case-sensitive). Each line is **path as key**, **enable flag as value**:
|
||||
|
||||
```ini
|
||||
[Preserve]
|
||||
'sd:/ROMs'=1
|
||||
sd:/switch/MyHomebrewFolder=0
|
||||
sd:/backup/tools=1
|
||||
```
|
||||
|
||||
### Path (key)
|
||||
|
||||
- Must start with **`sd:/`** (after optional quoting).
|
||||
- **Optional** matching **single** or **double** quotes around the key are stripped (helps Ultrahand `set-ini-val` and readability).
|
||||
- Trailing slashes are normalized away (except the volume root).
|
||||
- **`sd:/` alone** is rejected (too broad).
|
||||
- Minimum useful length after `sd:/` is enforced (at least one more character).
|
||||
|
||||
### Value (enable)
|
||||
|
||||
**On** if the value starts with `1`, `t`, `T`, `y`, or `Y` (e.g. `1`, `true`, `yes`). Anything else is **off** (path ignored).
|
||||
|
||||
### Limits
|
||||
|
||||
- Up to **96** preserved paths; longer keys truncated internally (~224 bytes per path).
|
||||
- Duplicate paths after normalization are skipped.
|
||||
- The list is allocated on the **heap** only while update cleanup runs (not a large static buffer in IPL IRAM), to avoid crowding hardware-adjacent memory used by Joy-Con UART and similar peripherals.
|
||||
|
||||
## Overlap rules
|
||||
|
||||
For each delete candidate from `deletion_lists_update.h`, deletion is skipped if it **overlaps** any enabled preserve path:
|
||||
|
||||
- Same path, or
|
||||
- Delete target is **inside** a preserved tree (preserved path is a prefix), or
|
||||
- Delete target is an **ancestor** of a preserved path (deleting it would remove the preserved data).
|
||||
|
||||
So preserving `sd:/ROMs` also blocks deleting `sd:/ROMs/title` if that were on the delete list, and blocks deleting `sd:/` if `sd:/ROMs` is preserved.
|
||||
|
||||
## Ultrahand
|
||||
|
||||
You can toggle entries with:
|
||||
|
||||
```text
|
||||
set-ini-val sd:/config/omninx/preserve.ini Preserve 'sd:/ROMs' 1
|
||||
```
|
||||
|
||||
and `0` to disable. Use the same quoted path in the key argument as in your INI.
|
||||
|
||||
## Clean install
|
||||
|
||||
**Not used** in clean install mode. `preserve.ini` is only read for **update** cleanup (`update_mode_cleanup`).
|
||||
@@ -23,12 +23,14 @@ Based on [HATS-Installer-Payload](https://github.com/sthetix/HATS-Installer-Payl
|
||||
For detailed information about the installation process, see:
|
||||
- **[INSTALLATION_PROCESS.md](INSTALLATION_PROCESS.md)** - Complete step-by-step breakdown of everything checked and done during installation/update
|
||||
- **[DEBUG_INI.md](DEBUG_INI.md)** - Optional `sd:/config/omninx/debug.ini` parameters to skip install steps (for testing)
|
||||
- **[PRESERVE_INI.md](PRESERVE_INI.md)** - Optional `sd:/config/omninx/preserve.ini` to skip deleting chosen paths in **update** mode
|
||||
|
||||
## Installation Modes
|
||||
|
||||
### Update Mode (OmniNX Detected)
|
||||
- Detected when version marker files (`1.0.0s`, `1.0.0l`, or `1.0.0oc`) are found
|
||||
- Performs selective deletion of specific directories/files
|
||||
- Optional **`sd:/config/omninx/preserve.ini`**: user-listed `sd:/…` paths (with `=1`) are excluded from that cleanup (see [PRESERVE_INI.md](PRESERVE_INI.md))
|
||||
- Preserves user data, savegames, and installed games
|
||||
- Updates only necessary CFW components
|
||||
|
||||
|
||||
@@ -10,14 +10,16 @@ static const char* atmosphere_dirs_to_delete[] = {
|
||||
"sd:/atmosphere/config",
|
||||
"sd:/atmosphere/crash_reports",
|
||||
"sd:/atmosphere/erpt_reports",
|
||||
"sd:/atmosphere/exefs_patches/audio_mastervolume",
|
||||
"sd:/atmosphere/exefs_patches/CrunchPatch",
|
||||
"sd:/atmosphere/exefs_patches/SaltyNX_Fixes",
|
||||
"sd:/atmosphere/exefs_patches/Crunchyroll Patch 1.10.0",
|
||||
"sd:/atmosphere/exefs_patches/bluetooth_patches",
|
||||
"sd:/atmosphere/exefs_patches/bootlogo",
|
||||
"sd:/atmosphere/exefs_patches/btm_patches",
|
||||
"sd:/atmosphere/exefs_patches/es_patches",
|
||||
"sd:/atmosphere/exefs_patches/hid_patches",
|
||||
"sd:/atmosphere/exefs_patches/logo1",
|
||||
"sd:/atmosphere/exefs_patches/logo",
|
||||
"sd:/atmosphere/exefs_patches/nfim_ctest",
|
||||
"sd:/atmosphere/exefs_patches/nim_ctest",
|
||||
"sd:/atmosphere/exefs_patches/nvnflinger_cmu",
|
||||
|
||||
191
source/install.c
191
source/install.c
@@ -21,6 +21,8 @@
|
||||
|
||||
#define GROUPED_DELETE_MAX_ENTRIES 512
|
||||
|
||||
static bool install_path_blocked_by_user_preserve(const char *path);
|
||||
|
||||
#ifndef VERSION
|
||||
#define VERSION "1.0.0"
|
||||
#endif
|
||||
@@ -463,7 +465,7 @@ int delete_path_lists_grouped(const char *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]))
|
||||
if (install_path_exists(list[i]) && !install_path_blocked_by_user_preserve(list[i]))
|
||||
entries[n_entries++] = list[i];
|
||||
}
|
||||
}
|
||||
@@ -538,9 +540,9 @@ int delete_path_list(const char* paths[], const char* description) {
|
||||
u32 start_x, start_y;
|
||||
int last_percent = -1;
|
||||
|
||||
// Count total paths first
|
||||
// Count total paths first (only those we will delete)
|
||||
for (int i = 0; paths[i] != NULL; i++) {
|
||||
if (install_path_exists(paths[i])) {
|
||||
if (install_path_exists(paths[i]) && !install_path_blocked_by_user_preserve(paths[i])) {
|
||||
total_paths++;
|
||||
}
|
||||
}
|
||||
@@ -558,7 +560,7 @@ int delete_path_list(const char* paths[], const char* description) {
|
||||
install_set_color(COLOR_WHITE);
|
||||
|
||||
for (int i = 0; paths[i] != NULL; i++) {
|
||||
if (install_path_exists(paths[i])) {
|
||||
if (install_path_exists(paths[i]) && !install_path_blocked_by_user_preserve(paths[i])) {
|
||||
FILINFO fno;
|
||||
f_stat(paths[i], &fno);
|
||||
|
||||
@@ -686,6 +688,159 @@ static void ram_config_free_sections(link_t *sections)
|
||||
}
|
||||
}
|
||||
|
||||
#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) {
|
||||
ram_config_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;
|
||||
}
|
||||
ram_config_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;
|
||||
}
|
||||
|
||||
/* Valid [Ram] 8gb=0|1 → silent; missing file or invalid → menu. */
|
||||
static int read_ram_config_silent(const char *path, bool *use_8gb)
|
||||
{
|
||||
@@ -884,14 +1039,6 @@ typedef struct {
|
||||
bool skip_hekate_8gb_post_copy;
|
||||
} install_debug_opts_t;
|
||||
|
||||
static bool install_ini_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 install_debug_load(install_debug_opts_t *d)
|
||||
{
|
||||
memset(d, 0, sizeof(*d));
|
||||
@@ -912,25 +1059,25 @@ static void install_debug_load(install_debug_opts_t *d)
|
||||
if (!kv->key || !kv->val)
|
||||
continue;
|
||||
if (strcmp(kv->key, "skip_clean_backup") == 0)
|
||||
d->skip_clean_backup = install_ini_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
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_truth(kv->val);
|
||||
d->skip_hekate_8gb_post_copy = install_ini_kv_truth(kv->val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ int clean_mode_wipe(void);
|
||||
int clean_mode_restore(void);
|
||||
int clean_mode_install(omninx_variant_t variant);
|
||||
|
||||
void install_preserve_update_cleanup_begin(void);
|
||||
void install_preserve_update_cleanup_end(void);
|
||||
|
||||
// Shared helpers (install.c) - used by install_update.c and install_clean.c
|
||||
void install_set_color(u32 color);
|
||||
void install_check_and_clear_screen_if_needed(void);
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
// Update mode: Cleanup specific directories/files
|
||||
int update_mode_cleanup(omninx_variant_t variant) {
|
||||
(void)variant;
|
||||
install_preserve_update_cleanup_begin();
|
||||
|
||||
check_and_clear_screen_if_needed();
|
||||
|
||||
set_color(COLOR_WHITE);
|
||||
@@ -66,6 +68,8 @@ int update_mode_cleanup(omninx_variant_t variant) {
|
||||
misc_files_to_delete,
|
||||
NULL);
|
||||
|
||||
install_preserve_update_cleanup_end();
|
||||
|
||||
set_color(COLOR_GREEN);
|
||||
gfx_printf(" Bereinigung abgeschlossen!\n");
|
||||
set_color(COLOR_WHITE);
|
||||
|
||||
@@ -145,7 +145,7 @@ static int launch_payload(const char *path) {
|
||||
if (sd_mount()) {
|
||||
FIL fp;
|
||||
if (f_open(&fp, path, FA_READ)) {
|
||||
gfx_printf("Payload nicht gefunden: %s\n", path);
|
||||
gfx_printf("Hekate nicht gefunden: %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ void ipl_main(void) {
|
||||
if (file_exists(PAYLOAD_PATH)) {
|
||||
gfx_printf("\n");
|
||||
set_color(COLOR_CYAN);
|
||||
gfx_printf("Payload wird gestartet...\n");
|
||||
gfx_printf("Hekate wird gestartet...\n");
|
||||
set_color(COLOR_WHITE);
|
||||
msleep(500);
|
||||
launch_payload(PAYLOAD_PATH);
|
||||
@@ -721,16 +721,16 @@ void ipl_main(void) {
|
||||
|
||||
gfx_printf("\n");
|
||||
set_color(COLOR_CYAN);
|
||||
gfx_printf("Payload wird gestartet...\n");
|
||||
gfx_printf("Hekate wird gestartet...\n");
|
||||
set_color(COLOR_WHITE);
|
||||
msleep(500); // Brief delay before launch
|
||||
|
||||
if (file_exists(PAYLOAD_PATH)) {
|
||||
launch_payload(PAYLOAD_PATH);
|
||||
} else {
|
||||
// Payload not found, show error
|
||||
// Hekate (update.bin) not found, show error
|
||||
set_color(COLOR_RED);
|
||||
gfx_printf("\nFEHLER: Payload nicht gefunden!\n");
|
||||
gfx_printf("\nFEHLER: Hekate nicht gefunden!\n");
|
||||
gfx_printf("Pfad: %s\n", PAYLOAD_PATH);
|
||||
set_color(COLOR_WHITE);
|
||||
gfx_printf("\nDruecke A oder Power zum Neustart...\n");
|
||||
|
||||
Reference in New Issue
Block a user