Preserve.ini for update cleanup, Hekate UI strings, docs
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:
2026-05-17 11:21:10 +02:00
parent dce11538d1
commit 59f103874c
8 changed files with 248 additions and 29 deletions

View File

@@ -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(&sections);
if (ini_parse(&sections, (char *)INSTALL_PRESERVE_INI, false) != 1) {
ram_config_free_sections(&sections);
free(g_preserve_block);
g_preserve_block = NULL;
return;
}
LIST_FOREACH_ENTRY(ini_sec_t, sec, &sections, 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(&sections);
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;
}