Update: preserve UltraHand .offload state (overlays/packages)
All checks were successful
Build / Build (push) Successful in 10s

- Add folder_copy_switch_update_offload_aware() for update-only switch copy
- If item exists in .overlays/.offload or .packages/.offload, update in place; else copy to main
- Remove sd:/switch/.overlays from update delete list so .offload survives
- Fresh install unchanged (no offload logic)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-17 21:01:36 +01:00
parent 5bcd3987a2
commit e71fb13bfc
4 changed files with 165 additions and 3 deletions

View File

@@ -122,9 +122,8 @@ static const char* config_dirs_to_delete[] = {
};
// Switch directories to delete
// NOTE: .packages is intentionally excluded - UltraHand package cache, preserve during updates
// NOTE: .overlays and .packages excluded - preserve UltraHand .offload hide state during updates
static const char* switch_dirs_to_delete[] = {
"sd:/switch/.overlays",
"sd:/switch/90DNS_tester",
"sd:/switch/aio-switch-updater",
"sd:/switch/amsPLUS-downloader",

View File

@@ -275,6 +275,167 @@ int folder_copy_with_progress_v2(const char *src, const char *dst, const char *d
return res;
}
// Copy one directory (e.g. .overlays or .packages) with .offload awareness: if the same
// name exists in dst_offload, copy pack's version into dst_offload (update in place);
// otherwise copy to dst_parent. Used only for update mode to preserve UltraHand hide state.
static int copy_dir_offload_aware(const char *src_parent, const char *dst_parent, const char *dst_offload,
const char *display_name, int *copied, int total, u32 start_x, u32 start_y, int *last_percent)
{
DIR dir;
FILINFO fno;
int res;
char src_full[256];
char dst_main[256];
char dst_off[256];
res = f_opendir(&dir, src_parent);
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 (strcmp(fno.fname, ".offload") == 0)
continue;
install_combine_path(src_full, sizeof(src_full), src_parent, fno.fname);
install_combine_path(dst_off, sizeof(dst_off), dst_offload, fno.fname);
if (install_path_exists(dst_off)) {
res = (fno.fattrib & AM_DIR) ? folder_copy(src_full, dst_offload) : file_copy(src_full, dst_off);
} else {
install_combine_path(dst_main, sizeof(dst_main), dst_parent, fno.fname);
res = (fno.fattrib & AM_DIR) ? folder_copy(src_full, dst_parent) : file_copy(src_full, dst_main);
}
(*copied)++;
if (total > 0) {
int percent = (*copied * 100) / total;
if (percent != *last_percent || *copied % 20 == 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) {
f_closedir(&dir);
return res;
}
}
f_closedir(&dir);
return FR_OK;
}
// Copy switch/ folder for update mode only: preserve SD's .overlays/.offload and
// .packages/.offload; for items that exist there, update in place instead of main area.
int folder_copy_switch_update_offload_aware(const char *src_switch, const char *dst_base, const char *display_name) {
char dst_switch[256];
char src_overlays[256], src_packages[256];
char dst_overlays[256], dst_offload_ovl[256], dst_packages[256], dst_offload_pkg[256];
DIR dir;
FILINFO fno;
int res;
int copied = 0;
int total;
int last_percent = -1;
u32 start_x, start_y;
install_combine_path(dst_switch, sizeof(dst_switch), dst_base, "switch");
if (!install_path_exists(src_switch)) {
install_set_color(COLOR_ORANGE);
gfx_printf(" Ueberspringe: %s (nicht gefunden)\n", display_name);
install_set_color(COLOR_WHITE);
return FR_NO_FILE;
}
total = install_count_directory_items(src_switch);
if (total == 0) {
f_mkdir(dst_switch);
return FR_OK;
}
gfx_con_getpos(&start_x, &start_y);
install_set_color(COLOR_CYAN);
gfx_printf(" Kopiere: %s [ 0%%] (0/%d)", display_name, total);
install_set_color(COLOR_WHITE);
res = f_mkdir(dst_switch);
if (res != FR_OK && res != FR_EXIST) return res;
res = f_opendir(&dir, src_switch);
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 (strcmp(fno.fname, ".overlays") == 0) {
install_combine_path(src_overlays, sizeof(src_overlays), src_switch, ".overlays");
install_combine_path(dst_overlays, sizeof(dst_overlays), dst_switch, ".overlays");
install_combine_path(dst_offload_ovl, sizeof(dst_offload_ovl), dst_overlays, ".offload");
if (!install_path_exists(src_overlays)) { f_closedir(&dir); continue; }
f_mkdir(dst_overlays);
f_mkdir(dst_offload_ovl);
res = copy_dir_offload_aware(src_overlays, dst_overlays, dst_offload_ovl, display_name,
&copied, total, start_x, start_y, &last_percent);
} else if (strcmp(fno.fname, ".packages") == 0) {
install_combine_path(src_packages, sizeof(src_packages), src_switch, ".packages");
install_combine_path(dst_packages, sizeof(dst_packages), dst_switch, ".packages");
install_combine_path(dst_offload_pkg, sizeof(dst_offload_pkg), dst_packages, ".offload");
if (!install_path_exists(src_packages)) { f_closedir(&dir); continue; }
f_mkdir(dst_packages);
f_mkdir(dst_offload_pkg);
{ /* package.ini into main area */
char src_ini[256], dst_ini[256];
s_printf(src_ini, "%s/package.ini", src_packages);
s_printf(dst_ini, "%s/package.ini", dst_packages);
if (install_path_exists(src_ini)) {
res = file_copy(src_ini, dst_ini);
if (res == FR_OK) copied++;
}
}
res = copy_dir_offload_aware(src_packages, dst_packages, dst_offload_pkg, display_name,
&copied, total, start_x, start_y, &last_percent);
} else {
char src_full[256], dst_full[256];
install_combine_path(src_full, sizeof(src_full), src_switch, fno.fname);
install_combine_path(dst_full, sizeof(dst_full), dst_switch, fno.fname);
if (fno.fattrib & AM_DIR)
res = folder_copy(src_full, dst_switch);
else
res = file_copy(src_full, dst_full);
copied++;
}
if (res != FR_OK) break;
if (total > 0 && (copied % 20 == 0 || copied == total)) {
int percent = (copied * 100) / total;
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);
}
}
f_closedir(&dir);
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);
install_set_color(COLOR_WHITE);
}
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;

View File

@@ -40,3 +40,5 @@ 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);
// Update-only: copy switch/ preserving .overlays/.offload and .packages/.offload (UltraHand hide state)
int folder_copy_switch_update_offload_aware(const char *src_switch, const char *dst_base, const char *display_name);

View File

@@ -103,7 +103,7 @@ int update_mode_install(omninx_variant_t variant) {
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/");
res = folder_copy_switch_update_offload_aware(src_path, "sd:/", "switch/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/warmboot_mariko", staging);