Compare commits

...

4 Commits

Author SHA1 Message Date
dce11538d1 docs: README and install process for debug.ini and backup
All checks were successful
Build / Build (push) Successful in 17s
- Link DEBUG_INI.md; describe clean install backup (dbi.config, prod.keys)
- Update INSTALLATION_PROCESS backup/restore steps

Made-with: Cursor
2026-04-12 16:22:45 +02:00
21f8e0e38a refactor: clean install backup — dbi.config and prod.keys only
- Backup/restore DBI settings file instead of whole DBI folder
- Remove Tinfoil folder backup/restore
- Adjust deletion_lists_clean.h comments; UI strings in install_clean.c

Made-with: Cursor
2026-04-12 16:22:43 +02:00
0ad2c63123 feat: optional debug.ini to skip install steps
Read sd:/config/omninx/debug.ini [Debug] with skip_* flags for clean/update
modes and hekate post-copy. Documented in DEBUG_INI.md.

Made-with: Cursor
2026-04-12 16:22:41 +02:00
6780936d2d chore: normalize line endings in fs.c and fs.h
Made-with: Cursor
2026-04-12 16:22:39 +02:00
11 changed files with 872 additions and 637 deletions

90
DEBUG_INI.md Normal file
View File

@@ -0,0 +1,90 @@
# Debug configuration (`debug.ini`)
Optional file to **skip parts of the install** for testing (e.g. run only deletion and inspect the SD card). If the file is **missing**, the installer ignores debug entirely and runs the full flow.
## Location
```
sd:/config/omninx/debug.ini
```
Create the folder `sd:/config/omninx/` on the SD card if it does not exist. Same parent directory as `manifest.ini` and `ram_config.ini`.
## Format
- Section name must be exactly **`[Debug]`** (case-sensitive in the parser).
- One key per line: `name=value`.
- **Enabling a skip:** set the value so it **starts** with one of: `1`, `t`, `T`, `y`, `Y`
Examples: `1`, `true`, `yes`, `True` — all enable that skip.
- **Disabling:** `0`, `false`, `no`, or omit the key.
## Parameters
### Clean install mode (`INSTALL_MODE_CLEAN`)
| Key | What is skipped |
|-----|-----------------|
| `skip_clean_backup` | Step 1 — backup of `sd:/switch/DBI/dbi.config`, `sd:/switch/prod.keys` to `sd:/temp_backup` |
| `skip_clean_wipe` | Step 2 — all cleanup from `deletion_lists_clean.h` (deletion / wipe) |
| `skip_clean_restore` | Step 3 — restore from `sd:/temp_backup` and post-restore paths (e.g. delete `sd:/switch/tinfoil/db`) |
| `skip_clean_install` | Step 4 — copy files from the OmniNX staging folder to the SD root |
| `skip_clean_staging_cleanup` | After install: do not remove the staging directory or other variant staging folders |
### Update mode (`INSTALL_MODE_UPDATE`)
| Key | What is skipped |
|-----|-----------------|
| `skip_update_cleanup` | Step 1 — selective deletion from `deletion_lists_update.h` |
| `skip_update_install` | Step 2 — copy from staging to SD |
| `skip_update_staging_cleanup` | End: do not remove staging / other variant folders |
| `skip_update_horizon_oc` | **OC variant only:** HorizonOC config backup before update, restore and cleanup after (entire HorizonOC update side path) |
### Both modes
| Key | What is skipped |
|-----|-----------------|
| `skip_hekate_8gb_post_copy` | After a successful copy: the optional `hekate_8GB` / RAM menu and related file moves (see `install_hekate_8gb_post_copy` in `install.c`) |
## Behaviour
- On startup, if `debug.ini` exists and parses successfully, the payload prints a **DEBUG-MODUS** banner listing which skips are active.
- If the file is absent, unreadable, or has no `[Debug]` section, **no** skips apply — behaviour matches a release install.
- Skipping steps can leave the SD in an inconsistent state; this is **for developers/testers only**.
## Examples
### Wipe only (test deletion, no backup/restore/copy)
Inspect leftovers after the clean-install wipe, without backing up, restoring, installing, or cleaning staging:
```ini
[Debug]
skip_clean_backup=1
skip_clean_restore=1
skip_clean_install=1
skip_clean_staging_cleanup=1
skip_hekate_8gb_post_copy=1
```
Do **not** set `skip_clean_wipe` (leave unset or `0`).
### Update: deletion only
```ini
[Debug]
skip_update_install=1
skip_update_staging_cleanup=1
skip_hekate_8gb_post_copy=1
```
### OC update without touching HorizonOC backup/restore
```ini
[Debug]
skip_update_horizon_oc=1
```
## Implementation
- Parsed in `install.c`: `install_debug_load()`, `perform_installation()`.
- Uses the same INI parser as `ram_config.ini` (`ini_parse` from BDK).

View File

