Compare commits

...

19 Commits

Author SHA1 Message Date
eb18bf8c72 Preserve Checkpoint across update installs.
All checks were successful
Build / Build (push) Successful in 13s
Skip deleting sd:/switch/checkpoint so user saves under saves/ are not wiped.
2026-07-17 21:37:20 +02:00
05926f7828 Show Install/Update completion based on detected mode.
All checks were successful
Build / Build (push) Successful in 14s
Drop the version header from the final summary and label the result as Update or Installation.
2026-07-17 13:43:00 +02:00
7b85d428a8 Add missing pack apps to update-mode deletion lists.
Include Benchmark-Toolbox, Furmark-NX, and other switch apps from the current OmniNX pack so updates replace them cleanly.
2026-07-17 13:42:42 +02:00
dce83bc3be Update FatFs NORTC fixed timestamp to 2026-07-17. 2026-07-17 13:42:24 +02:00
a26531c702 Add extras post-copy flow for RAM and Hekate-Pro selection.
All checks were successful
Build / Build (push) Successful in 14s
Copy sd:/extras from the pack, prompt for RAM and Hekate/AMS choices, persist them in config.ini, apply Pro/8GB payloads, then remove extras/. Use safe config.ini I/O and Joy-Con input clearing between menus.
2026-07-16 10:01:42 +02:00
c0741beda3 Preserve CyberFoil across clean install backup/restore.
All checks were successful
Build / Build (push) Successful in 13s
Back up cyberfoil.nro, config.json, and remotes/ when sd:/switch/CyberFoil exists, then restore them after the wipe.
2026-06-14 17:31:46 +02:00
d6ec345cce only check UHS on clean install
All checks were successful
Build / Build (push) Successful in 12s
2026-06-07 23:16:04 +02:00
9990dfb2ba install: shortend finish delay to 1,5 seconds
All checks were successful
Build / Build (push) Successful in 13s
2026-06-04 22:02:22 +02:00
c7ad761592 Added NiklasCFW Pack 1.6.2 Marker 2026-06-04 21:52:52 +02:00
59f103874c 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).
2026-05-17 11:21:14 +02:00
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
82639a7a86 Clean install: align deletions with NiklasCFW pack script
All checks were successful
Build / Build (push) Successful in 17s
- Remove entire sd:/atmosphere and sd:/switch (lists simplified)
- Add sd:/SaltySD/patches; drop nyx.ini and config/quickntp to match script
- Delete sd:/switch/tinfoil/db after backup restore via clean_post_restore_dirs_to_delete

Made-with: Cursor
2026-04-12 00:34:14 +02:00
bff8bc1910 Clean install: remove sd:/TegraExplorer entirely
Some checks failed
Build / Build (push) Failing after 14s
Delete the TegraExplorer directory in misc dirs instead of individual
script files under scripts/.

Made-with: Cursor
2026-04-11 19:44:47 +02:00
c9bcb29f50 Exclude source/TegraExplorer from Makefile object list
The vendored tree under source/TegraExplorer is optional reference-only;
building it fails without extra usb/ headers. The payload uses bdk/ at repo
root instead.

Made-with: Cursor
2026-04-11 00:14:41 +02:00
dc97677e72 Delete sd:/themes/systemData on update and clean installs
Removes cached qlaunch/FW theme extracts so theme-patches apply after
firmware updates; clearing atmosphere/contents alone is insufficient.

Made-with: Cursor
2026-04-11 00:12:45 +02:00
4535c4508f Added TegraExplorer 2026-04-10 22:32:01 +02:00
21 changed files with 1896 additions and 1217 deletions

3
.gitignore vendored
View File

@@ -20,3 +20,6 @@ Thumbs.db
# Version file is tracked, ignore local edits
# VERSION
# Vendored TegraExplorer tree
source/TegraExplorer/

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 `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 `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:
@@ -88,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`
@@ -305,17 +309,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 +324,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 +347,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
---

View File

@@ -31,6 +31,9 @@ OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(BDKDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(BDKDIR), *.S *.c)))
# Optional vendored tree under source/TegraExplorer/ is not part of this payload build.
OBJS := $(filter-out $(BUILDDIR)/$(TARGET)/TegraExplorer/%,$(OBJS))
GFX_INC := '"../$(SOURCEDIR)/gfx.h"'
FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"'

59
PRESERVE_INI.md Normal file
View 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`).

328
README.md
View File