@@ -2,6 +2,8 @@
This document provides a detailed, step-by-step breakdown of everything that is checked and done during the OmniNX installation/update process.
For **optional debug step skips** (`sd:/config/omninx/debug.ini`), see **[DEBUG_INI.md](DEBUG_INI.md)**.
## Overview
The OmniNX Installer Payload operates in two modes:
@@ -305,17 +307,12 @@ channel_pack={variant} # Same as current_pack
#### 32. Create Backup Directory
- Creates `sd:/temp_backup/` directory
#### 33. Backup DBI
- **Source**: `sd:/switch/DBI/`
- **Destination**: `sd:/temp_backup/DBI/`
- Only if source exists
#### 33. Backup DBI settings
- **Source**: `sd:/switch/DBI/dbi.config` (file only, not the whole `DBI` tree)
- **Destination**: `sd:/temp_backup/dbi.config`
- Only if the source file exists
#### 34. Backup Tinfoil
- **Source**: `sd:/switch/tinfoil/`
- **Destination**: `sd:/temp_backup/tinfoil/`
- Only if source exists
#### 35. Backup prod.keys
#### 34. Backup prod.keys
- **Source**: `sd:/switch/prod.keys`
- **Destination**: `sd:/temp_backup/prod.keys`
- Only if source exists
@@ -325,22 +322,22 @@ channel_pack={variant} # Same as current_pack
### Step 2: Wipe Directories
**Location**: `install.c:543-602`
#### 36. Delete Entire Directories
#### 35. Delete Entire Directories
Recursively deletes (if they exist):
- `sd:/atmosphere/` (entire directory tree)
- `sd:/bootloader/` (entire directory tree)
- `sd:/config/` (entire directory tree)
- `sd:/switch/` (entire directory tree)
#### 37. Delete Root Files
#### 36. Delete Root Files
- Uses same deletion list as update mode (Step 19)
- Deletes `boot.dat`, `boot.ini`, `exosphere.ini`, etc.
#### 38. Delete Miscellaneous Items
#### 37. Delete Miscellaneous Items
- Uses same deletion lists as update mode (Steps 20-21)
- Deletes `argon/`, `games/`, `SaltySD/`, etc.
#### 39. Recreate Switch Directory
#### 38. Recreate Switch Directory
- Creates empty `sd:/switch/` directory for restoration
---
@@ -348,27 +345,17 @@ Recursively deletes (if they exist):
### Step 3: Restore User Data
**Location**: `backup.c:55-95`
#### 40. Restore DBI
- **Source**: `sd:/temp_backup/DBI/`
- **Destination**: `sd:/switch/DBI/`
- **Cleanup**: After restoration, deletes old DBI NRO files:
- `sd:/switch/DBI/DBI_810_EN.nro`
- `sd:/switch/DBI/DBI_810_DE.nro`
- `sd:/switch/DBI/DBI_845_EN.nro`
- `sd:/switch/DBI/DBI_845_DE.nro`
- `sd:/switch/DBI/DBI.nro`
#### 39. Restore DBI settings
- **Source**: `sd:/temp_backup/dbi.config`
- **Destination**: `sd:/switch/DBI/dbi.config` (creates `sd:/switch/DBI/` if needed)
- No longer copies the full `DBI` folder; old `.nro` cleanup is unnecessary
#### 41. Restore Tinfoil
- **Source**: `sd:/temp_backup/tinfoil/`
- **Destination**: `sd:/switch/tinfoil/`
- **Cleanup**: After restoration, deletes `sd:/switch/tinfoil/tinfoil.nro`
#### 42. Restore prod.keys
#### 40. Restore prod.keys
- **Source**: `sd:/temp_backup/prod.keys`
- **Destination**: `sd:/switch/prod.keys`
#### 43. Cleanup Backup Directory
**Location**: `backup.c:98-103`
#### 41. Cleanup Backup Directory
**Location**: `backup.c` (`cleanup_backup`)
- Deletes `sd:/temp_backup/` directory after restoration
---

326
README.md
View File