@@ -1,163 +1,165 @@
# 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)
- **[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
### 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

@@ -1 +1 @@
1.0.0
1.0.1

View File

@@ -721,6 +721,12 @@ void jc_deinit()
clock_disable_uart(UART_C);
}
/* Drop current button state without power-cycling the Joy-Cons. */
void jc_clear_input(void)
{
jc_gamepad.buttons = 0;
}
static void jc_init_conn(joycon_ctxt_t *jc)
{
if (((u32)get_tmr_ms() - jc->last_received_time) > 1000)

View File

@@ -92,6 +92,7 @@ typedef struct _jc_gamepad_rpt_t
void jc_power_supply(u8 uart, bool enable);
void jc_init_hw();
void jc_deinit();
void jc_clear_input(void);
jc_gamepad_rpt_t *joycon_poll();
jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos);

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;
}
@@ -47,6 +39,35 @@ int backup_user_data(void) {
return res;
}
}
// Backup CyberFoil (nro, config, remotes) if installed
if (path_exists(CYBERFOIL_DIR)) {
res = f_mkdir(TEMP_BACKUP_CYBERFOIL);
if (res != FR_OK && res != FR_EXIST) {
return res;
}
if (path_exists(CYBERFOIL_NRO)) {
res = file_copy(CYBERFOIL_NRO, TEMP_BACKUP_CYBERFOIL_NRO);
if (res != FR_OK) {
return res;
}
}
if (path_exists(CYBERFOIL_CONFIG)) {
res = file_copy(CYBERFOIL_CONFIG, TEMP_BACKUP_CYBERFOIL_CONFIG);
if (res != FR_OK) {
return res;
}
}
if (path_exists(CYBERFOIL_REMOTES)) {
res = folder_copy(CYBERFOIL_REMOTES, TEMP_BACKUP_CYBERFOIL);
if (res != FR_OK) {
return res;
}
}
}
return FR_OK;
}
@@ -61,25 +82,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;
}
}
@@ -90,6 +101,35 @@ int restore_user_data(void) {
return res;
}
}
// Restore CyberFoil if backed up
if (path_exists(TEMP_BACKUP_CYBERFOIL)) {
res = f_mkdir(CYBERFOIL_DIR);
if (res != FR_OK && res != FR_EXIST) {
return res;
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_NRO)) {
res = file_copy(TEMP_BACKUP_CYBERFOIL_NRO, CYBERFOIL_NRO);
if (res != FR_OK) {
return res;
}
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_CONFIG)) {
res = file_copy(TEMP_BACKUP_CYBERFOIL_CONFIG, CYBERFOIL_CONFIG);
if (res != FR_OK) {
return res;
}
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_REMOTES)) {
res = folder_copy(TEMP_BACKUP_CYBERFOIL_REMOTES, CYBERFOIL_DIR);
if (res != FR_OK) {
return res;
}
}
}
return FR_OK;
}

View File

@@ -7,6 +7,19 @@
#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"
/** CyberFoil homebrew; preserved across clean install (nro, config, remotes only). */
#define CYBERFOIL_DIR "sd:/switch/CyberFoil"
#define CYBERFOIL_NRO CYBERFOIL_DIR "/cyberfoil.nro"
#define CYBERFOIL_CONFIG CYBERFOIL_DIR "/config.json"
#define CYBERFOIL_REMOTES CYBERFOIL_DIR "/remotes"
#define TEMP_BACKUP_CYBERFOIL TEMP_BACKUP_PATH "/CyberFoil"
#define TEMP_BACKUP_CYBERFOIL_NRO TEMP_BACKUP_CYBERFOIL "/cyberfoil.nro"
#define TEMP_BACKUP_CYBERFOIL_CONFIG TEMP_BACKUP_CYBERFOIL "/config.json"
#define TEMP_BACKUP_CYBERFOIL_REMOTES TEMP_BACKUP_CYBERFOIL "/remotes"
/** Live HorizonOC settings (Overclock tool). */
#define HORIZON_OC_CONFIG_PATH "sd:/config/horizon-oc/config.ini"
@@ -14,7 +27,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 (CyberFoil, DBI dbi.config, prod.keys) before clean install
int backup_user_data(void);
// Restore user data after clean install

View File

@@ -1,86 +1,23 @@
/*
* OmniNX Installer - Deletion Lists for Clean Install Mode
* Selective deletion when no OmniNX install was found.
* Only listed paths are removed; does not wipe whole card or user configs.
* 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). CyberFoil/DBI/prod.keys backup is in backup.c.
*/
#pragma once
// Atmosphere subdirectories to delete
// Entire atmosphere/ tree (NiklasCFW: deldir("sd:/atmosphere"))
static const char* clean_atmosphere_dirs_to_delete[] = {
"sd:/atmosphere/config",
"sd:/atmosphere/crash_reports",
"sd:/atmosphere/erpt_reports",
"sd:/atmosphere/exefs_patches/CrunchPatch",
"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/nfim_ctest",
"sd:/atmosphere/exefs_patches/nim_ctest",
"sd:/atmosphere/exefs_patches/nvnflinger_cmu",
"sd:/atmosphere/extrazz",
"sd:/atmosphere/fatal_errors",
"sd:/atmosphere/fatal_reports",
"sd:/atmosphere/flags",
"sd:/atmosphere/hbl_html",
"sd:/atmosphere/hosts",
"sd:/atmosphere/kips",
"sd:/atmosphere/kip1",
"sd:/atmosphere/kip_patches",
"sd:/atmosphere/logs",
"sd:/atmosphere",
NULL
};
// Atmosphere contents directories (title IDs; only under atmosphere/contents/)
static const char* clean_atmosphere_contents_dirs_to_delete[] = {
"sd:/atmosphere/contents/0000000000534C56",
"sd:/atmosphere/contents/00FF0000B378D640",
"sd:/atmosphere/contents/00FF0000636C6BFF",
"sd:/atmosphere/contents/00FF0000A53BB665",
"sd:/atmosphere/contents/0100000000000008",
"sd:/atmosphere/contents/010000000000000D",
"sd:/atmosphere/contents/010000000000002B",
"sd:/atmosphere/contents/0100000000000032",
"sd:/atmosphere/contents/0100000000000034",
"sd:/atmosphere/contents/0100000000000036",
"sd:/atmosphere/contents/0100000000000037",
"sd:/atmosphere/contents/010000000000003C",
"sd:/atmosphere/contents/0100000000000042",
"sd:/atmosphere/contents/0100000000000895",
"sd:/atmosphere/contents/0100000000000F12",
"sd:/atmosphere/contents/0100000000001000",
"sd:/atmosphere/contents/0100000000001007",
"sd:/atmosphere/contents/0100000000001013",
"sd:/atmosphere/contents/010000000000DA7A",
"sd:/atmosphere/contents/010000000000bd00",
"sd:/atmosphere/contents/01006a800016e000",
"sd:/atmosphere/contents/01009D901BC56000",
"sd:/atmosphere/contents/0100A3900C3E2000",
"sd:/atmosphere/contents/0100F43008C44000",
"sd:/atmosphere/contents/050000BADDAD0000",
"sd:/atmosphere/contents/4200000000000000",
"sd:/atmosphere/contents/420000000000000B",
"sd:/atmosphere/contents/420000000000000E",
"sd:/atmosphere/contents/4200000000000010",
"sd:/atmosphere/contents/4200000000000FFF",
"sd:/atmosphere/contents/420000000007E51A",
"sd:/atmosphere/contents/420000000007E51B",
"sd:/atmosphere/contents/690000000000000D",
NULL
};
// Atmosphere files to delete
static const char* clean_atmosphere_files_to_delete[] = {
"sd:/atmosphere/config/exosphere.ini",
"sd:/atmosphere/config/stratosphere.ini",
"sd:/atmosphere/hbl.nsp",
"sd:/atmosphere/package3",
"sd:/atmosphere/reboot_payload.bin",
"sd:/atmosphere/stratosphere.romfs",
NULL
};
@@ -101,7 +38,6 @@ static const char* clean_bootloader_files_to_delete[] = {
"sd:/bootloader/ArgonNX.bin",
"sd:/bootloader/bootlogo.bmp",
"sd:/bootloader/hekate_ipl.ini",
"sd:/bootloader/nyx.ini",
"sd:/bootloader/patches.ini",
"sd:/bootloader/update.bin",
"sd:/bootloader/ini/EmuMMC ohne Mods.ini",
@@ -114,7 +50,6 @@ static const char* clean_config_dirs_to_delete[] = {
"sd:/config/blue_pack_updater",
"sd:/config/kefir-updater",
"sd:/config/nx-hbmenu",
"sd:/config/quickntp",
"sd:/config/sys-con",
"sd:/config/sys-patch",
"sd:/config/uberhand",
@@ -122,120 +57,13 @@ static const char* clean_config_dirs_to_delete[] = {
NULL
};
// Switch directories to delete
// Entire switch/ tree (clean install recreates sd:/switch after wipe)
static const char* clean_switch_dirs_to_delete[] = {
"sd:/switch/.overlays",
"sd:/switch/.packages",
"sd:/switch/90DNS_tester",
"sd:/switch/aio-switch-updater",
"sd:/switch/amsPLUS-downloader",
"sd:/switch/appstore",
"sd:/switch/AtmoXL-Titel-Installer",
"sd:/switch/breeze",
"sd:/switch/checkpoint",
"sd:/switch/cheats-updater",
"sd:/switch/chiaki",
"sd:/switch/ChoiDujourNX",
"sd:/switch/crash_ams",
"sd:/switch/Daybreak",
"sd:/switch/DNS_mitm Tester",
"sd:/switch/EdiZon",
"sd:/switch/Fizeau",
"sd:/switch/FTPD",
"sd:/switch/fw-downloader",
"sd:/switch/gamecard_installer",
"sd:/switch/Goldleaf",
"sd:/switch/haze",
"sd:/switch/JKSV",
"sd:/switch/kefir-updater",
"sd:/switch/ldnmitm_config",
"sd:/switch/Linkalho",
"sd:/switch/Moonlight-Switch",
"sd:/switch/Neumann",
"sd:/switch/NX-Activity-Log",
"sd:/switch/NX-Save-Sync",
"sd:/switch/NX-Shell",
"sd:/switch/NX-Update-Checker ",
"sd:/switch/NXGallery",
"sd:/switch/NXRemoteLauncher",
"sd:/switch/NXThemesInstaller",
"sd:/switch/nxdumptool",
"sd:/switch/nxmtp",
"sd:/switch/Payload_launcher",
"sd:/switch/Reboot",
"sd:/switch/reboot_to_argonNX",
"sd:/switch/reboot_to_hekate",
"sd:/switch/Shutdown_System",
"sd:/switch/SimpleModDownloader",
"sd:/switch/SimpleModManager",
"sd:/switch/sphaira",
"sd:/switch/studious-pancake",
"sd:/switch/Switch-Time",
"sd:/switch/SwitchIdent",
"sd:/switch/Switch_themes_Installer",
"sd:/switch/Switchfin",
"sd:/switch/Sys-Clk Manager",
"sd:/switch/Sys-Con",
"sd:/switch/sys-clk-manager",
"sd:/switch/themezer-nx",
"sd:/switch/themezernx",
"sd:/switch/tinwoo",
"sd:/switch",
NULL
};
// Switch files (NRO) to delete
static const char* clean_switch_files_to_delete[] = {
"sd:/switch/90DNS_tester/90DNS_tester.nro",
"sd:/switch/breeze.nro",
"sd:/switch/cheats-updater.nro",
"sd:/switch/chiaki.nro",
"sd:/switch/ChoiDujourNX.nro",
"sd:/switch/daybreak.nro",
"sd:/switch/DBI.nro",
"sd:/switch/DBI/DBI.nro",
"sd:/switch/DBI/DBI_810_DE.nro",
"sd:/switch/DBI/DBI_810_EN.nro",
"sd:/switch/DBI/DBI_845_DE.nro",
"sd:/switch/DBI/DBI_845_EN.nro",
"sd:/switch/DBI/DBI_849_DE.nro",
"sd:/switch/DBI/DBI_849_EN.nro",
"sd:/switch/DBI/DBI_874_DE.nro",
"sd:/switch/DBI/DBI_874_EN.nro",
"sd:/switch/DBI_810_DE/DBI_810.nro",
"sd:/switch/DBI_810_DE/DBI_810_DE.nro",
"sd:/switch/DBI_810_EN/DBI_810_EN.nro",
"sd:/switch/DBI_RU/DBI_RU.nro",
"sd:/switch/DBI/DBI_EN.nro",
"sd:/switch/DBI_DE/DBI_DE.nro",
"sd:/switch/DNS_mitm Tester.nro",
"sd:/switch/EdiZon.nro",
"sd:/switch/Fizeau.nro",
"sd:/switch/Goldleaf.nro",
"sd:/switch/haze.nro",
"sd:/switch/JKSV.nro",
"sd:/switch/ldnmitm_config.nro",
"sd:/switch/linkalho.nro",
"sd:/switch/Moonlight-Switch.nro",
"sd:/switch/Neumann.nro",
"sd:/switch/NX-Shell.nro",
"sd:/switch/NXGallery.nro",
"sd:/switch/NXThemesInstaller.nro",
"sd:/switch/nxdumptool.nro",
"sd:/switch/nxtc.bin",
"sd:/switch/reboot_to_payload.nro",
"sd:/switch/SimpleModDownloader.nro",
"sd:/switch/SimpleModManager.nro",
"sd:/switch/sphaira.nro",
"sd:/switch/SwitchIdent.nro",
"sd:/switch/Switch_themes_Installer/NXThemesInstaller.nro",
"sd:/switch/Switchfin.nro",
"sd:/switch/Sys-Clk Manager/sys-clk-manager.nro",
"sd:/switch/Sys-Con.nro",
"sd:/switch/sys-clk-manager.nro",
"sd:/switch/tinfoil.nro",
"sd:/switch/tinfoil/tinfoil.nro",
"sd:/switch/tinwoo.nro",
"sd:/switch/tinwoo/tinwoo.nro",
NULL
};
@@ -263,8 +91,10 @@ static const char* clean_misc_dirs_to_delete[] = {
"sd:/NSPs (Tools)",
"sd:/Patched Apps",
"sd:/SaltySD/flags",
"sd:/SaltySD/patches",
"sd:/scripts",
"sd:/switch/tinfoil/db",
"sd:/TegraExplorer",
"sd:/themes/systemData",
"sd:/tools",
"sd:/warmboot_mariko",
NULL
@@ -272,10 +102,6 @@ static const char* clean_misc_dirs_to_delete[] = {
// Miscellaneous files to delete
static const char* clean_misc_files_to_delete[] = {
"sd:/TegraExplorer/scripts/Reparatur Skript fuer error 010000000000BD00.te",
"sd:/TegraExplorer/scripts/Reparatur Skript fuer error 0100000000001000.te",
"sd:/TegraExplorer/scripts/Reparatur Skript fuer error 690000000000000D.te",
"sd:/TegraExplorer/scripts/Reparatur Skript fuer error 4200000000000010.te",
"sd:/fusee-primary.bin",
"sd:/fusee.bin",
"sd:/SaltySD/exceptions.txt",
@@ -287,6 +113,12 @@ static const char* clean_misc_files_to_delete[] = {
NULL
};
// 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
};
// Old version marker files to delete (clean install only)
static const char* old_version_files_to_delete[] = {
"sd:/1.4.0-pre",
@@ -296,5 +128,6 @@ static const char* old_version_files_to_delete[] = {
"sd:/1.5.0",
"sd:/1.6.0",
"sd:/1.6.1",
"sd:/1.6.2",
NULL
};

View File

@@ -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",
@@ -132,17 +134,19 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/amsPLUS-downloader",
"sd:/switch/appstore",
"sd:/switch/AtmoXL-Titel-Installer",
"sd:/switch/Benchmark-Toolbox",
"sd:/switch/breeze",
"sd:/switch/checkpoint",
"sd:/switch/cheats-updater",
"sd:/switch/chiaki",
"sd:/switch/ChoiDujourNX",
"sd:/switch/crash_ams",
"sd:/switch/Daybreak",
"sd:/switch/DNS-Block_Tester",
"sd:/switch/DNS_mitm Tester",
"sd:/switch/EdiZon",
"sd:/switch/Fizeau",
"sd:/switch/FTPD",
"sd:/switch/Furmark-NX",
"sd:/switch/fw-downloader",
"sd:/switch/gamecard_installer",
"sd:/switch/Goldleaf",
@@ -167,6 +171,7 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/reboot_to_argonNX",
"sd:/switch/reboot_to_hekate",
"sd:/switch/Shutdown_System",
"sd:/switch/SimpleModAlchemist",
"sd:/switch/SimpleModDownloader",
"sd:/switch/SimpleModManager",
"sd:/switch/sphaira",
@@ -174,18 +179,22 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/Switch-Time",
"sd:/switch/SwitchIdent",
"sd:/switch/Switch_themes_Installer",
"sd:/switch/swr-ini-tool",
"sd:/switch/Sys-Clk Manager",
"sd:/switch/Sys-Con",
"sd:/switch/sys-clk-manager",
"sd:/switch/themezer-nx",
"sd:/switch/ThemezerNX",
"sd:/switch/themezernx",
"sd:/switch/tinwoo",
"sd:/switch/Ultrahand-Reload",
NULL
};
// Switch files (NRO) to delete
static const char* switch_files_to_delete[] = {
"sd:/switch/90DNS_tester/90DNS_tester.nro",
"sd:/switch/Benchmark-Toolbox/Benchmark-Toolbox.nro",
"sd:/switch/breeze.nro",
"sd:/switch/cheats-updater.nro",
"sd:/switch/chiaki.nro",
@@ -205,9 +214,11 @@ static const char* switch_files_to_delete[] = {
"sd:/switch/DBI_RU/DBI_RU.nro",
"sd:/switch/DBI/DBI_EN.nro",
"sd:/switch/DBI_DE/DBI_DE.nro",
"sd:/switch/DNS-Block_Tester/dns_tester.nro",
"sd:/switch/DNS_mitm Tester.nro",
"sd:/switch/EdiZon.nro",
"sd:/switch/Fizeau.nro",
"sd:/switch/Furmark-NX/Furmark-NX.nro",
"sd:/switch/Goldleaf.nro",
"sd:/switch/haze.nro",
"sd:/switch/JKSV.nro",
@@ -221,6 +232,7 @@ static const char* switch_files_to_delete[] = {
"sd:/switch/nxdumptool.nro",
"sd:/switch/nxtc.bin",
"sd:/switch/reboot_to_payload.nro",
"sd:/switch/SimpleModAlchemist/Simple_Mod_Alchemist.nro",
"sd:/switch/SimpleModDownloader.nro",
"sd:/switch/SimpleModManager.nro",
"sd:/switch/sphaira.nro",
@@ -230,10 +242,12 @@ static const char* switch_files_to_delete[] = {
"sd:/switch/Sys-Clk Manager/sys-clk-manager.nro",
"sd:/switch/Sys-Con.nro",
"sd:/switch/sys-clk-manager.nro",
"sd:/switch/ThemezerNX/themezer-nx.nro",
"sd:/switch/tinfoil.nro",
"sd:/switch/tinfoil/tinfoil.nro",
"sd:/switch/tinwoo.nro",
"sd:/switch/tinwoo/tinwoo.nro",
"sd:/switch/Ultrahand-Reload/Ultrahand-Reload.nro",
NULL
};
@@ -263,6 +277,7 @@ static const char* misc_dirs_to_delete[] = {
"sd:/SaltySD/flags",
"sd:/scripts",
"sd:/switch/tinfoil/db",
"sd:/themes/systemData",
"sd:/tools",
"sd:/warmboot_mariko",
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

@@ -21,10 +21,7 @@
#define GROUPED_DELETE_MAX_ENTRIES 512
#ifndef VERSION
#define VERSION "1.0.0"
#endif
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
@@ -463,7 +460,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 +535,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 +555,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);
@@ -635,12 +632,19 @@ int delete_path_list(const char* paths[], const char* description) {
return (failed == 0) ? FR_OK : FR_DISK_ERR;
}
#define HEKATE_8GB_SRC "sd:/bootloader/hekate_8gb.bin"
#define PAYLOAD_BIN_DST "sd:/payload.bin"
#define UPDATE_BIN_DST "sd:/bootloader/update.bin"
#define RAM_CONFIG_PATH "sd:/config/omninx/ram_config.ini"
#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 { RAM_READ_OK = 0, RAM_READ_NEED_MENU = 1 };
enum {
INSTALL_CONFIG_OK = 0,
INSTALL_CONFIG_NEED_MENU = 1,
};
static void unlink_ignore_err(const char *path)
{
@@ -652,30 +656,13 @@ static void unlink_ignore_err(const char *path)
f_unlink(path);
}
static void ram_config_ensure_parent_dirs(void)
static void install_config_ensure_parent_dirs(void)
{
f_mkdir("sd:/config");
f_mkdir("sd:/config/omninx");
}
static int ram_config_write(bool use_8gb)
{
ram_config_ensure_parent_dirs();
FIL f;
if (f_open(&f, RAM_CONFIG_PATH, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
return FR_DISK_ERR;
static const char sec[] = "[Ram]\n";
static const char line0[] = "8gb=0\n";
static const char line1[] = "8gb=1\n";
UINT bw;
f_write(&f, sec, sizeof(sec) - 1, &bw);
f_write(&f, use_8gb ? line1 : line0, sizeof(line0) - 1, &bw);
f_close(&f);
return FR_OK;
}
static void ram_config_free_sections(link_t *sections)
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) {
@@ -686,46 +673,278 @@ static void ram_config_free_sections(link_t *sections)
}
}
/* Valid [Ram] 8gb=0|1 → silent; missing file or invalid → menu. */
static int read_ram_config_silent(const char *path, bool *use_8gb)
static bool install_config_read_key_bool(const char *path, const char *section, const char *key, bool *out)
{
if (!install_path_exists(path))
return RAM_READ_NEED_MENU;
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 (or legacy ram_config.ini) → silent; else menu. */
static int install_config_read_ram(bool *use_8gb)
{
static const char *paths[] = { INSTALL_CONFIG_INI, INSTALL_CONFIG_INI_LEGACY, NULL };
for (int i = 0; paths[i]; i++) {
if (!install_path_exists(paths[i]))
continue;
if (install_config_read_key_bool(paths[i], "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;
}
#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 *)path, false) != 1) {
ram_config_free_sections(&sections);
return RAM_READ_NEED_MENU;
if (ini_parse(&sections, (char *)INSTALL_PRESERVE_INI, false) != 1) {
install_ini_free_sections(&sections);
free(g_preserve_block);
g_preserve_block = NULL;
return;
}
bool found = false;
bool eight = false;
LIST_FOREACH_ENTRY(ini_sec_t, sec, &sections, link) {
if (sec->name && !strcmp(sec->name, "Ram")) {
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
if (kv->key && kv->val && !strcmp(kv->key, "8gb")) {
if (!strcmp(kv->val, "0")) {
found = true;
eight = false;
} else if (!strcmp(kv->val, "1")) {
found = true;
eight = true;
}
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;
}
}
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(&sections);
if (g_preserve_count == 0) {
free(g_preserve_block);
g_preserve_block = NULL;
return;
}
ram_config_free_sections(&sections);
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);
}
if (!found)
return RAM_READ_NEED_MENU;
*use_8gb = eight;
return RAM_READ_OK;
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);
}
/* Same navigation as main.c multi-variant menu; returns false if user aborts (payload/reboot). */
@@ -838,7 +1057,6 @@ static bool install_ram_config_menu(bool *use_8gb)
}
*use_8gb = (selected == 1);
ram_config_write(*use_8gb);
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
@@ -846,34 +1064,374 @@ static bool install_ram_config_menu(bool *use_8gb)
return true;
}
/* After pack copy: use ram_config.ini [Ram] 8gb=0|1 if present; else menu, write file, then apply. No fuse autodetect. */
static void install_hekate_8gb_post_copy(void)
static bool install_hekate_ams_menu(bool *use_pro)
{
if (!install_path_exists(HEKATE_8GB_SRC))
jc_init_hw();
while (btn_read() & BTN_POWER)
msleep(50);
int selected = 0;
int prev_selected = 0;
bool confirmed = false;
const int n = 2;
const char *labels[2] = {
"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);
u32 menu_x, menu_y;
gfx_con_getpos(&menu_x, &menu_y);
for (int i = 0; i < n; i++) {
if (i == selected) {
install_set_color(COLOR_GREEN);
gfx_printf(" > %s\n", labels[i]);
install_set_color(COLOR_WHITE);
} else {
gfx_printf(" %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;
bool menu_aborted = false;
while (!confirmed && !menu_aborted) {
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)) {
menu_aborted = true;
break;
}
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)
confirmed = true;
prev_up = cur_up;
prev_down = cur_down;
if (btn & BTN_POWER)
confirmed = true;
msleep(50);
}
if (menu_aborted) {
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;
}
*use_pro = (selected == 1);
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
install_print_main_header();
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;
char src[256];
s_printf(src, "%s/%s", HEKATE_AMS_PRO_SRC, fno.fname);
if (fno.fattrib & AM_DIR)
res = folder_copy(src, "sd:/");
else {
char dst[256];
s_printf(dst, "sd:/%s", fno.fname);
res = file_copy(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;
bool use_8gb;
if (read_ram_config_silent(RAM_CONFIG_PATH, &use_8gb) != RAM_READ_OK) {
if (!install_ram_config_menu(&use_8gb))
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)
{
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 (use_8gb) {
int r1 = file_copy(HEKATE_8GB_SRC, PAYLOAD_BIN_DST);
int r2 = file_copy(HEKATE_8GB_SRC, UPDATE_BIN_DST);
if (r1 == FR_OK && r2 == FR_OK)
unlink_ignore_err(HEKATE_8GB_SRC);
} else {
unlink_ignore_err(HEKATE_8GB_SRC);
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"
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 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) {
install_ini_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_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);
}
break;
}
install_ini_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: extras RAM / Hekate-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 +1444,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_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);
@@ -923,55 +1503,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);
}
install_hekate_8gb_post_copy();
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();
// 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)
@@ -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);

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: CyberFoil, DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE);
int res = backup_user_data();
if (res == FR_OK) {
@@ -95,13 +95,14 @@ 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: CyberFoil, DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE);
int res = restore_user_data();
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" [OK] Wiederherstellung abgeschlossen\n");
set_color(COLOR_WHITE);
delete_path_list(clean_post_restore_dirs_to_delete, "switch/ (nach Restore)");
}
cleanup_backup();
return res;

View File

@@ -12,10 +12,6 @@
#include <string.h>
#include <utils/sprintf.h>
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#undef COLOR_CYAN
#undef COLOR_WHITE
#undef COLOR_GREEN
@@ -37,6 +33,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 +64,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);
@@ -119,6 +119,10 @@ int update_mode_install(omninx_variant_t variant) {
if (res != FR_OK && res != FR_NO_FILE) return res;
}
s_printf(src_path, "%s/extras", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "extras/");
if (res != FR_OK && res != FR_NO_FILE) return res;
set_color(COLOR_CYAN);
gfx_printf(" Kopiere Root-Dateien...\n");
set_color(COLOR_WHITE);

View File

@@ -1,294 +1,294 @@
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86604 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 2
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 1
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_FASTFS 0
#if FF_FASTFS
#define FF_USE_FASTSEEK 1
#else
#define FF_USE_FASTSEEK 0
#endif
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 1
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 850
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
/ 0 - Include all code pages above and configured by f_setcp()
*/
#define FF_USE_LFN 3
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 0
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 4
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 1
// Order is important. Any change to order, must also be reflected to diskio drive enum.
#define FF_VOLUME_STRS "sd","ram","emmc","bis"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 1
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 1
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2020
/* The option FF_FS_NORTC switches timestamp function. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/*--- End of configuration options ---*/
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86604 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 2
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 1
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_FASTFS 0
#if FF_FASTFS
#define FF_USE_FASTSEEK 1
#else
#define FF_USE_FASTSEEK 0
#endif
/* This option switches fast seek function. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 1
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 850
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
/ 437 - U.S.
/ 720 - Arabic
/ 737 - Greek
/ 771 - KBL
/ 775 - Baltic
/ 850 - Latin 1
/ 852 - Latin 2
/ 855 - Cyrillic
/ 857 - Turkish
/ 860 - Portuguese
/ 861 - Icelandic
/ 862 - Hebrew
/ 863 - Canadian French
/ 864 - Arabic
/ 865 - Nordic
/ 866 - Russian
/ 869 - Greek 2
/ 932 - Japanese (DBCS)
/ 936 - Simplified Chinese (DBCS)
/ 949 - Korean (DBCS)
/ 950 - Traditional Chinese (DBCS)
/ 0 - Include all code pages above and configured by f_setcp()
*/
#define FF_USE_LFN 3
#define FF_MAX_LFN 255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 0
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
*/
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES 4
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 1
// Order is important. Any change to order, must also be reflected to diskio drive enum.
#define FF_VOLUME_STRS "sd","ram","emmc","bis"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
/* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 1
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 1
#define FF_NORTC_MON 7
#define FF_NORTC_MDAY 17
#define FF_NORTC_YEAR 2026
/* The option FF_FS_NORTC switches timestamp function. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_LOCK 0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/ is 1.
/
/ 0: Disable file lock function. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock function. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/*--- End of configuration options ---*/

View File

@@ -6,10 +6,6 @@
* Based on TegraExplorer/hekate by CTCaer, naehrwert, shchmue
*/
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#include <string.h>
#include <display/di.h>
@@ -145,7 +141,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 +297,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);
@@ -480,7 +476,8 @@ void ipl_main(void) {
gfx_con_setpos(0, 0);
print_header();
// UHS class check: recommend U2 / V30 / A2 for emuMMC (speed and reliability)
// UHS class check: recommend U2 / V30 / A2 for emuMMC (clean install only)
if (mode == INSTALL_MODE_CLEAN) {
#define UHS_U2_MIN 2
#define UHS_V30_MIN 30
#define UHS_A2_MIN 2
@@ -557,6 +554,7 @@ void ipl_main(void) {
gfx_con_setpos(0, 0);
print_header();
}
}
// Show information
set_color(COLOR_CYAN);
@@ -651,7 +649,7 @@ void ipl_main(void) {
int result = perform_installation(pack_variant, mode);
// Wait 3 seconds before clearing screen to allow errors to be visible
msleep(3000);
msleep(1500);
// take_screenshot(); // Take a screenshot of the installation summary
// Clear screen for final summary to ensure it's visible
@@ -659,25 +657,20 @@ void ipl_main(void) {
gfx_con_setpos(0, 0);
// Summary
set_color(COLOR_CYAN);
gfx_printf("========================================\n");
gfx_printf(" OmniNX Installer Payload v%s\n", VERSION);
gfx_printf("========================================\n\n");
set_color(COLOR_WHITE);
const char *op_name = (mode == INSTALL_MODE_UPDATE) ? " Update" : " Installation";
if (result == FR_OK && total_errors == 0) {
set_color(COLOR_GREEN);
gfx_printf("========================================\n");
gfx_printf(" Installation abgeschlossen!\n");
gfx_printf("========================================\n");
gfx_printf("=======================================\n");
gfx_printf(" %s abgeschlossen!\n", op_name);
gfx_printf("=======================================\n");
} else {
set_color(COLOR_RED);
gfx_printf("========================================\n");
gfx_printf(" Installation beendet\n");
gfx_printf("=======================================\n");
gfx_printf(" %s beendet\n", op_name);
if (total_errors > 0) {
gfx_printf(" %d Fehler\n", total_errors);
}
gfx_printf("========================================\n");
gfx_printf("=======================================\n");
}
set_color(COLOR_WHITE);
@@ -721,16 +714,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");