@@ -1,163 +1,163 @@
# OmniNX Installer Payload
> **To properly use this payload, download the latest OmniNX Release:** [https://git.niklascfw.de/OmniNX/OmniNX/releases](https://git.niklascfw.de/OmniNX/OmniNX/releases)
A minimal payload for installing OmniNX CFW Pack files on Nintendo Switch outside of Horizon OS.
Based on [TegraExplorer](https://github.com/shchmue/TegraExplorer) and [hekate](https://github.com/CTCaer/hekate) by CTCaer, naehrwert, and shchmue.
Based on [HATS-Installer-Payload](https://github.com/sthetix/HATS-Installer-Payload) by sthetix.
## Features
- **Automatic Variant Detection**: Detects which OmniNX pack variant is present (Standard/Light/OC)
- **Smart Installation Modes**:
- **Update Mode**: Selective deletion when OmniNX is already installed
- **Clean Install**: Full wipe with backup/restore of user data (DBI, Tinfoil, prod.keys)
- **Version Detection**: Detects installed OmniNX version via marker files (`1.0.0s`, `1.0.0l`, `1.0.0oc`)
- **Progress Display**: Visual status messages during installation
- **Error Handling**: Detailed error reporting on screen
- **Payload Chaining**: Automatically launch hekate after installation
## Documentation
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
## 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
- Preserves user data, savegames, and installed games
- Updates only necessary CFW components
### Clean Install (No OmniNX Detected)
- Detected when no version marker files are found
- Performs full wipe of `/atmosphere`, `/bootloader`, `/config`, and `/switch`
- **Backs up and restores**:
- `sd:/switch/DBI` → preserved
- `sd:/switch/tinfoil` → preserved
- `sd:/switch/prod.keys` → preserved
- Fresh installation of all CFW components
## Building
### Prerequisites
- **devkitARM** - ARM toolchain for Nintendo Switch development
- **BDK** - Blue Development Kit (included in this repo)
### Build Commands
```bash
make clean
make
```
The built payload will be output to `output/OmniNX-Installer.bin`.
### CI / GitHub Actions
Every push triggers a build. The `.bin` file is produced and attached as a workflow artifact (Actions tab → select run → Artifacts).
## Usage
### 1. Extract OmniNX Pack to SD Card
Extract the OmniNX pack zip file directly to your SD card. The pack should contain one of:
```
sd:/OmniNX Standard/
├── atmosphere/
├── bootloader/
├── config/
├── switch/
├── TegraExplorer/
├── warmboot_mariko/
├── boot.dat
├── boot.ini
├── exosphere.ini
├── hbmenu.nro
├── loader.bin
├── payload.bin
└── 1.0.0s
```
Or `sd:/OmniNX Light/` or `sd:/OmniNX OC/` for other variants.
**Important**: Extract both the OmniNX pack zip AND this payload to your SD card.
### 2. Launch Payload
Use hekate or another bootloader to launch the payload:
1. Place `omninx-installer.bin` in `sd:/bootloader/payloads/`
2. Launch the payload from hekate's payload menu
3. The payload will automatically:
- Detect which pack variant is present
- Detect if OmniNX is already installed
- Perform appropriate installation (update or clean)
- Launch hekate after completion
### 3. What Happens
1. Payload mounts the SD card
2. Detects current OmniNX installation (if any)
3. Detects which pack variant is on SD card
4. Determines installation mode (update vs clean)
5. Performs cleanup based on mode:
- **Update**: Selective deletion of specific paths
- **Clean**: Full wipe with backup/restore
6. Copies files from pack directory to SD root
7. Creates version marker file
8. Cleans up old version markers
9. Launches hekate (`sd:/bootloader/update.bin`)
## Variant Support
The payload supports three OmniNX variants:
- **Standard** `sd:/OmniNX Standard/`
- **Light** `sd:/OmniNX Light/`
- **OC** `sd:/OmniNX OC/`
The payload detects the pack variant from the staging directory presence and detects the current installation (variant + version) from `sd:/config/omninx/manifest.ini` (`current_pack` and `version` keys).
## Project Structure
```
OmniNX-Installer-Payload/
├── source/ # Main source code
│ ├── main.c # Entry point and main flow
│ ├── version.c # Version/variant detection
│ ├── version.h
│ ├── install.c # Installation logic
│ ├── install.h
│ ├── backup.c # Backup/restore operations
│ ├── backup.h
│ ├── deletion_lists.h # Arrays of paths to delete
│ ├── fs.c # File system operations
│ ├── fs.h
│ ├── gfx.c # Graphics utilities
│ ├── gfx.h
│ ├── nx_sd.c # SD card operations
│ ├── nx_sd.h
│ ├── link.ld # Linker script
│ └── start.S # Startup assembly
├── bdk/ # Blue Development Kit
├── Makefile # Build configuration
├── VERSION # Version file
└── README.md # This file
```
## License
This project is based on TegraExplorer and hekate. Please refer to those projects for their respective licenses.
## Credits
- **CTCaer** - [hekate](https://github.com/CTCaer/hekate)
- **naehrwert** - Tegra exploration work
- **shchmue** - [TegraExplorer](https://github.com/shchmue/TegraExplorer)
- **sthetix** - [HATS-Installer-Payload](https://github.com/sthetix/HATS-Installer-Payload) (inspiration and base structure)
- **Woody2408** - OmniNX CFW Pack creator
# OmniNX Installer Payload
> **To properly use this payload, download the latest OmniNX Release:** [https://git.niklascfw.de/OmniNX/OmniNX/releases](https://git.niklascfw.de/OmniNX/OmniNX/releases)
A minimal payload for installing OmniNX CFW Pack files on Nintendo Switch outside of Horizon OS.
Based on [TegraExplorer](https://github.com/shchmue/TegraExplorer) and [hekate](https://github.com/CTCaer/hekate) by CTCaer, naehrwert, and shchmue.
Based on [HATS-Installer-Payload](https://github.com/sthetix/HATS-Installer-Payload) by sthetix.
## Features
- **Automatic Variant Detection**: Detects which OmniNX pack variant is present (Standard/Light/OC)
- **Smart Installation Modes**:
- **Update Mode**: Selective deletion when OmniNX is already installed
- **Clean Install**: Full wipe with backup/restore of user data (DBI `dbi.config`, prod.keys)
- **Version Detection**: Detects installed OmniNX version via marker files (`1.0.0s`, `1.0.0l`, `1.0.0oc`)
- **Progress Display**: Visual status messages during installation
- **Error Handling**: Detailed error reporting on screen
- **Payload Chaining**: Automatically launch hekate after installation
## Documentation
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)
## 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
- Preserves user data, savegames, and installed games
- Updates only necessary CFW components
### Clean Install (No OmniNX Detected)
- Detected when no version marker files are found
- Performs full wipe of `/atmosphere`, `/bootloader`, `/config`, and `/switch`
- **Backs up and restores**:
- `sd:/switch/DBI/dbi.config` → preserved (not the whole `DBI` folder)
- `sd:/switch/prod.keys` → preserved
- Fresh installation of all CFW components
## Building
### Prerequisites
- **devkitARM** - ARM toolchain for Nintendo Switch development
- **BDK** - Blue Development Kit (included in this repo)
### Build Commands
```bash
make clean
make
```
The built payload will be output to `output/OmniNX-Installer.bin`.
### CI / GitHub Actions
Every push triggers a build. The `.bin` file is produced and attached as a workflow artifact (Actions tab → select run → Artifacts).
## Usage
### 1. Extract OmniNX Pack to SD Card
Extract the OmniNX pack zip file directly to your SD card. The pack should contain one of:
```
sd:/OmniNX Standard/
├── atmosphere/
├── bootloader/
├── config/
├── switch/
├── TegraExplorer/
├── warmboot_mariko/
├── boot.dat
├── boot.ini
├── exosphere.ini
├── hbmenu.nro
├── loader.bin
├── payload.bin
└── 1.0.0s
```
Or `sd:/OmniNX Light/` or `sd:/OmniNX OC/` for other variants.
**Important**: Extract both the OmniNX pack zip AND this payload to your SD card.
### 2. Launch Payload
Use hekate or another bootloader to launch the payload:
1. Place `omninx-installer.bin` in `sd:/bootloader/payloads/`
2. Launch the payload from hekate's payload menu
3. The payload will automatically:
- Detect which pack variant is present
- Detect if OmniNX is already installed
- Perform appropriate installation (update or clean)
- Launch hekate after completion
### 3. What Happens
1. Payload mounts the SD card
2. Detects current OmniNX installation (if any)
3. Detects which pack variant is on SD card
4. Determines installation mode (update vs clean)
5. Performs cleanup based on mode:
- **Update**: Selective deletion of specific paths
- **Clean**: Full wipe with backup/restore
6. Copies files from pack directory to SD root
7. Creates version marker file
8. Cleans up old version markers
9. Launches hekate (`sd:/bootloader/update.bin`)
## Variant Support
The payload supports three OmniNX variants:
- **Standard** `sd:/OmniNX Standard/`
- **Light** `sd:/OmniNX Light/`
- **OC** `sd:/OmniNX OC/`
The payload detects the pack variant from the staging directory presence and detects the current installation (variant + version) from `sd:/config/omninx/manifest.ini` (`current_pack` and `version` keys).
## Project Structure
```
OmniNX-Installer-Payload/
├── source/ # Main source code
│ ├── main.c # Entry point and main flow
│ ├── version.c # Version/variant detection
│ ├── version.h
│ ├── install.c # Installation logic
│ ├── install.h
│ ├── backup.c # Backup/restore operations
│ ├── backup.h
│ ├── deletion_lists.h # Arrays of paths to delete
│ ├── fs.c # File system operations
│ ├── fs.h
│ ├── gfx.c # Graphics utilities
│ ├── gfx.h
│ ├── nx_sd.c # SD card operations
│ ├── nx_sd.h
│ ├── link.ld # Linker script
│ └── start.S # Startup assembly
├── bdk/ # Blue Development Kit
├── Makefile # Build configuration
├── VERSION # Version file
└── README.md # This file
```
## License
This project is based on TegraExplorer and hekate. Please refer to those projects for their respective licenses.
## Credits
- **CTCaer** - [hekate](https://github.com/CTCaer/hekate)
- **naehrwert** - Tegra exploration work
- **shchmue** - [TegraExplorer](https://github.com/shchmue/TegraExplorer)
- **sthetix** - [HATS-Installer-Payload](https://github.com/sthetix/HATS-Installer-Payload) (inspiration and base structure)
- **Woody2408** - OmniNX CFW Pack creator

View File

@@ -24,17 +24,9 @@ int backup_user_data(void) {
return res;
}
// Backup DBI if it exists
if (path_exists("sd:/switch/DBI")) {
res = folder_copy("sd:/switch/DBI", TEMP_BACKUP_PATH);
if (res != FR_OK) {
return res;
}
}
// Backup Tinfoil if it exists
if (path_exists("sd:/switch/tinfoil")) {
res = folder_copy("sd:/switch/tinfoil", TEMP_BACKUP_PATH);
// Backup DBI settings file only (not .nro installers)
if (path_exists(DBI_CONFIG_PATH)) {
res = file_copy(DBI_CONFIG_PATH, TEMP_BACKUP_DBI_CONFIG);
if (res != FR_OK) {
return res;
}
@@ -61,25 +53,15 @@ int restore_user_data(void) {
return res;
}
// Restore DBI if backup exists
if (path_exists("sd:/temp_backup/DBI")) {
res = folder_copy("sd:/temp_backup/DBI", "sd:/switch");
if (res == FR_OK) {
// Delete old DBI .nro files
f_unlink("sd:/switch/DBI/DBI_810_EN.nro");
f_unlink("sd:/switch/DBI/DBI_810_DE.nro");
f_unlink("sd:/switch/DBI/DBI_845_EN.nro");
f_unlink("sd:/switch/DBI/DBI_845_DE.nro");
f_unlink("sd:/switch/DBI/DBI.nro");
// Restore DBI settings if backed up
if (path_exists(TEMP_BACKUP_DBI_CONFIG)) {
res = f_mkdir("sd:/switch/DBI");
if (res != FR_OK && res != FR_EXIST) {
return res;
}
}
// Restore Tinfoil if backup exists
if (path_exists("sd:/temp_backup/tinfoil")) {
res = folder_copy("sd:/temp_backup/tinfoil", "sd:/switch");
if (res == FR_OK) {
// Delete old tinfoil.nro
f_unlink("sd:/switch/tinfoil/tinfoil.nro");
res = file_copy(TEMP_BACKUP_DBI_CONFIG, DBI_CONFIG_PATH);
if (res != FR_OK) {
return res;
}
}

View File

@@ -7,6 +7,9 @@
#include <utils/types.h>
#define TEMP_BACKUP_PATH "sd:/temp_backup"
/** DBI settings; preserved across clean install (not whole DBI folder). */
#define DBI_CONFIG_PATH "sd:/switch/DBI/dbi.config"
#define TEMP_BACKUP_DBI_CONFIG TEMP_BACKUP_PATH "/dbi.config"
/** Live HorizonOC settings (Overclock tool). */
#define HORIZON_OC_CONFIG_PATH "sd:/config/horizon-oc/config.ini"
@@ -14,7 +17,7 @@
#define HORIZON_OC_UPDATE_BACKUP_DIR "sd:/.omninx_oc_update"
#define HORIZON_OC_UPDATE_BACKUP_INI HORIZON_OC_UPDATE_BACKUP_DIR "/config.ini"
// Backup user data (DBI, Tinfoil, prod.keys) before clean install
// Backup user data (DBI dbi.config, prod.keys) before clean install
int backup_user_data(void);
// Restore user data after clean install

View File

@@ -2,8 +2,7 @@
* OmniNX Installer - Deletion Lists for Clean Install Mode
* Deletion policy aligned with NiklasCFW pack clean install (TegraExplorer script):
* full sd:/atmosphere, bootloader/config subsets, sd:/switch, root + misc, then
* post-restore paths (e.g. tinfoil db). Backup/restore of DBI, Tinfoil, prod.keys
* is implemented in backup.c, not here.
* post-restore paths (e.g. tinfoil db). DBI/prod.keys backup is in backup.c.
*/
#pragma once
@@ -114,7 +113,7 @@ static const char* clean_misc_files_to_delete[] = {
NULL
};
// After DBI/Tinfoil/prod.keys restore (NiklasCFW: deldir("sd:/switch/tinfoil/db"))
// After DBI/prod.keys restore (NiklasCFW: deldir("sd:/switch/tinfoil/db"))
static const char* clean_post_restore_dirs_to_delete[] = {
"sd:/switch/tinfoil/db",
NULL

View File

@@ -1,351 +1,351 @@
/*
* OmniNX Installer - Filesystem operations with file logging
* Based on HATS Installer
*/
#include "fs.h"
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
#include <string.h>
#include <utils/sprintf.h>
#include <stdarg.h>
#include <stdio.h>
#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer
#define LOG_BUFFER_SIZE 512
// Log file handle
static FIL log_file;
static bool log_enabled = false;
static char log_buf[LOG_BUFFER_SIZE];
// Initialize log file
void log_init(const char *path) {
int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS);
if (res == FR_OK) {
log_enabled = true;
log_write("=== OmniNX Installer Log ===\n\n");
}
}
// Close log file
void log_close(void) {
if (log_enabled) {
f_sync(&log_file);
f_close(&log_file);
log_enabled = false;
}
}
// Write to log file (variadic version)
void log_write(const char *fmt, ...) {
if (!log_enabled) return;
va_list args;
va_start(args, fmt);
// Format the message
vsnprintf(log_buf, LOG_BUFFER_SIZE, fmt, args);
va_end(args);
// Write to file
UINT bw;
f_write(&log_file, log_buf, strlen(log_buf), &bw);
f_sync(&log_file); // Flush to ensure data is written
}
// Convert FatFS error code to string
const char *fs_error_str(int err) {
switch (err) {
case FR_OK: return "OK";
case FR_DISK_ERR: return "DISK_ERR: Low level disk error";
case FR_INT_ERR: return "INT_ERR: Internal error";
case FR_NOT_READY: return "NOT_READY: Drive not ready";
case FR_NO_FILE: return "NO_FILE: File not found";
case FR_NO_PATH: return "NO_PATH: Path not found";
case FR_INVALID_NAME: return "INVALID_NAME: Invalid path name";
case FR_DENIED: return "DENIED: Access denied";
case FR_EXIST: return "EXIST: Already exists";
case FR_INVALID_OBJECT: return "INVALID_OBJECT: Invalid object";
case FR_WRITE_PROTECTED: return "WRITE_PROTECTED: Write protected";
case FR_INVALID_DRIVE: return "INVALID_DRIVE: Invalid drive";
case FR_NOT_ENABLED: return "NOT_ENABLED: Volume not mounted";
case FR_NO_FILESYSTEM: return "NO_FILESYSTEM: No valid FAT";
case FR_MKFS_ABORTED: return "MKFS_ABORTED: mkfs aborted";
case FR_TIMEOUT: return "TIMEOUT: Timeout";
case FR_LOCKED: return "LOCKED: File locked";
case FR_NOT_ENOUGH_CORE: return "NOT_ENOUGH_CORE: Out of memory";
case FR_TOO_MANY_OPEN_FILES: return "TOO_MANY_OPEN_FILES";
case FR_INVALID_PARAMETER: return "INVALID_PARAMETER";
default: return "UNKNOWN_ERROR";
}
}
// Combine two paths
static char *combine_paths(const char *base, const char *add) {
size_t base_len = strlen(base);
size_t add_len = strlen(add);
char *result = malloc(base_len + add_len + 2);
if (!result) return NULL;
if (base_len > 0 && base[base_len - 1] == '/') {
s_printf(result, "%s%s", base, add);
} else {
s_printf(result, "%s/%s", base, add);
}
return result;
}
// Copy a single file with logging
int file_copy(const char *src, const char *dst) {
FIL fin, fout;
FILINFO fno;
int res;
log_write("COPY: %s -> %s\n", src, dst);
res = f_open(&fin, src, FA_READ | FA_OPEN_EXISTING);
if (res != FR_OK) {
log_write(" ERROR open src: %s\n", fs_error_str(res));
return res;
}
f_stat(src, &fno);
u64 file_size = f_size(&fin);
log_write(" Size: %d bytes\n", (u32)file_size);
res = f_open(&fout, dst, FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) {
f_close(&fin);
log_write(" ERROR open dst: %s\n", fs_error_str(res));
return res;
}
u8 *buf = malloc(FS_BUFFER_SIZE);
if (!buf) {
f_close(&fin);
f_close(&fout);
log_write(" ERROR: Out of memory for buffer\n");
return FR_NOT_ENOUGH_CORE;
}
u64 remaining = file_size;
UINT br, bw;
while (remaining > 0) {
UINT to_copy = (remaining > FS_BUFFER_SIZE) ? FS_BUFFER_SIZE : (UINT)remaining;
res = f_read(&fin, buf, to_copy, &br);
if (res != FR_OK) {
log_write(" ERROR read: %s\n", fs_error_str(res));
break;
}
if (br != to_copy) {
log_write(" ERROR: Read %d bytes, expected %d\n", br, to_copy);
res = FR_DISK_ERR;
break;
}
res = f_write(&fout, buf, to_copy, &bw);
if (res != FR_OK) {
log_write(" ERROR write: %s\n", fs_error_str(res));
break;
}
if (bw != to_copy) {
log_write(" ERROR: Wrote %d bytes, expected %d\n", bw, to_copy);
res = FR_DISK_ERR;
break;
}
remaining -= to_copy;
}
free(buf);
f_close(&fin);
f_close(&fout);
if (res == FR_OK) {
f_chmod(dst, fno.fattrib, 0x3A);
log_write(" OK\n");
}
return res;
}
// Recursively delete a folder with logging
int folder_delete(const char *path) {
DIR dir;
FILINFO fno;
int res;
log_write("DELETE: %s\n", path);
res = f_opendir(&dir, path);
if (res != FR_OK) {
// Maybe it's a file, try to delete it
log_write(" Not a dir, trying as file...\n");
res = f_unlink(path);
if (res != FR_OK) {
log_write(" ERROR unlink: %s\n", fs_error_str(res));
} else {
log_write(" OK (file deleted)\n");
}
return res;
}
int file_count = 0;
int dir_count = 0;
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res));
break;
}
if (fno.fname[0] == 0) break; // End of directory
char *full_path = combine_paths(path, fno.fname);
if (!full_path) {
res = FR_NOT_ENOUGH_CORE;
break;
}
if (fno.fattrib & AM_DIR) {
dir_count++;
res = folder_delete(full_path);
} else {
file_count++;
log_write(" DEL: %s\n", fno.fname);
// Clear read-only attribute if set (common with some CFW packs)
if (fno.fattrib & AM_RDO) {
f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO);
}
res = f_unlink(full_path);
if (res != FR_OK) {
log_write(" ERROR: %s\n", fs_error_str(res));
}
}
free(full_path);
if (res != FR_OK) break;
}
f_closedir(&dir);
if (res == FR_OK || res == FR_NO_FILE) {
log_write(" Removing dir: %s (%d files, %d subdirs)\n", path, file_count, dir_count);
// 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);
if (res != FR_OK) {
log_write(" ERROR rmdir: %s\n", fs_error_str(res));
} else {
log_write(" OK\n");
}
}
return res;
}
// Recursively copy a folder with logging
int folder_copy(const char *src, const char *dst) {
DIR dir;
FILINFO fno;
int res;
log_write("FOLDER COPY: %s -> %s\n", src, dst);
res = f_opendir(&dir, src);
if (res != FR_OK) {
log_write(" ERROR opendir src: %s\n", fs_error_str(res));
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
char *dst_path = combine_paths(dst, folder_name);
if (!dst_path) {
f_closedir(&dir);
return FR_NOT_ENOUGH_CORE;
}
log_write(" Creating: %s\n", dst_path);
res = f_mkdir(dst_path);
if (res == FR_EXIST) {
log_write(" (already exists)\n");
res = FR_OK;
} else if (res != FR_OK) {
log_write(" ERROR mkdir: %s\n", fs_error_str(res));
f_closedir(&dir);
free(dst_path);
return res;
}
int file_count = 0;
int dir_count = 0;
// Copy contents
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res));
break;
}
if (fno.fname[0] == 0) break; // End of directory
char *src_full = combine_paths(src, fno.fname);
char *dst_full = combine_paths(dst_path, fno.fname);
if (!src_full || !dst_full) {
if (src_full) free(src_full);
if (dst_full) free(dst_full);
res = FR_NOT_ENOUGH_CORE;
break;
}
if (fno.fattrib & AM_DIR) {
dir_count++;
res = folder_copy(src_full, dst_path);
} else {
file_count++;
res = file_copy(src_full, dst_full);
}
free(src_full);
free(dst_full);
if (res != FR_OK) break;
}
f_closedir(&dir);
// Copy folder attributes
if (res == FR_OK) {
FILINFO src_info;
if (f_stat(src, &src_info) == FR_OK) {
f_chmod(dst_path, src_info.fattrib, 0x3A);
}
log_write(" Done: %d files, %d subdirs\n", file_count, dir_count);
}
free(dst_path);
return res;
}
/*
* OmniNX Installer - Filesystem operations with file logging
* Based on HATS Installer
*/
#include "fs.h"
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
#include <string.h>
#include <utils/sprintf.h>
#include <stdarg.h>
#include <stdio.h>
#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer
#define LOG_BUFFER_SIZE 512
// Log file handle
static FIL log_file;
static bool log_enabled = false;
static char log_buf[LOG_BUFFER_SIZE];
// Initialize log file
void log_init(const char *path) {
int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS);
if (res == FR_OK) {
log_enabled = true;
log_write("=== OmniNX Installer Log ===\n\n");
}
}
// Close log file
void log_close(void) {
if (log_enabled) {
f_sync(&log_file);
f_close(&log_file);
log_enabled = false;
}
}
// Write to log file (variadic version)
void log_write(const char *fmt, ...) {
if (!log_enabled) return;
va_list args;
va_start(args, fmt);
// Format the message
vsnprintf(log_buf, LOG_BUFFER_SIZE, fmt, args);
va_end(args);
// Write to file
UINT bw;
f_write(&log_file, log_buf, strlen(log_buf), &bw);
f_sync(&log_file); // Flush to ensure data is written
}
// Convert FatFS error code to string
const char *fs_error_str(int err) {
switch (err) {
case FR_OK: return "OK";
case FR_DISK_ERR: return "DISK_ERR: Low level disk error";
case FR_INT_ERR: return "INT_ERR: Internal error";
case FR_NOT_READY: return "NOT_READY: Drive not ready";
case FR_NO_FILE: return "NO_FILE: File not found";
case FR_NO_PATH: return "NO_PATH: Path not found";
case FR_INVALID_NAME: return "INVALID_NAME: Invalid path name";
case FR_DENIED: return "DENIED: Access denied";
case FR_EXIST: return "EXIST: Already exists";
case FR_INVALID_OBJECT: return "INVALID_OBJECT: Invalid object";
case FR_WRITE_PROTECTED: return "WRITE_PROTECTED: Write protected";
case FR_INVALID_DRIVE: return "INVALID_DRIVE: Invalid drive";
case FR_NOT_ENABLED: return "NOT_ENABLED: Volume not mounted";
case FR_NO_FILESYSTEM: return "NO_FILESYSTEM: No valid FAT";
case FR_MKFS_ABORTED: return "MKFS_ABORTED: mkfs aborted";
case FR_TIMEOUT: return "TIMEOUT: Timeout";
case FR_LOCKED: return "LOCKED: File locked";
case FR_NOT_ENOUGH_CORE: return "NOT_ENOUGH_CORE: Out of memory";
case FR_TOO_MANY_OPEN_FILES: return "TOO_MANY_OPEN_FILES";
case FR_INVALID_PARAMETER: return "INVALID_PARAMETER";
default: return "UNKNOWN_ERROR";
}
}
// Combine two paths
static char *combine_paths(const char *base, const char *add) {
size_t base_len = strlen(base);
size_t add_len = strlen(add);
char *result = malloc(base_len + add_len + 2);
if (!result) return NULL;
if (base_len > 0 && base[base_len - 1] == '/') {
s_printf(result, "%s%s", base, add);
} else {
s_printf(result, "%s/%s", base, add);
}
return result;
}
// Copy a single file with logging
int file_copy(const char *src, const char *dst) {
FIL fin, fout;
FILINFO fno;
int res;
log_write("COPY: %s -> %s\n", src, dst);
res = f_open(&fin, src, FA_READ | FA_OPEN_EXISTING);
if (res != FR_OK) {
log_write(" ERROR open src: %s\n", fs_error_str(res));
return res;
}
f_stat(src, &fno);
u64 file_size = f_size(&fin);
log_write(" Size: %d bytes\n", (u32)file_size);
res = f_open(&fout, dst, FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) {
f_close(&fin);
log_write(" ERROR open dst: %s\n", fs_error_str(res));
return res;
}
u8 *buf = malloc(FS_BUFFER_SIZE);
if (!buf) {
f_close(&fin);
f_close(&fout);
log_write(" ERROR: Out of memory for buffer\n");
return FR_NOT_ENOUGH_CORE;
}
u64 remaining = file_size;
UINT br, bw;
while (remaining > 0) {
UINT to_copy = (remaining > FS_BUFFER_SIZE) ? FS_BUFFER_SIZE : (UINT)remaining;
res = f_read(&fin, buf, to_copy, &br);
if (res != FR_OK) {
log_write(" ERROR read: %s\n", fs_error_str(res));
break;
}
if (br != to_copy) {
log_write(" ERROR: Read %d bytes, expected %d\n", br, to_copy);
res = FR_DISK_ERR;
break;
}
res = f_write(&fout, buf, to_copy, &bw);
if (res != FR_OK) {
log_write(" ERROR write: %s\n", fs_error_str(res));
break;
}
if (bw != to_copy) {
log_write(" ERROR: Wrote %d bytes, expected %d\n", bw, to_copy);
res = FR_DISK_ERR;
break;
}
remaining -= to_copy;
}
free(buf);
f_close(&fin);
f_close(&fout);
if (res == FR_OK) {
f_chmod(dst, fno.fattrib, 0x3A);
log_write(" OK\n");
}
return res;
}
// Recursively delete a folder with logging
int folder_delete(const char *path) {
DIR dir;
FILINFO fno;
int res;
log_write("DELETE: %s\n", path);
res = f_opendir(&dir, path);
if (res != FR_OK) {
// Maybe it's a file, try to delete it
log_write(" Not a dir, trying as file...\n");
res = f_unlink(path);
if (res != FR_OK) {
log_write(" ERROR unlink: %s\n", fs_error_str(res));
} else {
log_write(" OK (file deleted)\n");
}
return res;
}
int file_count = 0;
int dir_count = 0;
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res));
break;
}
if (fno.fname[0] == 0) break; // End of directory
char *full_path = combine_paths(path, fno.fname);
if (!full_path) {
res = FR_NOT_ENOUGH_CORE;
break;
}
if (fno.fattrib & AM_DIR) {
dir_count++;
res = folder_delete(full_path);
} else {
file_count++;
log_write(" DEL: %s\n", fno.fname);
// Clear read-only attribute if set (common with some CFW packs)
if (fno.fattrib & AM_RDO) {
f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO);
}
res = f_unlink(full_path);
if (res != FR_OK) {
log_write(" ERROR: %s\n", fs_error_str(res));
}
}
free(full_path);
if (res != FR_OK) break;
}
f_closedir(&dir);
if (res == FR_OK || res == FR_NO_FILE) {
log_write(" Removing dir: %s (%d files, %d subdirs)\n", path, file_count, dir_count);
// 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);
if (res != FR_OK) {
log_write(" ERROR rmdir: %s\n", fs_error_str(res));
} else {
log_write(" OK\n");
}
}
return res;
}
// Recursively copy a folder with logging
int folder_copy(const char *src, const char *dst) {
DIR dir;
FILINFO fno;
int res;
log_write("FOLDER COPY: %s -> %s\n", src, dst);
res = f_opendir(&dir, src);
if (res != FR_OK) {
log_write(" ERROR opendir src: %s\n", fs_error_str(res));
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
char *dst_path = combine_paths(dst, folder_name);
if (!dst_path) {
f_closedir(&dir);
return FR_NOT_ENOUGH_CORE;
}
log_write(" Creating: %s\n", dst_path);
res = f_mkdir(dst_path);
if (res == FR_EXIST) {
log_write(" (already exists)\n");
res = FR_OK;
} else if (res != FR_OK) {
log_write(" ERROR mkdir: %s\n", fs_error_str(res));
f_closedir(&dir);
free(dst_path);
return res;
}
int file_count = 0;
int dir_count = 0;
// Copy contents
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res));
break;
}
if (fno.fname[0] == 0) break; // End of directory
char *src_full = combine_paths(src, fno.fname);
char *dst_full = combine_paths(dst_path, fno.fname);
if (!src_full || !dst_full) {
if (src_full) free(src_full);
if (dst_full) free(dst_full);
res = FR_NOT_ENOUGH_CORE;
break;
}
if (fno.fattrib & AM_DIR) {
dir_count++;
res = folder_copy(src_full, dst_path);
} else {
file_count++;
res = file_copy(src_full, dst_full);
}
free(src_full);
free(dst_full);
if (res != FR_OK) break;
}
f_closedir(&dir);
// Copy folder attributes
if (res == FR_OK) {
FILINFO src_info;
if (f_stat(src, &src_info) == FR_OK) {
f_chmod(dst_path, src_info.fattrib, 0x3A);
}
log_write(" Done: %d files, %d subdirs\n", file_count, dir_count);
}
free(dst_path);
return res;
}

View File

@@ -1,20 +1,20 @@
/*
* OmniNX Installer - Filesystem operations
* Based on HATS Installer
*/
#pragma once
#include <utils/types.h>
// Error code to string
const char *fs_error_str(int err);
// File/folder operations - returns 0 on success
int file_copy(const char *src, const char *dst);
int folder_copy(const char *src, const char *dst);
int folder_delete(const char *path);
// File logging
void log_init(const char *path);
void log_close(void);
void log_write(const char *fmt, ...);
/*
* OmniNX Installer - Filesystem operations
* Based on HATS Installer
*/
#pragma once
#include <utils/types.h>
// Error code to string
const char *fs_error_str(int err);
// File/folder operations - returns 0 on success
int file_copy(const char *src, const char *dst);
int folder_copy(const char *src, const char *dst);
int folder_delete(const char *path);
// File logging
void log_init(const char *path);
void log_close(void);
void log_write(const char *fmt, ...);

View File

@@ -868,12 +868,120 @@ static void install_hekate_8gb_post_copy(void)
}
}
#define INSTALL_DEBUG_INI "sd:/config/omninx/debug.ini"
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;
} 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));
if (!install_path_exists(INSTALL_DEBUG_INI))
return;
link_t sections;
list_init(&sections);
if (ini_parse(&sections, (char *)INSTALL_DEBUG_INI, false) != 1) {
ram_config_free_sections(&sections);
return;
}
LIST_FOREACH_ENTRY(ini_sec_t, sec, &sections, 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_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_wipe") == 0)
d->skip_clean_wipe = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_restore") == 0)
d->skip_clean_restore = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_install") == 0)
d->skip_clean_install = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_staging_cleanup") == 0)
d->skip_clean_staging_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_cleanup") == 0)
d->skip_update_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_install") == 0)
d->skip_update_install = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_staging_cleanup") == 0)
d->skip_update_staging_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_horizon_oc") == 0)
d->skip_update_horizon_oc = install_ini_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);
}
break;
}
ram_config_free_sections(&sections);
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;
}
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: hekate 8GB RAM Auswahl / Post-Copy\n");
install_set_color(COLOR_WHITE);
gfx_printf("\n");
}
// Main installation function
int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
int res;
install_debug_opts_t dbg;
install_debug_load(&dbg);
install_debug_print_banner(&dbg);
if (mode == INSTALL_MODE_UPDATE) {
if (pack_variant == VARIANT_OC) {
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");
@@ -886,26 +994,48 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
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);
}
// 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();
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 (pack_variant == VARIANT_OC) {
if (!dbg.skip_hekate_8gb_post_copy)
install_hekate_8gb_post_copy();
else {
install_set_color(COLOR_ORANGE);
gfx_printf("[DEBUG] hekate 8GB 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);
@@ -923,55 +1053,99 @@ int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
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();
// 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
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;
install_check_and_clear_screen_if_needed();
gfx_printf("\n");
} 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;
install_check_and_clear_screen_if_needed();
gfx_printf("\n");
} 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;
install_check_and_clear_screen_if_needed();
gfx_printf("\n");
} 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_hekate_8gb_post_copy();
else {
install_set_color(COLOR_ORANGE);
gfx_printf("[DEBUG] hekate 8GB Post-Copy uebersprungen.\n");
install_set_color(COLOR_WHITE);
}
install_check_and_clear_screen_if_needed();
// Remove staging directory (installed pack)
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;
// Remove other detected install directories (Standard/Light/OC) that were on SD
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;
}

View File

@@ -16,7 +16,7 @@ typedef enum {
// If a sub-menu aborts to Hekate / update.bin (does not return if launch succeeds)
void installer_launch_hekate_payload(void);
// Main installation function
// Main installation function (optional debug: see DEBUG_INI.md in repo root)
int perform_installation(omninx_variant_t pack_variant, install_mode_t mode);
// Update mode operations (install_update.c)

View File

@@ -34,7 +34,7 @@
// Clean mode: Backup user data
int clean_mode_backup(void) {
set_color(COLOR_CYAN);
gfx_printf(" Sichere: DBI, Tinfoil, prod.keys\n");
gfx_printf(" Sichere: DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE);
int res = backup_user_data();
if (res == FR_OK) {
@@ -95,7 +95,7 @@ int clean_mode_wipe(void) {
// Clean mode: Restore user data
int clean_mode_restore(void) {
set_color(COLOR_CYAN);
gfx_printf(" Stelle wieder her: DBI, Tinfoil, prod.keys\n");
gfx_printf(" Stelle wieder her: DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE);
int res = restore_user_data();
if (res == FR_OK) {