Compare commits

..

52 Commits

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

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

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

Made-with: Cursor
2026-04-12 16:22:41 +02:00
6780936d2d chore: normalize line endings in fs.c and fs.h
Made-with: Cursor
2026-04-12 16:22:39 +02:00
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
726ef77194 update: delete atmosphere kips loader.kip and hoc.kip on OmniNX update
All checks were successful
Build / Build (push) Successful in 15s
Made-with: Cursor
2026-04-09 22:11:31 +02:00
13c35400a1 install: backup and restore HorizonOC config.ini on OC pack update
- Copy sd:/config/horizon-oc/config.ini to sd:/.omninx_oc_update before
  update cleanup/copy; restore after Hekate step, then remove the scratch dir.
- Only when INSTALL_MODE_UPDATE and VARIANT_OC.

Made-with: Cursor
2026-04-09 22:11:30 +02:00
f71b6ab629 install: clarify Hekate RAM menu copy (4GB/8GB labels and warnings)
All checks were successful
Build / Build (push) Successful in 16s
Made-with: Cursor
2026-04-09 20:48:50 +02:00
9b559eb7e6 clean: delete TegraExplorer repair .te scripts on clean install
All checks were successful
Build / Build (push) Successful in 16s
Made-with: Cursor
2026-04-07 23:54:54 +02:00
d3735d17ee install: RAM config ini, selection menu, and silent Hekate 8GB step
- Read sd:/config/omninx/ram_config.ini [Ram] 8gb=0|1 when present;
  otherwise show 4 GB / 8 GB menu, write ini, same input as variant menu.
- Replace fuse-based DRAM autodetect for hekate_8gb.bin handling.
- Reprint installer top bar after RAM menu confirm.

Made-with: Cursor
2026-04-07 23:54:53 +02:00
db7d83304f main: add installer_launch_hekate_payload (update.bin or reboot)
Made-with: Cursor
2026-04-07 23:54:51 +02:00
113542f7fb install: declare installer_launch_hekate_payload for sub-menu exit
Made-with: Cursor
2026-04-07 23:54:50 +02:00
72a2083d76 Updated Deletion list
All checks were successful
Build / Build (push) Successful in 16s
2026-04-05 15:38:32 +02:00
3f6bbc0752 Input: console Vol+/- combo only before install; Joy-Con +/- exit removed
All checks were successful
Build / Build (push) Successful in 16s
- cancel_combo_pressed uses only hardware volume buttons
- After installation summary and payload-missing screen: A or Power only

Made-with: Cursor
2026-03-31 13:18:06 +02:00
d1fc24dae8 Install: Hekate 8GB after copy (fuse >4GiB), dram_fuse helper, RAM test target
All checks were successful
Build / Build (push) Successful in 14s
- Add dram_fuse.c/h for shared fuse-to-MiB mapping
- After successful update/clean install copy: if hekate_8gb.bin exists and
  fuse reports more than 4 GiB, copy to sd:/payload.bin and
  sd:/bootloader/update.bin, then remove source; otherwise remove helper only
- Makefile: make ram-test builds output/RAM-Test.bin (tools/ram_test_main.c)

Made-with: Cursor
2026-03-31 00:05:30 +02:00
ef104bce41 Update deletions: keep Switchfin, remove swr-ini-tool nro
Made-with: Cursor
2026-03-30 20:27:39 +02:00
0015c7e8ac Install: copy all staging root files via directory iteration
All checks were successful
Build / Build (push) Successful in 18s
Made-with: Cursor
2026-03-30 17:49:52 +02:00
6dde73f39e Install: copy themes/ in stage 2 (update and clean)
All checks were successful
Build / Build (push) Successful in 11s
Made-with: Cursor
2026-03-06 23:46:40 +01:00
97e8c3d965 Remove outdated UltraHand comment from deletion_lists_update.h
All checks were successful
Build / Build (push) Successful in 13s
Made-with: Cursor
2026-03-06 22:10:32 +01:00
586afc2f56 Revert "Update: preserve UltraHand .offload state (overlays/packages)"
This reverts commit e71fb13bfc.
2026-03-05 17:24:37 +01:00
f943887b39 Revert "Clean install: use normal switch copy, not .offload-aware"
This reverts commit 335ea03b05.
2026-03-05 17:24:26 +01:00
335ea03b05 Clean install: use normal switch copy, not .offload-aware
All checks were successful
Build / Build (push) Successful in 14s
- update_mode_install(variant, offload_aware_switch): true = update (preserve .offload), false = clean
- Update path calls with true; clean_mode_install calls with false so switch/ is copied normally on fresh install

Made-with: Cursor
2026-03-01 15:15:45 +01:00
b0523ada6c nx_sd: add sd_get_card_mounted() for screenshot and other callers
Made-with: Cursor
2026-03-01 14:44:37 +01:00
9a2307a8ee Add screenshot capture via Joy-Con Capture button
- source/screenshot.c, screenshot.h: take_screenshot() saves framebuffer as BMP
- Path: sd:/switch/screenshot/omninx_installer_YYYYMMDD_HHMMSS.bmp (RTC timestamp)
- 3s cooldown, backlight flash on capture; TegraExplorer-style BGR flip
- main.c: call take_screenshot() when jc->cap in all joycon poll loops

Made-with: Cursor
2026-03-01 14:43:58 +01:00
e71fb13bfc Update: preserve UltraHand .offload state (overlays/packages)
All checks were successful
Build / Build (push) Successful in 10s
- Add folder_copy_switch_update_offload_aware() for update-only switch copy
- If item exists in .overlays/.offload or .packages/.offload, update in place; else copy to main
- Remove sd:/switch/.overlays from update delete list so .offload survives
- Fresh install unchanged (no offload logic)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 21:01:36 +01:00
5bcd3987a2 UHS warning: recommend Samsung EVO Plus, EVO Select, PRO Plus
All checks were successful
Build / Build (push) Successful in 11s
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 20:28:15 +01:00
15b7cb1f4c Add battery run protection: require charger below 10%
All checks were successful
Build / Build (push) Successful in 10s
- Add bq24193_charger_connected() for charger detection
- Block install when battery < 10% and charger not plugged in
- Show clear screen after charger detected before continuing
- User can cancel with + and - to return to Hekate

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:12:06 +01:00
4b124a9998 Add + and - simultaneously to cancel and return to Hekate before install
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 15:03:58 +01:00
89279e27d5 Updated CI
All checks were successful
Build / Build (push) Successful in 10s
2026-02-13 22:13:29 +01:00
4ae286d21a Updated CI
All checks were successful
Build / Build (push) Successful in 11s
2026-02-13 22:11:42 +01:00
880a9af105 Updated CI
Some checks failed
Build / build (push) Failing after 3s
2026-02-13 22:09:31 +01:00
7fac0e50bb Updated CI
Some checks failed
Build / build (push) Failing after 3s
2026-02-13 22:08:40 +01:00
2c32f19262 Removed .packages from Update to be deleted 2026-02-11 17:58:32 +01:00
4effcf513b Updated Notice 2026-02-11 17:56:31 +01:00
6b04eb5d89 Fixed typo 2026-02-11 17:47:00 +01:00
ed93c32131 Updated Readme
- Added notice for proper use of this payload\n- Fixed some explenations
2026-02-11 17:17:37 +01:00
bae5ecebf1 Updated CI
All checks were successful
Build and Release / build-and-release (push) Successful in 10s
2026-02-11 17:11:18 +01:00
ef16df416b Updated CI
All checks were successful
Build and Release / build-and-release (push) Successful in 9s
2026-02-11 17:09:04 +01:00
f81cd8e381 Updated CI
Some checks failed
Build and Release / build-and-release (push) Failing after 13s
2026-02-11 17:07:40 +01:00
04362c6e0c Added CI 2026-02-11 17:06:19 +01:00
8457cdd5a6 Keeping UltraHand packages on Update 2026-02-11 17:06:09 +01:00
d1f78968a4 Added wait warning 2026-02-11 16:50:22 +01:00
b2c245d6ab removed .vscode folder 2026-02-11 16:21:21 +01:00
50618d4607 Changed logging and credits 2026-02-11 16:20:56 +01:00
Niklas080208
4d43ddf1e5 Cleanup UX: slim logging, DBI keep config, no manifest overwrite
- Slim cleanup: one progress line per section (Bereinige: X [p%] (n/n))
  via delete_path_lists_grouped() in clean and update mode
- DBI: remove folder entries from deletion lists; only .nro deleted,
  folder and config preserved
- Remove redundant manifest.ini creation; use manifest from pack config/

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 00:00:22 +01:00
Niklas080208
4a4147686e Added detection if multiple packs found 2026-02-03 22:15:17 +01:00
Niklas080208
fdad061616 Capitalized Variants 2026-02-03 21:22:37 +01:00
Niklas080208
3d3489f1e6 Removed HATS leftovers 2026-02-03 20:40:39 +01:00
30 changed files with 3529 additions and 2374 deletions

32
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Build
on:
push:
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
container: devkitpro/devkitarm:latest
steps:
- name: Install dependencies
run: apt-get update && apt-get install -y nodejs
- name: Checkout latest code
uses: actions/checkout@v5
with:
clean: true
- name: Build
run: |
export DEVKITPRO=/opt/devkitpro
export DEVKITARM=/opt/devkitpro/devkitARM
make -j4
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: OmniNX-Installer.bin
path: output/OmniNX-Installer.bin

3
.gitignore vendored
View File

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

17
.vscode/settings.json vendored
View File

@@ -1,17 +0,0 @@
{
"claudeCode.disableLoginPrompt": false,
"claudeCode.environmentVariables": [
{
"name": "ANTHROPIC_BASE_URL",
"value": "https://api.z.ai/api/anthropic"
},
{
"name": "ANTHROPIC_AUTH_TOKEN",
"value": "caceb12fd1a740c68631842764f1b38a.kBX5z6hfW95z8QHj"
},
{
"name": "CLAUDE_CODE_SKIP_AUTH_LOGIN",
"value": "true"
}
]
}

90
DEBUG_INI.md Normal file
View File

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

View File

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

202
Makefile
View File

@@ -1,90 +1,112 @@
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/base_rules
################################################################################
IPL_LOAD_ADDR := 0x40008000
VERSION := $(shell cat VERSION)
################################################################################
TARGET := omninx-installer
OUTPUT_NAME := OmniNX-Installer.bin
BUILDDIR := build
OUTPUTDIR := output
SOURCEDIR := source
BDKDIR := bdk
BDKINC := -I./$(BDKDIR)
VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/))
VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/))
# All source files
OBJS = $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(SOURCEDIR), *.S *.c)))
OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(BDKDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(BDKDIR), *.S *.c)))
GFX_INC := '"../$(SOURCEDIR)/gfx.h"'
FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"'
################################################################################
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
CUSTOMDEFINES += -DVERSION='"$(VERSION)"'
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork
CFLAGS = $(ARCH) -Os -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11 -Wall -Wno-missing-braces $(CUSTOMDEFINES)
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections -Xlinker --defsym=IPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
################################################################################
.PHONY: all clean release
all: $(OUTPUTDIR)/$(OUTPUT_NAME)
$(eval BIN_SIZE = $(shell wc -c < $(OUTPUTDIR)/$(OUTPUT_NAME)))
@echo "Payload size is $(BIN_SIZE) bytes"
@echo "Max size is 126296 bytes."
clean:
@rm -rf $(BUILDDIR)
@rm -rf $(OUTPUTDIR)
@rm -rf release
@rm -f $(TARGET)-*.zip
$(OUTPUTDIR)/$(OUTPUT_NAME): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
@mkdir -p "$(@D)"
$(OBJCOPY) -S -O binary $< $(OUTPUTDIR)/$(TARGET).bin
@mv $(OUTPUTDIR)/$(TARGET).bin $(OUTPUTDIR)/$(OUTPUT_NAME)
$(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS)
$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.c
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.S
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.c
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.S
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) -c $< -o $@
release: $(OUTPUTDIR)/$(OUTPUT_NAME)
@mkdir -p release/bootloader/payloads
@cp $(OUTPUTDIR)/$(OUTPUT_NAME) release/bootloader/payloads/$(OUTPUT_NAME)
@cd release && zip -r ../$(TARGET)-$(VERSION).zip bootloader
@echo "Release package created: $(TARGET)-$(VERSION).zip"
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/base_rules
################################################################################
IPL_LOAD_ADDR := 0x40008000
VERSION := $(shell cat VERSION)
################################################################################
TARGET := omninx-installer
OUTPUT_NAME := OmniNX-Installer.bin
BUILDDIR := build
OUTPUTDIR := output
SOURCEDIR := source
BDKDIR := bdk
BDKINC := -I./$(BDKDIR)
VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/))
VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/))
# All source files
OBJS = $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(SOURCEDIR), *.S *.c)))
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"'
################################################################################
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
CUSTOMDEFINES += -DVERSION='"$(VERSION)"'
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork
CFLAGS = $(ARCH) -Os -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11 -Wall -Wno-missing-braces $(CUSTOMDEFINES)
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections -Xlinker --defsym=IPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
################################################################################
RAMTEST_BIN := $(OUTPUTDIR)/RAM-Test.bin
OBJS_RAMTEST := $(filter-out $(BUILDDIR)/$(TARGET)/main.o,$(OBJS)) \
$(BUILDDIR)/$(TARGET)/ram_test_main.o
.PHONY: all clean release ram-test
all: $(OUTPUTDIR)/$(OUTPUT_NAME)
$(eval BIN_SIZE = $(shell wc -c < $(OUTPUTDIR)/$(OUTPUT_NAME)))
@echo "Payload size is $(BIN_SIZE) bytes"
@echo "Max size is 126296 bytes."
clean:
@rm -rf $(BUILDDIR)
@rm -rf $(OUTPUTDIR)
@rm -f $(RAMTEST_BIN)
@rm -rf release
@rm -f $(TARGET)-*.zip
$(OUTPUTDIR)/$(OUTPUT_NAME): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
@mkdir -p "$(@D)"
$(OBJCOPY) -S -O binary $< $(OUTPUTDIR)/$(TARGET).bin
@mv $(OUTPUTDIR)/$(TARGET).bin $(OUTPUTDIR)/$(OUTPUT_NAME)
$(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS)
$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(BUILDDIR)/$(TARGET)/ram_test_main.o: tools/ram_test_main.c
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/ram_test.elf: $(OBJS_RAMTEST)
$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(RAMTEST_BIN): $(BUILDDIR)/$(TARGET)/ram_test.elf
@mkdir -p "$(@D)"
$(OBJCOPY) -S -O binary $< $@
@echo "RAM-Test payload: $(RAMTEST_BIN) ($$(wc -c < $@) bytes)"
ram-test: $(RAMTEST_BIN)
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.c
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.S
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.c
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.S
@mkdir -p "$(@D)"
$(CC) $(CFLAGS) -c $< -o $@
release: $(OUTPUTDIR)/$(OUTPUT_NAME)
@mkdir -p release/bootloader/payloads
@cp $(OUTPUTDIR)/$(OUTPUT_NAME) release/bootloader/payloads/$(OUTPUT_NAME)
@cd release && zip -r ../$(TARGET)-$(VERSION).zip bootloader
@echo "Release package created: $(TARGET)-$(VERSION).zip"

320
README.md
View File

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

View File

@@ -164,6 +164,12 @@ void bq24193_enable_charger()
i2c_send_byte(I2C_1, BQ24193_I2C_ADDR, BQ24193_PORConfig, reg);
}
bool bq24193_charger_connected()
{
u8 status = bq24193_get_reg(BQ24193_Status);
return (status & BQ24193_STATUS_PG_MASK) != 0;
}
void bq24193_fake_battery_removal()
{
// Disable watchdog to keep BATFET disabled.

View File

@@ -19,6 +19,8 @@
#ifndef __BQ24193_H_
#define __BQ24193_H_
#include <utils/types.h>
#define BQ24193_I2C_ADDR 0x6B
// REG 0 masks.
@@ -117,5 +119,6 @@ enum BQ24193_reg_prop {
int bq24193_get_property(enum BQ24193_reg_prop prop, int *value);
void bq24193_enable_charger();
void bq24193_fake_battery_removal();
bool bq24193_charger_connected();
#endif /* __BQ24193_H_ */

View File

@@ -1,13 +0,0 @@
# HATS Tools Universal Config
# Location: sd:/config/hats-tools/config.ini
# Shared between HATS Tools GUI and hats-installer payload
[hats]
install_mode=default ; options: replace, default, clean
# Mode descriptions:
# replace - Only replace files, no deletion (old files may remain)
# default - Delete /atmosphere only, keep /bootloader and /switch
# clean - Delete /atmosphere, /bootloader, and /switch (fresh install)
#
# All modes will remove HATS-*.txt version files from SD root

View File

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

View File

@@ -7,8 +7,17 @@
#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"
// Backup user data (DBI, Tinfoil, prod.keys) before clean install
/** Live HorizonOC settings (Overclock tool). */
#define HORIZON_OC_CONFIG_PATH "sd:/config/horizon-oc/config.ini"
/** Scratch dir during OC-variant update only; removed after successful restore. */
#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 dbi.config, prod.keys) before clean install
int backup_user_data(void);
// Restore user data after clean install
@@ -16,3 +25,10 @@ int restore_user_data(void);
// Clean up temporary backup directory
int cleanup_backup(void);
// HorizonOC: backup config.ini before OC pack update (update mode only); no-op if missing
int backup_horizon_oc_config_for_oc_update(void);
// Restore after file copy; no-op if no backup from this run
int restore_horizon_oc_config_after_oc_update(void);
// Delete sd:/.omninx_oc_update after successful restore
int cleanup_horizon_oc_update_backup(void);

View File

@@ -1,298 +1,132 @@
/*
* 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.
*/
#pragma once
// Atmosphere subdirectories to delete
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",
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
};
// Bootloader directories to delete
static const char* clean_bootloader_dirs_to_delete[] = {
"sd:/bootloader/boot",
"sd:/bootloader/bootlogo",
"sd:/bootloader/ini2",
"sd:/bootloader/payloads",
"sd:/bootloader/reboot",
"sd:/bootloader/res",
"sd:/bootloader/sys",
NULL
};
// Bootloader files to delete
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",
NULL
};
// Config directories to delete
static const char* clean_config_dirs_to_delete[] = {
"sd:/config/aio-switch-updater",
"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",
"sd:/config/ultrahand",
NULL
};
// Switch directories to delete
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/DBI_658_EN",
"sd:/switch/DBI_810",
"sd:/switch/DBI_810_DE",
"sd:/switch/DBI_810_EN",
"sd:/switch/DBI_RU",
"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",
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_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/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
};
// Root CFW files to delete
static const char* clean_root_files_to_delete[] = {
"sd:/boot.dat",
"sd:/boot.ini",
"sd:/exosphere.bin",
"sd:/exosphere.ini",
"sd:/hbmenu.nro",
"sd:/install.bat",
"sd:/license",
"sd:/loader.bin",
"sd:/mc-mitm.log",
"sd:/payload.bin",
"sd:/update.bin",
"sd:/version",
NULL
};
// Miscellaneous directories to delete
static const char* clean_misc_dirs_to_delete[] = {
"sd:/argon",
"sd:/games",
"sd:/NSPs (Tools)",
"sd:/Patched Apps",
"sd:/SaltySD/flags",
"sd:/scripts",
"sd:/switch/tinfoil/db",
"sd:/tools",
"sd:/warmboot_mariko",
NULL
};
// Miscellaneous files to delete
static const char* clean_misc_files_to_delete[] = {
"sd:/fusee-primary.bin",
"sd:/fusee.bin",
"sd:/SaltySD/exceptions.txt",
"sd:/SaltySD/saltysd_bootstrap.elf",
"sd:/SaltySD/saltysd_bootstrap32_3k.elf",
"sd:/SaltySD/saltysd_bootstrap32_5k.elf",
"sd:/SaltySD/saltysd_core.elf",
"sd:/SaltySD/saltysd_core32.elf",
NULL
};
// Old version marker files to delete (clean install only)
static const char* old_version_files_to_delete[] = {
"sd:/1.0.0l",
"sd:/1.0.0s",
"sd:/1.0.0oc",
"sd:/1.4.0-pre",
"sd:/1.4.0-pre-c",
"sd:/1.4.0-pre-d",
"sd:/1.4.1",
"sd:/1.5.0",
NULL
};
/*
* OmniNX Installer - Deletion Lists for Clean Install Mode
* Deletion policy aligned with NiklasCFW pack clean install (TegraExplorer script):
* full sd:/atmosphere, bootloader/config subsets, sd:/switch, root + misc, then
* post-restore paths (e.g. tinfoil db). DBI/prod.keys backup is in backup.c.
*/
#pragma once
// Entire atmosphere/ tree (NiklasCFW: deldir("sd:/atmosphere"))
static const char* clean_atmosphere_dirs_to_delete[] = {
"sd:/atmosphere",
NULL
};
static const char* clean_atmosphere_contents_dirs_to_delete[] = {
NULL
};
static const char* clean_atmosphere_files_to_delete[] = {
NULL
};
// Bootloader directories to delete
static const char* clean_bootloader_dirs_to_delete[] = {
"sd:/bootloader/boot",
"sd:/bootloader/bootlogo",
"sd:/bootloader/ini2",
"sd:/bootloader/payloads",
"sd:/bootloader/reboot",
"sd:/bootloader/res",
"sd:/bootloader/sys",
NULL
};
// Bootloader files to delete
static const char* clean_bootloader_files_to_delete[] = {
"sd:/bootloader/ArgonNX.bin",
"sd:/bootloader/bootlogo.bmp",
"sd:/bootloader/hekate_ipl.ini",
"sd:/bootloader/patches.ini",
"sd:/bootloader/update.bin",
"sd:/bootloader/ini/EmuMMC ohne Mods.ini",
NULL
};
// Config directories to delete
static const char* clean_config_dirs_to_delete[] = {
"sd:/config/aio-switch-updater",
"sd:/config/blue_pack_updater",
"sd:/config/kefir-updater",
"sd:/config/nx-hbmenu",
"sd:/config/sys-con",
"sd:/config/sys-patch",
"sd:/config/uberhand",
"sd:/config/ultrahand",
NULL
};
// Entire switch/ tree (clean install recreates sd:/switch after wipe)
static const char* clean_switch_dirs_to_delete[] = {
"sd:/switch",
NULL
};
static const char* clean_switch_files_to_delete[] = {
NULL
};
// Root CFW files to delete
static const char* clean_root_files_to_delete[] = {
"sd:/boot.dat",
"sd:/boot.ini",
"sd:/exosphere.bin",
"sd:/exosphere.ini",
"sd:/hbmenu.nro",
"sd:/install.bat",
"sd:/license",
"sd:/loader.bin",
"sd:/mc-mitm.log",
"sd:/payload.bin",
"sd:/update.bin",
"sd:/version",
NULL
};
// Miscellaneous directories to delete
static const char* clean_misc_dirs_to_delete[] = {
"sd:/argon",
"sd:/games",
"sd:/NSPs (Tools)",
"sd:/Patched Apps",
"sd:/SaltySD/flags",
"sd:/SaltySD/patches",
"sd:/scripts",
"sd:/TegraExplorer",
"sd:/themes/systemData",
"sd:/tools",
"sd:/warmboot_mariko",
NULL
};
// Miscellaneous files to delete
static const char* clean_misc_files_to_delete[] = {
"sd:/fusee-primary.bin",
"sd:/fusee.bin",
"sd:/SaltySD/exceptions.txt",
"sd:/SaltySD/saltysd_bootstrap.elf",
"sd:/SaltySD/saltysd_bootstrap32_3k.elf",
"sd:/SaltySD/saltysd_bootstrap32_5k.elf",
"sd:/SaltySD/saltysd_core.elf",
"sd:/SaltySD/saltysd_core32.elf",
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",
"sd:/1.4.0-pre-c",
"sd:/1.4.0-pre-d",
"sd:/1.4.1",
"sd:/1.5.0",
"sd:/1.6.0",
"sd:/1.6.1",
NULL
};

View File

@@ -1,284 +1,283 @@
/*
* OmniNX Installer - Deletion Lists for Update Mode
* Selective deletion when OmniNX is already installed.
*/
#pragma once
// Atmosphere subdirectories to delete
static const char* 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",
NULL
};
// Atmosphere contents directories (title IDs; only under atmosphere/contents/)
static const char* 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* 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
};
// Bootloader directories to delete
static const char* bootloader_dirs_to_delete[] = {
"sd:/bootloader/boot",
"sd:/bootloader/bootlogo",
"sd:/bootloader/ini2",
"sd:/bootloader/payloads",
"sd:/bootloader/reboot",
"sd:/bootloader/res",
"sd:/bootloader/sys",
NULL
};
// Bootloader files to delete
static const char* 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",
NULL
};
// Config directories to delete
static const char* config_dirs_to_delete[] = {
"sd:/config/aio-switch-updater",
"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",
"sd:/config/ultrahand",
NULL
};
// Switch directories to delete
static const char* 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/DBI_658_EN",
"sd:/switch/DBI_810",
"sd:/switch/DBI_810_DE",
"sd:/switch/DBI_810_EN",
"sd:/switch/DBI_RU",
"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",
NULL
};
// Switch files (NRO) to delete
static const char* 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_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/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
};
// Root CFW files to delete
static const char* root_files_to_delete[] = {
"sd:/boot.dat",
"sd:/boot.ini",
"sd:/exosphere.bin",
"sd:/exosphere.ini",
"sd:/hbmenu.nro",
"sd:/install.bat",
"sd:/license",
"sd:/loader.bin",
"sd:/mc-mitm.log",
"sd:/payload.bin",
"sd:/update.bin",
"sd:/version",
NULL
};
// Miscellaneous directories to delete
static const char* misc_dirs_to_delete[] = {
"sd:/argon",
"sd:/games",
"sd:/NSPs (Tools)",
"sd:/Patched Apps",
"sd:/SaltySD/flags",
"sd:/scripts",
"sd:/switch/tinfoil/db",
"sd:/tools",
"sd:/warmboot_mariko",
NULL
};
// Miscellaneous files to delete
static const char* misc_files_to_delete[] = {
"sd:/fusee-primary.bin",
"sd:/fusee.bin",
"sd:/SaltySD/exceptions.txt",
"sd:/SaltySD/saltysd_bootstrap.elf",
"sd:/SaltySD/saltysd_bootstrap32_3k.elf",
"sd:/SaltySD/saltysd_bootstrap32_5k.elf",
"sd:/SaltySD/saltysd_core.elf",
"sd:/SaltySD/saltysd_core32.elf",
NULL
};
/*
* OmniNX Installer - Deletion Lists for Update Mode
* Selective deletion when OmniNX is already installed.
*/
#pragma once
// Atmosphere subdirectories to delete
static const char* 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",
NULL
};
// Atmosphere contents directories (title IDs; only under atmosphere/contents/)
static const char* 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* 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",
"sd:/atmosphere/kips/loader.kip",
"sd:/atmosphere/kips/hoc.kip",
NULL
};
// Bootloader directories to delete
static const char* bootloader_dirs_to_delete[] = {
"sd:/bootloader/boot",
"sd:/bootloader/bootlogo",
"sd:/bootloader/ini2",
"sd:/bootloader/payloads",
"sd:/bootloader/reboot",
"sd:/bootloader/res",
"sd:/bootloader/sys",
NULL
};
// Bootloader files to delete
static const char* 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",
NULL
};
// Config directories to delete
static const char* config_dirs_to_delete[] = {
"sd:/config/aio-switch-updater",
"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",
"sd:/config/ultrahand",
NULL
};
// Switch directories to delete
static const char* switch_dirs_to_delete[] = {
"sd:/switch/.packages",
"sd:/switch/.overlays",
"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/Sys-Clk Manager",
"sd:/switch/Sys-Con",
"sd:/switch/sys-clk-manager",
"sd:/switch/themezer-nx",
"sd:/switch/themezernx",
"sd:/switch/tinwoo",
NULL
};
// Switch files (NRO) to delete
static const char* 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_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/swr-ini-tool/swr-ini-tool.nro",
"sd:/switch/SwitchIdent.nro",
"sd:/switch/Switch_themes_Installer/NXThemesInstaller.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
};
// Root CFW files to delete
static const char* root_files_to_delete[] = {
"sd:/boot.dat",
"sd:/boot.ini",
"sd:/exosphere.bin",
"sd:/exosphere.ini",
"sd:/hbmenu.nro",
"sd:/install.bat",
"sd:/license",
"sd:/loader.bin",
"sd:/mc-mitm.log",
"sd:/payload.bin",
"sd:/update.bin",
"sd:/version",
NULL
};
// Miscellaneous directories to delete
static const char* misc_dirs_to_delete[] = {
"sd:/argon",
"sd:/games",
"sd:/NSPs (Tools)",
"sd:/Patched Apps",
"sd:/SaltySD/flags",
"sd:/scripts",
"sd:/switch/tinfoil/db",
"sd:/themes/systemData",
"sd:/tools",
"sd:/warmboot_mariko",
NULL
};
// Miscellaneous files to delete
static const char* misc_files_to_delete[] = {
"sd:/fusee-primary.bin",
"sd:/fusee.bin",
"sd:/SaltySD/exceptions.txt",
"sd:/SaltySD/saltysd_bootstrap.elf",
"sd:/SaltySD/saltysd_bootstrap32_3k.elf",
"sd:/SaltySD/saltysd_bootstrap32_5k.elf",
"sd:/SaltySD/saltysd_core.elf",
"sd:/SaltySD/saltysd_core32.elf",
NULL
};

46
source/dram_fuse.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* DRAM capacity from fuse (SKU), not physical probe.
*/
#include "dram_fuse.h"
#include <soc/fuse.h>
#include <soc/hw_init.h>
#include <soc/t210.h>
static int mariko_dram_mib(u32 dram_id)
{
switch (dram_id) {
case 9:
case 13:
case 18:
case 21:
case 23:
case 28:
return 8192;
default:
if (dram_id >= 3 && dram_id <= 28)
return 4096;
return -1;
}
}
static int erista_dram_mib(u32 dram_id)
{
if (dram_id == 4)
return 6144;
if (dram_id <= 6)
return 4096;
return -1;
}
int dram_capacity_mib_from_fuse(void)
{
u32 nid = fuse_read_dramid(false);
u32 chip = hw_get_chip_id();
if (chip == GP_HIDREV_MAJOR_T210)
return erista_dram_mib(nid);
if (chip == GP_HIDREV_MAJOR_T210B01)
return mariko_dram_mib(nid);
return -1;
}

6
source/dram_fuse.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <utils/types.h>
/* DRAM capacity in MiB from fuse DRAM ID + SoC (same table as RAM test payload). */
int dram_capacity_mib_from_fuse(void);

View File

@@ -1,350 +1,351 @@
/*
* HATS Installer - Filesystem operations with file logging
*/
#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("=== HATS 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,19 +1,20 @@
/*
* HATS Installer - Filesystem operations
*/
#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

@@ -3,12 +3,23 @@
*/
#include "install.h"
#include "backup.h"
#include "fs.h"
#include "screenshot.h"
#include "version.h"
#include "gfx.h"
#include <libs/fatfs/ff.h>
#include <input/joycon.h>
#include <mem/heap.h>
#include <stdarg.h>
#include <string.h>
#include <utils/btn.h>
#include <utils/ini.h>
#include <utils/list.h>
#include <utils/sprintf.h>
#include <utils/util.h>
#define GROUPED_DELETE_MAX_ENTRIES 512
#ifndef VERSION
#define VERSION "1.0.0"
@@ -29,10 +40,25 @@
#define COLOR_ORANGE 0xFF00A5FF
#define COLOR_RED 0xFFFF0000
static inline bool cancel_combo_pressed(u8 btn)
{
return (btn & (BTN_VOL_UP | BTN_VOL_DOWN)) == (BTN_VOL_UP | BTN_VOL_DOWN);
}
void install_set_color(u32 color) {
gfx_con_setcol(color, gfx_con.fillbg, gfx_con.bgcol);
}
/* Same top bar as main.c print_header() (without full screen clear). */
static void install_print_main_header(void)
{
install_set_color(COLOR_CYAN);
gfx_printf("===================================\n");
gfx_printf(" OmniNX Installer Payload v%s\n", VERSION);
gfx_printf("===================================\n\n");
install_set_color(COLOR_WHITE);
}
// Check if we need to clear screen (when getting close to bottom)
void install_check_and_clear_screen_if_needed(void) {
// In the gfx system:
@@ -47,13 +73,7 @@ void install_check_and_clear_screen_if_needed(void) {
// Clear screen and reset position
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
// Reprint same header as initial launch
install_set_color(COLOR_CYAN);
gfx_printf("===================================\n");
gfx_printf(" OmniNX Installer Payload v%s\n", VERSION);
gfx_printf("===================================\n\n");
install_set_color(COLOR_WHITE);
install_print_main_header();
}
}
@@ -110,6 +130,38 @@ void install_combine_path(char *result, size_t size, const char *base, const cha
}
}
/* Copy every regular file at staging root to dst_root; subdirs and volume labels skipped. */
int install_copy_staging_root_files(const char *staging, const char *dst_root) {
DIR dir;
FILINFO fno;
char src_full[256];
char dst_full[256];
int res = f_opendir(&dir, staging);
if (res != FR_OK)
return res;
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0)
break;
if (fno.fname[0] == '.' && (fno.fname[1] == '\0' || (fno.fname[1] == '.' && fno.fname[2] == '\0')))
continue;
if (fno.fattrib & (AM_DIR | AM_VOL))
continue;
install_combine_path(src_full, sizeof(src_full), staging, fno.fname);
install_combine_path(dst_full, sizeof(dst_full), dst_root, fno.fname);
res = file_copy(src_full, dst_full);
if (res != FR_OK) {
f_closedir(&dir);
return res;
}
}
f_closedir(&dir);
return FR_OK;
}
// Recursive folder copy with progress tracking
static int folder_copy_progress_recursive(const char *src, const char *dst, int *copied, int total, u32 start_x, u32 start_y, const char *display_name, int *last_percent) {
DIR dir;
@@ -395,6 +447,89 @@ int folder_delete_single_with_progress(const char *path, const char *display_nam
}
// Delete a list of paths with progress tracking
// Delete multiple path lists under one label; one progress line "Bereinige: folder/ [p%] (d/t)".
// Varargs: path lists (const char**), terminated by NULL.
int delete_path_lists_grouped(const char *folder_display_name, ...) {
va_list ap;
const char *entries[GROUPED_DELETE_MAX_ENTRIES];
int n_entries = 0;
int deleted = 0;
int failed = 0;
u32 start_x, start_y;
int last_percent = -1;
int res;
va_start(ap, 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]))
entries[n_entries++] = list[i];
}
}
va_end(ap);
if (n_entries == 0)
return FR_OK;
int total_paths = n_entries;
gfx_con_getpos(&start_x, &start_y);
install_set_color(COLOR_CYAN);
gfx_printf(" Bereinige: %s [ 0%%] (0/%d)", folder_display_name, total_paths);
install_set_color(COLOR_WHITE);
for (int i = 0; i < n_entries; i++) {
const char *path = entries[i];
FILINFO fno;
if (f_stat(path, &fno) != FR_OK)
continue;
const char *item_name = strrchr(path, '/');
if (item_name) item_name++;
else item_name = path;
if (fno.fattrib & AM_DIR) {
int item_count = install_count_directory_items(path);
if (item_count > 50) {
gfx_printf("\n");
res = folder_delete_single_with_progress(path, item_name);
} else {
res = folder_delete(path);
}
} else {
if (fno.fattrib & AM_RDO)
f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO);
res = f_unlink(path);
}
if (res == FR_OK || res == FR_NO_FILE)
deleted++;
else
failed++;
int percent = (deleted * 100) / total_paths;
if (percent != last_percent || deleted % 5 == 0) {
gfx_con_setpos(start_x, start_y);
install_set_color(COLOR_CYAN);
gfx_printf(" Bereinige: %s [%3d%%] (%d/%d)", folder_display_name, percent, deleted, total_paths);
install_set_color(COLOR_WHITE);
last_percent = percent;
}
}
gfx_con_setpos(start_x, start_y);
if (failed == 0) {
install_set_color(COLOR_GREEN);
gfx_printf(" Bereinige: %s [100%%] (%d/%d) - Fertig!\n", folder_display_name, deleted, total_paths);
install_set_color(COLOR_WHITE);
} else {
install_set_color(COLOR_ORANGE);
gfx_printf(" Bereinige: %s [%3d%%] (%d/%d) - %d Fehler\n", folder_display_name, last_percent, deleted, total_paths, failed);
install_set_color(COLOR_WHITE);
}
return (failed == 0) ? FR_OK : FR_DISK_ERR;
}
int delete_path_list(const char* paths[], const char* description) {
int res;
int deleted = 0;
@@ -500,65 +635,517 @@ 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"
enum { RAM_READ_OK = 0, RAM_READ_NEED_MENU = 1 };
static void unlink_ignore_err(const char *path)
{
FILINFO fno;
if (f_stat(path, &fno) != FR_OK)
return;
if (fno.fattrib & AM_RDO)
f_chmod(path, fno.fattrib & ~AM_RDO, AM_RDO);
f_unlink(path);
}
static void ram_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)
{
LIST_FOREACH_ENTRY(ini_sec_t, sec, sections, link) {
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
if (kv->key) free(kv->key);
if (kv->val) free(kv->val);
}
if (sec->name) free(sec->name);
}
}
/* Valid [Ram] 8gb=0|1 → silent; missing file or invalid → menu. */
static int read_ram_config_silent(const char *path, bool *use_8gb)
{
if (!install_path_exists(path))
return RAM_READ_NEED_MENU;
link_t sections;
list_init(&sections);
if (ini_parse(&sections, (char *)path, false) != 1) {
ram_config_free_sections(&sections);
return RAM_READ_NEED_MENU;
}
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;
}
break;
}
}
break;
}
}
ram_config_free_sections(&sections);
if (!found)
return RAM_READ_NEED_MENU;
*use_8gb = eight;
return RAM_READ_OK;
}
/* Same navigation as main.c multi-variant menu; returns false if user aborts (payload/reboot). */
static bool install_ram_config_menu(bool *use_8gb)
{
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] = {
"4GB Konsole (Standard)",
"8GB Konsole",
};
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
install_print_main_header();
install_set_color(COLOR_YELLOW);
gfx_printf("Hekate Payload Auswahl:\n\n");
install_set_color(COLOR_WHITE);
gfx_printf("Wenn du einen 8GB RAM-Umbau hast, waehle 8GB Konsole aus.\n");
gfx_printf("Alle anderen waehlen 4GB Konsole aus.\n\n");
install_set_color(COLOR_RED);
gfx_printf("Warnung: Solltest du keinen RAM-Umbau haben, waehle auf keinen\n");
gfx_printf("Fall 8GB aus, da du sonst Boot Probleme bekommst!!!\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_8gb = (selected == 1);
ram_config_write(*use_8gb);
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
install_print_main_header();
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)
{
if (!install_path_exists(HEKATE_8GB_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;
}
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);
}
}
#define INSTALL_DEBUG_INI "sd:/config/omninx/debug.ini"
typedef struct {
bool active;
bool skip_clean_backup;
bool skip_clean_wipe;
bool skip_clean_restore;
bool skip_clean_install;
bool skip_clean_staging_cleanup;
bool skip_update_cleanup;
bool skip_update_install;
bool skip_update_staging_cleanup;
bool skip_update_horizon_oc;
bool skip_hekate_8gb_post_copy;
} install_debug_opts_t;
static bool install_ini_truth(const char *val)
{
if (!val || !val[0])
return false;
char c = val[0];
return c == '1' || c == 't' || c == 'T' || c == 'y' || c == 'Y';
}
static void install_debug_load(install_debug_opts_t *d)
{
memset(d, 0, sizeof(*d));
if (!install_path_exists(INSTALL_DEBUG_INI))
return;
link_t sections;
list_init(&sections);
if (ini_parse(&sections, (char *)INSTALL_DEBUG_INI, false) != 1) {
ram_config_free_sections(&sections);
return;
}
LIST_FOREACH_ENTRY(ini_sec_t, sec, &sections, link) {
if (!sec->name || strcmp(sec->name, "Debug") != 0)
continue;
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
if (!kv->key || !kv->val)
continue;
if (strcmp(kv->key, "skip_clean_backup") == 0)
d->skip_clean_backup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_wipe") == 0)
d->skip_clean_wipe = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_restore") == 0)
d->skip_clean_restore = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_install") == 0)
d->skip_clean_install = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_clean_staging_cleanup") == 0)
d->skip_clean_staging_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_cleanup") == 0)
d->skip_update_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_install") == 0)
d->skip_update_install = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_staging_cleanup") == 0)
d->skip_update_staging_cleanup = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_update_horizon_oc") == 0)
d->skip_update_horizon_oc = install_ini_truth(kv->val);
else if (strcmp(kv->key, "skip_hekate_8gb_post_copy") == 0)
d->skip_hekate_8gb_post_copy = install_ini_truth(kv->val);
}
break;
}
ram_config_free_sections(&sections);
d->active = d->skip_clean_backup || d->skip_clean_wipe || d->skip_clean_restore
|| d->skip_clean_install || d->skip_clean_staging_cleanup || d->skip_update_cleanup
|| d->skip_update_install || d->skip_update_staging_cleanup || d->skip_update_horizon_oc
|| d->skip_hekate_8gb_post_copy;
}
static void install_debug_print_banner(const install_debug_opts_t *d)
{
if (!d->active)
return;
install_set_color(COLOR_RED);
gfx_printf("*** DEBUG-MODUS: %s ***\n", INSTALL_DEBUG_INI);
install_set_color(COLOR_ORANGE);
if (d->skip_clean_backup)
gfx_printf(" ueberspringe: Clean Schritt 1 (Backup)\n");
if (d->skip_clean_wipe)
gfx_printf(" ueberspringe: Clean Schritt 2 (Bereinigung)\n");
if (d->skip_clean_restore)
gfx_printf(" ueberspringe: Clean Schritt 3 (Restore)\n");
if (d->skip_clean_install)
gfx_printf(" ueberspringe: Clean Schritt 4 (Kopieren)\n");
if (d->skip_clean_staging_cleanup)
gfx_printf(" ueberspringe: Clean Staging-Aufraeumen\n");
if (d->skip_update_cleanup)
gfx_printf(" ueberspringe: Update Schritt 1 (Bereinigung)\n");
if (d->skip_update_install)
gfx_printf(" ueberspringe: Update Schritt 2 (Kopieren)\n");
if (d->skip_update_staging_cleanup)
gfx_printf(" ueberspringe: Update Staging-Aufraeumen\n");
if (d->skip_update_horizon_oc)
gfx_printf(" ueberspringe: HorizonOC Update (Backup/Restore)\n");
if (d->skip_hekate_8gb_post_copy)
gfx_printf(" ueberspringe: hekate 8GB RAM Auswahl / Post-Copy\n");
install_set_color(COLOR_WHITE);
gfx_printf("\n");
}
// Main installation function
int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
int res;
install_debug_opts_t dbg;
install_debug_load(&dbg);
install_debug_print_banner(&dbg);
if (mode == INSTALL_MODE_UPDATE) {
// 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;
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");
install_set_color(COLOR_WHITE);
res = backup_horizon_oc_config_for_oc_update();
if (res != FR_OK)
return res;
if (had_horizon_cfg) {
install_set_color(COLOR_GREEN);
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);
}
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;
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 (!dbg.skip_hekate_8gb_post_copy)
install_hekate_8gb_post_copy();
else {
install_set_color(COLOR_ORANGE);
gfx_printf("[DEBUG] hekate 8GB Post-Copy uebersprungen.\n");
install_set_color(COLOR_WHITE);
}
if (pack_variant == VARIANT_OC && !dbg.skip_update_horizon_oc) {
bool had_horizon_bak = install_path_exists(HORIZON_OC_UPDATE_BACKUP_INI);
install_check_and_clear_screen_if_needed();
install_set_color(COLOR_YELLOW);
gfx_printf("HorizonOC: Stelle config.ini wieder her...\n");
install_set_color(COLOR_WHITE);
res = restore_horizon_oc_config_after_oc_update();
if (res != FR_OK)
return res;
install_set_color(COLOR_GREEN);
if (had_horizon_bak)
gfx_printf(" [OK] Deine HorizonOC-Einstellungen wurden wiederhergestellt.\n");
else
gfx_printf(" [OK] Keine fruehere config.ini — Standard aus dem Paket bleibt.\n");
install_set_color(COLOR_WHITE);
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
res = cleanup_staging_directory(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;
install_check_and_clear_screen_if_needed();
// Remove staging directory
} else {
install_set_color(COLOR_ORANGE);
gfx_printf("[DEBUG] Schritt 4 (Kopieren) uebersprungen.\n");
install_set_color(COLOR_WHITE);
}
if (!dbg.skip_hekate_8gb_post_copy)
install_hekate_8gb_post_copy();
else {
install_set_color(COLOR_ORANGE);
gfx_printf("[DEBUG] hekate 8GB Post-Copy uebersprungen.\n");
install_set_color(COLOR_WHITE);
}
install_check_and_clear_screen_if_needed();
if (!dbg.skip_clean_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;
}

View File

@@ -13,13 +13,18 @@ typedef enum {
INSTALL_MODE_CLEAN // No OmniNX - selective deletion from clean list
} install_mode_t;
// Main installation function
// If a sub-menu aborts to Hekate / update.bin (does not return if launch succeeds)
void installer_launch_hekate_payload(void);
// 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)
int update_mode_cleanup(omninx_variant_t variant);
int update_mode_install(omninx_variant_t variant);
int cleanup_staging_directory(omninx_variant_t pack_variant);
// Remove other OmniNX staging directories (Standard/Light/OC) that exist, except the one just installed
int cleanup_other_staging_directories(omninx_variant_t installed_variant);
// Clean install operations (install_clean.c)
int clean_mode_backup(void);
@@ -33,7 +38,9 @@ void install_check_and_clear_screen_if_needed(void);
bool install_path_exists(const char *path);
int install_count_directory_items(const char *path);
void install_combine_path(char *result, size_t size, const char *base, const char *add);
int install_copy_staging_root_files(const char *staging, const char *dst_root);
int delete_path_list(const char* paths[], const char* description);
int delete_path_lists_grouped(const char *folder_display_name, ...);
int folder_delete_single_with_progress(const char *path, const char *display_name);
int folder_delete_progress_recursive(const char *path, int *deleted, int total, u32 start_x, u32 start_y, const char *display_name, int *last_percent);
int folder_copy_with_progress_v2(const char *src, const char *dst, const char *display_name);

View File

@@ -1,111 +1,166 @@
/*
* OmniNX Installer - Clean Install Mode
* Backup, selective cleanup from list, restore, then install when no OmniNX found.
* Does not wipe whole card; only paths in deletion_lists_clean.h are removed.
*/
#include "install.h"
#include "backup.h"
#include "deletion_lists_clean.h"
#include "fs.h"
#include "gfx.h"
#include <libs/fatfs/ff.h>
#undef COLOR_CYAN
#undef COLOR_WHITE
#undef COLOR_GREEN
#undef COLOR_RED
#define COLOR_CYAN 0xFF00FFFF
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_GREEN 0xFF00FF00
#define COLOR_RED 0xFFFF0000
#define set_color install_set_color
#define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed
#define path_exists install_path_exists
// Clean mode: Backup user data
int clean_mode_backup(void) {
set_color(COLOR_CYAN);
gfx_printf(" Sichere: DBI, Tinfoil, prod.keys\n");
set_color(COLOR_WHITE);
int res = backup_user_data();
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" [OK] Sicherung abgeschlossen\n");
set_color(COLOR_WHITE);
}
return res;
}
// Clean mode: Wipe using selective deletion list (does not wipe whole card)
int clean_mode_wipe(void) {
check_and_clear_screen_if_needed();
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: atmosphere/\n");
set_color(COLOR_WHITE);
delete_path_list(clean_atmosphere_dirs_to_delete, "atmosphere subdirs");
delete_path_list(clean_atmosphere_contents_dirs_to_delete, "atmosphere contents dirs");
delete_path_list(clean_atmosphere_files_to_delete, "atmosphere files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: bootloader/\n");
set_color(COLOR_WHITE);
delete_path_list(clean_bootloader_dirs_to_delete, "bootloader dirs");
delete_path_list(clean_bootloader_files_to_delete, "bootloader files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: config/\n");
set_color(COLOR_WHITE);
delete_path_list(clean_config_dirs_to_delete, "config dirs");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: switch/\n");
set_color(COLOR_WHITE);
delete_path_list(clean_switch_dirs_to_delete, "switch dirs");
delete_path_list(clean_switch_files_to_delete, "switch files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: Root-Dateien\n");
set_color(COLOR_WHITE);
delete_path_list(clean_root_files_to_delete, "root files");
delete_path_list(clean_misc_dirs_to_delete, "misc dirs");
delete_path_list(clean_misc_files_to_delete, "misc files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: Alte Versionsmarker\n");
set_color(COLOR_WHITE);
delete_path_list(old_version_files_to_delete, "old version markers");
set_color(COLOR_CYAN);
gfx_printf(" Erstelle: switch/\n");
set_color(COLOR_WHITE);
f_mkdir("sd:/switch");
set_color(COLOR_GREEN);
gfx_printf(" Bereinigung abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Clean mode: Restore user data
int clean_mode_restore(void) {
set_color(COLOR_CYAN);
gfx_printf(" Stelle wieder her: DBI, Tinfoil, 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);
}
cleanup_backup();
return res;
}
// Clean mode: Install files (reuse update mode install)
int clean_mode_install(omninx_variant_t variant) {
return update_mode_install(variant);
}
/*
* OmniNX Installer - Clean Install Mode
* Backup, selective cleanup from list, restore, then install when no OmniNX found.
* Does not wipe whole card; only paths in deletion_lists_clean.h are removed.
*/
#include "install.h"
#include "backup.h"
#include "deletion_lists_clean.h"
#include "fs.h"
#include "gfx.h"
#include "version.h"
#include <libs/fatfs/ff.h>
#include <string.h>
#undef COLOR_CYAN
#undef COLOR_WHITE
#undef COLOR_GREEN
#undef COLOR_RED
#undef COLOR_YELLOW
#undef COLOR_ORANGE
#define COLOR_CYAN 0xFF00FFFF
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_GREEN 0xFF00FF00
#define COLOR_RED 0xFFFF0000
#define COLOR_YELLOW 0xFFFFDD00
#define COLOR_ORANGE 0xFF00A5FF
#define set_color install_set_color
#define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed
#define path_exists install_path_exists
#define count_directory_items install_count_directory_items
// Clean mode: Backup user data
int clean_mode_backup(void) {
set_color(COLOR_CYAN);
gfx_printf(" Sichere: DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE);
int res = backup_user_data();
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" [OK] Sicherung abgeschlossen\n");
set_color(COLOR_WHITE);
}
return res;
}
// Clean mode: Wipe using selective deletion list (does not wipe whole card)
int clean_mode_wipe(void) {
check_and_clear_screen_if_needed();
set_color(COLOR_WHITE);
delete_path_lists_grouped("atmosphere/",
clean_atmosphere_dirs_to_delete,
clean_atmosphere_contents_dirs_to_delete,
clean_atmosphere_files_to_delete,
NULL);
delete_path_lists_grouped("bootloader/",
clean_bootloader_dirs_to_delete,
clean_bootloader_files_to_delete,
NULL);
delete_path_lists_grouped("config/",
clean_config_dirs_to_delete,
NULL);
delete_path_lists_grouped("switch/",
clean_switch_dirs_to_delete,
clean_switch_files_to_delete,
NULL);
delete_path_lists_grouped("Root-Dateien",
clean_root_files_to_delete,
clean_misc_dirs_to_delete,
clean_misc_files_to_delete,
NULL);
delete_path_lists_grouped("Alte Versionsmarker",
old_version_files_to_delete,
NULL);
set_color(COLOR_CYAN);
gfx_printf(" Erstelle: switch/\n");
set_color(COLOR_WHITE);
f_mkdir("sd:/switch");
set_color(COLOR_GREEN);
gfx_printf(" Bereinigung abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Clean mode: Restore user data
int clean_mode_restore(void) {
set_color(COLOR_CYAN);
gfx_printf(" Stelle wieder her: 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;
}
// Clean mode: Install files (reuse update mode install)
int clean_mode_install(omninx_variant_t variant) {
return update_mode_install(variant);
}
// Remove other staging directories (OmniNX Standard/Light/OC) that exist on SD
int cleanup_other_staging_directories(omninx_variant_t installed_variant) {
static const omninx_variant_t all_variants[] = { VARIANT_STANDARD, VARIANT_LIGHT, VARIANT_OC };
const int n = sizeof(all_variants) / sizeof(all_variants[0]);
int last_res = FR_OK;
for (int i = 0; i < n; i++) {
omninx_variant_t v = all_variants[i];
if (v == installed_variant)
continue;
const char *staging = get_staging_path(v);
if (!staging || !path_exists(staging))
continue;
check_and_clear_screen_if_needed();
set_color(COLOR_YELLOW);
gfx_printf("\nEntferne weiteren Installationsordner...\n");
set_color(COLOR_WHITE);
int total = count_directory_items(staging);
u32 start_x, start_y;
gfx_con_getpos(&start_x, &start_y);
const char *folder_name = strrchr(staging, '/');
if (folder_name) folder_name++;
else folder_name = staging;
set_color(COLOR_CYAN);
gfx_printf(" Loesche: %s [ 0%%] (0/%d)", folder_name, total);
set_color(COLOR_WHITE);
int deleted = 0;
int last_percent = -1;
int res = folder_delete_progress_recursive(staging, &deleted, total, start_x, start_y, folder_name, &last_percent);
gfx_con_setpos(start_x, start_y);
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", folder_name, deleted, total);
set_color(COLOR_WHITE);
} else {
set_color(COLOR_ORANGE);
gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", folder_name);
gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res);
set_color(COLOR_WHITE);
}
last_res = res;
}
return last_res;
}

View File

@@ -1,237 +1,182 @@
/*
* OmniNX Installer - Update Mode
* Selective cleanup and install when OmniNX is already installed.
*/
#include "install.h"
#include "deletion_lists_update.h"
#include "fs.h"
#include "version.h"
#include "gfx.h"
#include <libs/fatfs/ff.h>
#include <string.h>
#include <utils/sprintf.h>
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#undef COLOR_CYAN
#undef COLOR_WHITE
#undef COLOR_GREEN
#undef COLOR_YELLOW
#undef COLOR_ORANGE
#undef COLOR_RED
#define COLOR_CYAN 0xFF00FFFF
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_GREEN 0xFF00FF00
#define COLOR_YELLOW 0xFFFFDD00
#define COLOR_ORANGE 0xFF00A5FF
#define COLOR_RED 0xFFFF0000
#define set_color install_set_color
#define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed
#define path_exists install_path_exists
#define count_directory_items install_count_directory_items
// Update mode: Cleanup specific directories/files
int update_mode_cleanup(omninx_variant_t variant) {
(void)variant;
check_and_clear_screen_if_needed();
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: atmosphere/\n");
set_color(COLOR_WHITE);
delete_path_list(atmosphere_dirs_to_delete, "atmosphere subdirs");
delete_path_list(atmosphere_contents_dirs_to_delete, "atmosphere contents dirs");
delete_path_list(atmosphere_files_to_delete, "atmosphere files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: bootloader/\n");
set_color(COLOR_WHITE);
delete_path_list(bootloader_dirs_to_delete, "bootloader dirs");
delete_path_list(bootloader_files_to_delete, "bootloader files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: config/\n");
set_color(COLOR_WHITE);
delete_path_list(config_dirs_to_delete, "config dirs");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: switch/\n");
set_color(COLOR_WHITE);
delete_path_list(switch_dirs_to_delete, "switch dirs");
delete_path_list(switch_files_to_delete, "switch files");
set_color(COLOR_CYAN);
gfx_printf(" Bereinige: Root-Dateien\n");
set_color(COLOR_WHITE);
delete_path_list(root_files_to_delete, "root files");
delete_path_list(misc_dirs_to_delete, "misc dirs");
delete_path_list(misc_files_to_delete, "misc files");
set_color(COLOR_GREEN);
gfx_printf(" Bereinigung abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Update mode: Copy files from staging
int update_mode_install(omninx_variant_t variant) {
int res;
const char* staging = get_staging_path(variant);
char src_path[256];
char dst_path[256];
if (!staging) {
return FR_INVALID_PARAMETER;
}
check_and_clear_screen_if_needed();
set_color(COLOR_YELLOW);
gfx_printf("Dateien werden kopiert...\n");
set_color(COLOR_WHITE);
s_printf(src_path, "%s/atmosphere", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "atmosphere/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/bootloader", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "bootloader/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/config", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "config/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/switch", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "switch/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/warmboot_mariko", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "warmboot_mariko/");
if (res != FR_OK && res != FR_NO_FILE) return res;
if (variant == VARIANT_OC) {
s_printf(src_path, "%s/SaltySD", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "SaltySD/");
if (res != FR_OK && res != FR_NO_FILE) return res;
}
set_color(COLOR_CYAN);
gfx_printf(" Kopiere Root-Dateien...\n");
set_color(COLOR_WHITE);
s_printf(src_path, "%s/boot.dat", staging);
s_printf(dst_path, "sd:/boot.dat");
if (path_exists(src_path)) file_copy(src_path, dst_path);
s_printf(src_path, "%s/boot.ini", staging);
s_printf(dst_path, "sd:/boot.ini");
if (path_exists(src_path)) file_copy(src_path, dst_path);
s_printf(src_path, "%s/exosphere.ini", staging);
s_printf(dst_path, "sd:/exosphere.ini");
if (path_exists(src_path)) file_copy(src_path, dst_path);
s_printf(src_path, "%s/hbmenu.nro", staging);
s_printf(dst_path, "sd:/hbmenu.nro");
if (path_exists(src_path)) file_copy(src_path, dst_path);
s_printf(src_path, "%s/loader.bin", staging);
s_printf(dst_path, "sd:/loader.bin");
if (path_exists(src_path)) file_copy(src_path, dst_path);
s_printf(src_path, "%s/payload.bin", staging);
s_printf(dst_path, "sd:/payload.bin");
if (path_exists(src_path)) file_copy(src_path, dst_path);
set_color(COLOR_CYAN);
gfx_printf(" Erstelle manifest.ini...\n");
set_color(COLOR_WHITE);
s_printf(dst_path, "sd:/config/omninx");
f_mkdir(dst_path);
const char* pack_name;
int update_channel;
switch (variant) {
case VARIANT_STANDARD: pack_name = "standard"; update_channel = 2; break;
case VARIANT_LIGHT: pack_name = "light"; update_channel = 0; break;
case VARIANT_OC: pack_name = "oc"; update_channel = 1; break;
default: pack_name = "unknown"; update_channel = 0; break;
}
s_printf(dst_path, "sd:/config/omninx/manifest.ini");
FIL fp;
if (f_open(&fp, dst_path, FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) {
f_printf(&fp, "[OmniNX]\n");
f_printf(&fp, "current_pack=%s\n", pack_name);
f_printf(&fp, "version=%s\n", VERSION);
f_printf(&fp, "update_channel=%d\n", update_channel);
f_printf(&fp, "channel_pack=%s\n", pack_name);
f_close(&fp);
set_color(COLOR_GREEN);
gfx_printf(" [OK] manifest.ini erstellt\n");
set_color(COLOR_WHITE);
} else {
set_color(COLOR_ORANGE);
gfx_printf(" [WARN] manifest.ini konnte nicht erstellt werden\n");
set_color(COLOR_WHITE);
}
set_color(COLOR_GREEN);
gfx_printf(" Kopie abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Remove staging directory after installation
int cleanup_staging_directory(omninx_variant_t pack_variant) {
const char* staging = get_staging_path(pack_variant);
if (!staging) {
return FR_INVALID_PARAMETER;
}
check_and_clear_screen_if_needed();
if (path_exists(staging)) {
set_color(COLOR_YELLOW);
gfx_printf("\nEntferne Installationsordner...\n");
set_color(COLOR_WHITE);
int total = count_directory_items(staging);
u32 start_x, start_y;
gfx_con_getpos(&start_x, &start_y);
const char *folder_name = strrchr(staging, '/');
if (folder_name) folder_name++;
else folder_name = staging;
set_color(COLOR_CYAN);
gfx_printf(" Loesche: %s [ 0%%] (0/%d)", folder_name, total);
set_color(COLOR_WHITE);
int deleted = 0;
int last_percent = -1;
int res = folder_delete_progress_recursive(staging, &deleted, total, start_x, start_y, folder_name, &last_percent);
gfx_con_setpos(start_x, start_y);
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", folder_name, deleted, total);
set_color(COLOR_WHITE);
} else {
set_color(COLOR_ORANGE);
gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", folder_name);
gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res);
set_color(COLOR_WHITE);
}
return res;
}
return FR_OK;
}
/*
* OmniNX Installer - Update Mode
* Selective cleanup and install when OmniNX is already installed.
*/
#include "install.h"
#include "deletion_lists_update.h"
#include "fs.h"
#include "version.h"
#include "gfx.h"
#include <libs/fatfs/ff.h>
#include <string.h>
#include <utils/sprintf.h>
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#undef COLOR_CYAN
#undef COLOR_WHITE
#undef COLOR_GREEN
#undef COLOR_YELLOW
#undef COLOR_ORANGE
#undef COLOR_RED
#define COLOR_CYAN 0xFF00FFFF
#define COLOR_WHITE 0xFFFFFFFF
#define COLOR_GREEN 0xFF00FF00
#define COLOR_YELLOW 0xFFFFDD00
#define COLOR_ORANGE 0xFF00A5FF
#define COLOR_RED 0xFFFF0000
#define set_color install_set_color
#define check_and_clear_screen_if_needed install_check_and_clear_screen_if_needed
#define path_exists install_path_exists
#define count_directory_items install_count_directory_items
// Update mode: Cleanup specific directories/files
int update_mode_cleanup(omninx_variant_t variant) {
(void)variant;
check_and_clear_screen_if_needed();
set_color(COLOR_WHITE);
delete_path_lists_grouped("atmosphere/",
atmosphere_dirs_to_delete,
atmosphere_contents_dirs_to_delete,
atmosphere_files_to_delete,
NULL);
delete_path_lists_grouped("bootloader/",
bootloader_dirs_to_delete,
bootloader_files_to_delete,
NULL);
delete_path_lists_grouped("config/",
config_dirs_to_delete,
NULL);
delete_path_lists_grouped("switch/",
switch_dirs_to_delete,
switch_files_to_delete,
NULL);
delete_path_lists_grouped("Root-Dateien",
root_files_to_delete,
misc_dirs_to_delete,
misc_files_to_delete,
NULL);
set_color(COLOR_GREEN);
gfx_printf(" Bereinigung abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Update mode: Copy files from staging
int update_mode_install(omninx_variant_t variant) {
int res;
const char* staging = get_staging_path(variant);
char src_path[256];
if (!staging) {
return FR_INVALID_PARAMETER;
}
check_and_clear_screen_if_needed();
set_color(COLOR_YELLOW);
gfx_printf("Dateien werden kopiert...\n");
set_color(COLOR_WHITE);
s_printf(src_path, "%s/atmosphere", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "atmosphere/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/bootloader", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "bootloader/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/config", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "config/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/switch", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "switch/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/themes", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "themes/");
if (res != FR_OK && res != FR_NO_FILE) return res;
s_printf(src_path, "%s/warmboot_mariko", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "warmboot_mariko/");
if (res != FR_OK && res != FR_NO_FILE) return res;
if (variant == VARIANT_OC) {
s_printf(src_path, "%s/SaltySD", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "SaltySD/");
if (res != FR_OK && res != FR_NO_FILE) return res;
}
set_color(COLOR_CYAN);
gfx_printf(" Kopiere Root-Dateien...\n");
set_color(COLOR_WHITE);
res = install_copy_staging_root_files(staging, "sd:/");
if (res != FR_OK)
return res;
set_color(COLOR_GREEN);
gfx_printf(" Kopie abgeschlossen!\n");
set_color(COLOR_WHITE);
return FR_OK;
}
// Remove staging directory after installation
int cleanup_staging_directory(omninx_variant_t pack_variant) {
const char* staging = get_staging_path(pack_variant);
if (!staging) {
return FR_INVALID_PARAMETER;
}
check_and_clear_screen_if_needed();
if (path_exists(staging)) {
set_color(COLOR_YELLOW);
gfx_printf("\nEntferne Installationsordner...\n");
set_color(COLOR_WHITE);
int total = count_directory_items(staging);
u32 start_x, start_y;
gfx_con_getpos(&start_x, &start_y);
const char *folder_name = strrchr(staging, '/');
if (folder_name) folder_name++;
else folder_name = staging;
set_color(COLOR_CYAN);
gfx_printf(" Loesche: %s [ 0%%] (0/%d)", folder_name, total);
set_color(COLOR_WHITE);
int deleted = 0;
int last_percent = -1;
int res = folder_delete_progress_recursive(staging, &deleted, total, start_x, start_y, folder_name, &last_percent);
gfx_con_setpos(start_x, start_y);
if (res == FR_OK) {
set_color(COLOR_GREEN);
gfx_printf(" Loesche: %s [100%%] (%d/%d) - Fertig!\n", folder_name, deleted, total);
set_color(COLOR_WHITE);
} else {
set_color(COLOR_ORANGE);
gfx_printf(" Loesche: %s - Fehlgeschlagen!\n", folder_name);
gfx_printf(" [WARN] Ordner konnte nicht entfernt werden (err=%d)\n", res);
set_color(COLOR_WHITE);
}
return res;
}
return FR_OK;
}

View File

@@ -1,9 +1,9 @@
/*
* HATS Installer - Simplified disk I/O (SD card only)
* Based on TegraExplorer diskio.c by shchmue
* OmniNX Installer - Simplified disk I/O (SD card only)
* Based on HATS Installer, TegraExplorer diskio.c by shchmue
*
* This simplified version removes BIS/eMMC support since the
* HATS installer only needs SD card access.
* OmniNX installer only needs SD card access.
*/
/*-----------------------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@@ -1,228 +1,233 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <storage/nx_sd.h>
#include <storage/sdmmc.h>
#include <storage/sdmmc_driver.h>
#include <gfx_utils.h>
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
bool sd_mounted = false;
static u16 sd_errors[3] = { 0 }; // Init and Read/Write errors.
static u32 sd_mode = SD_UHS_SDR82;
sdmmc_t sd_sdmmc;
sdmmc_storage_t sd_storage;
FATFS sd_fs;
void sd_error_count_increment(u8 type)
{
switch (type)
{
case SD_ERROR_INIT_FAIL:
sd_errors[0]++;
break;
case SD_ERROR_RW_FAIL:
sd_errors[1]++;
break;
case SD_ERROR_RW_RETRY:
sd_errors[2]++;
break;
}
}
u16 *sd_get_error_count()
{
return sd_errors;
}
bool sd_get_card_removed()
{
if (!sdmmc_get_sd_inserted())
return true;
return false;
}
u32 sd_get_mode()
{
return sd_mode;
}
int sd_init_retry(bool power_cycle)
{
u32 bus_width = SDMMC_BUS_WIDTH_4;
u32 type = SDHCI_TIMING_UHS_SDR82;
// Power cycle SD card.
if (power_cycle)
{
sd_mode--;
sdmmc_storage_end(&sd_storage);
}
// Get init parameters.
switch (sd_mode)
{
case SD_INIT_FAIL: // Reset to max.
return 0;
case SD_1BIT_HS25:
bus_width = SDMMC_BUS_WIDTH_1;
type = SDHCI_TIMING_SD_HS25;
break;
case SD_4BIT_HS25:
type = SDHCI_TIMING_SD_HS25;
break;
case SD_UHS_SDR82:
type = SDHCI_TIMING_UHS_SDR82;
break;
default:
sd_mode = SD_UHS_SDR82;
}
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
}
bool sd_initialize(bool power_cycle)
{
if (power_cycle)
sdmmc_storage_end(&sd_storage);
int res = !sd_init_retry(false);
while (true)
{
if (!res)
return true;
else if (!sdmmc_get_sd_inserted()) // SD Card is not inserted.
{
sd_mode = SD_UHS_SDR82;
break;
}
else
{
sd_errors[SD_ERROR_INIT_FAIL]++;
if (sd_mode == SD_INIT_FAIL)
break;
else
res = !sd_init_retry(true);
}
}
sdmmc_storage_end(&sd_storage);
return false;
}
bool is_sd_inited = false;
bool sd_mount()
{
if (sd_mounted)
return true;
int res = !sd_initialize(false);
is_sd_inited = !res;
if (res)
{
gfx_con.mute = false;
EPRINTF("Failed to init SD card.");
if (!sdmmc_get_sd_inserted())
EPRINTF("Make sure that it is inserted.");
else
EPRINTF("SD Card Reader is not properly seated!");
}
else
{
res = f_mount(&sd_fs, "", 1);
if (res == FR_OK)
{
sd_mounted = true;
return true;
}
else
{
gfx_con.mute = false;
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\nMake sure that a FAT partition exists..", res);
}
}
return false;
}
static void _sd_deinit()
{
if (sd_mode == SD_INIT_FAIL)
sd_mode = SD_UHS_SDR82;
if (sd_mounted)
{
f_mount(NULL, "", 1);
sdmmc_storage_end(&sd_storage);
sd_mounted = false;
is_sd_inited = false;
}
}
void sd_unmount() { _sd_deinit(); }
void sd_end() { _sd_deinit(); }
void *sd_file_read(const char *path, u32 *fsize)
{
FIL fp;
if (f_open(&fp, path, FA_READ) != FR_OK)
return NULL;
u32 size = f_size(&fp);
if (fsize)
*fsize = size;
char *buf = malloc(size + 1);
buf[size] = '\0';
if (f_read(&fp, buf, size, NULL) != FR_OK)
{
free(buf);
f_close(&fp);
return NULL;
}
f_close(&fp);
return buf;
}
int sd_save_to_file(void *buf, u32 size, const char *filename)
{
FIL fp;
u32 res = 0;
res = f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE);
if (res)
{
EPRINTFARGS("Error (%d) creating file\n%s.\n", res, filename);
return res;
}
f_write(&fp, buf, size, NULL);
f_close(&fp);
return 0;
}
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <storage/nx_sd.h>
#include <storage/sdmmc.h>
#include <storage/sdmmc_driver.h>
#include <gfx_utils.h>
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
bool sd_mounted = false;
static u16 sd_errors[3] = { 0 }; // Init and Read/Write errors.
static u32 sd_mode = SD_UHS_SDR82;
sdmmc_t sd_sdmmc;
sdmmc_storage_t sd_storage;
FATFS sd_fs;
void sd_error_count_increment(u8 type)
{
switch (type)
{
case SD_ERROR_INIT_FAIL:
sd_errors[0]++;
break;
case SD_ERROR_RW_FAIL:
sd_errors[1]++;
break;
case SD_ERROR_RW_RETRY:
sd_errors[2]++;
break;
}
}
u16 *sd_get_error_count()
{
return sd_errors;
}
bool sd_get_card_removed()
{
if (!sdmmc_get_sd_inserted())
return true;
return false;
}
u32 sd_get_mode()
{
return sd_mode;
}
int sd_init_retry(bool power_cycle)
{
u32 bus_width = SDMMC_BUS_WIDTH_4;
u32 type = SDHCI_TIMING_UHS_SDR82;
// Power cycle SD card.
if (power_cycle)
{
sd_mode--;
sdmmc_storage_end(&sd_storage);
}
// Get init parameters.
switch (sd_mode)
{
case SD_INIT_FAIL: // Reset to max.
return 0;
case SD_1BIT_HS25:
bus_width = SDMMC_BUS_WIDTH_1;
type = SDHCI_TIMING_SD_HS25;
break;
case SD_4BIT_HS25:
type = SDHCI_TIMING_SD_HS25;
break;
case SD_UHS_SDR82:
type = SDHCI_TIMING_UHS_SDR82;
break;
default:
sd_mode = SD_UHS_SDR82;
}
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
}
bool sd_initialize(bool power_cycle)
{
if (power_cycle)
sdmmc_storage_end(&sd_storage);
int res = !sd_init_retry(false);
while (true)
{
if (!res)
return true;
else if (!sdmmc_get_sd_inserted()) // SD Card is not inserted.
{
sd_mode = SD_UHS_SDR82;
break;
}
else
{
sd_errors[SD_ERROR_INIT_FAIL]++;
if (sd_mode == SD_INIT_FAIL)
break;
else
res = !sd_init_retry(true);
}
}
sdmmc_storage_end(&sd_storage);
return false;
}
bool is_sd_inited = false;
bool sd_mount()
{
if (sd_mounted)
return true;
int res = !sd_initialize(false);
is_sd_inited = !res;
if (res)
{
gfx_con.mute = false;
EPRINTF("Failed to init SD card.");
if (!sdmmc_get_sd_inserted())
EPRINTF("Make sure that it is inserted.");
else
EPRINTF("SD Card Reader is not properly seated!");
}
else
{
res = f_mount(&sd_fs, "", 1);
if (res == FR_OK)
{
sd_mounted = true;
return true;
}
else
{
gfx_con.mute = false;
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\nMake sure that a FAT partition exists..", res);
}
}
return false;
}
static void _sd_deinit()
{
if (sd_mode == SD_INIT_FAIL)
sd_mode = SD_UHS_SDR82;
if (sd_mounted)
{
f_mount(NULL, "", 1);
sdmmc_storage_end(&sd_storage);
sd_mounted = false;
is_sd_inited = false;
}
}
void sd_unmount() { _sd_deinit(); }
void sd_end() { _sd_deinit(); }
bool sd_get_card_mounted(void)
{
return sd_mounted;
}
void *sd_file_read(const char *path, u32 *fsize)
{
FIL fp;
if (f_open(&fp, path, FA_READ) != FR_OK)
return NULL;
u32 size = f_size(&fp);
if (fsize)
*fsize = size;
char *buf = malloc(size + 1);
buf[size] = '\0';
if (f_read(&fp, buf, size, NULL) != FR_OK)
{
free(buf);
f_close(&fp);
return NULL;
}
f_close(&fp);
return buf;
}
int sd_save_to_file(void *buf, u32 size, const char *filename)
{
FIL fp;
u32 res = 0;
res = f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE);
if (res)
{
EPRINTFARGS("Error (%d) creating file\n%s.\n", res, filename);
return res;
}
f_write(&fp, buf, size, NULL);
f_close(&fp);
return 0;
}

111
source/screenshot.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* Screenshot support for OmniNX Installer Payload
* Based on TegraExplorer (AllgemeinerProblemLoeser) TakeScreenshot implementation.
*/
#include "screenshot.h"
#include "gfx.h"
#include "nx_sd.h"
#include <libs/fatfs/ff.h>
#include <mem/heap.h>
#include <display/di.h>
#include <utils/util.h>
#include <utils/sprintf.h>
#include <rtc/max77620-rtc.h>
#include <string.h>
/* BMP file header (54 bytes), matching TegraExplorer tools.h */
typedef struct __attribute__((packed)) _bmp_hdr_t
{
u16 magic;
u32 size;
u32 rsvd;
u32 data_off;
u32 hdr_size;
u32 width;
u32 height;
u16 planes;
u16 pxl_bits;
u32 comp;
u32 img_size;
u32 res_h;
u32 res_v;
u64 rsvd2;
} bmp_hdr_t;
#define BMP_HEADER_SIZE 0x36
#define FB_SIZE 0x384000 /* 1280 * 720 * 4 */
#define SCREEN_W 1280
#define SCREEN_H 720
void take_screenshot(void)
{
static u32 last_timer = 0;
if (!sd_get_card_mounted())
return;
/* 3-second cooldown (same as TegraExplorer) */
u32 now = get_tmr_s();
if (last_timer != 0 && now < last_timer + 3)
return;
last_timer = now;
const char basepath[] = "sd:/switch/screenshot";
char name[48];
char path[80];
rtc_time_t rtc;
max77620_rtc_get_time(&rtc);
s_printf(name, "omninx_installer_%04d%02d%02d_%02d%02d%02d.bmp",
(u32)rtc.year, (u32)rtc.month, (u32)rtc.day,
(u32)rtc.hour, (u32)rtc.min, (u32)rtc.sec);
s_printf(path, "%s/%s", basepath, name);
f_mkdir("sd:/switch");
f_mkdir(basepath);
const u32 file_size = BMP_HEADER_SIZE + FB_SIZE;
u8 *bitmap = (u8 *)malloc(file_size);
u32 *fb_copy = (u32 *)malloc(FB_SIZE);
if (!bitmap || !fb_copy) {
if (bitmap) free(bitmap);
if (fb_copy) free(fb_copy);
return;
}
/* Copy framebuffer with same coordinate flip as TegraExplorer (BGR layout) */
u32 *fb_ptr = gfx_ctxt.fb;
for (int x = SCREEN_W - 1; x >= 0; x--) {
for (int y = SCREEN_H - 1; y >= 0; y--)
fb_copy[y * SCREEN_W + x] = *fb_ptr++;
}
memcpy(bitmap + BMP_HEADER_SIZE, fb_copy, FB_SIZE);
bmp_hdr_t *bmp = (bmp_hdr_t *)bitmap;
bmp->magic = 0x4D42;
bmp->size = file_size;
bmp->rsvd = 0;
bmp->data_off = BMP_HEADER_SIZE;
bmp->hdr_size = 40;
bmp->width = SCREEN_W;
bmp->height = SCREEN_H;
bmp->planes = 1;
bmp->pxl_bits = 32;
bmp->comp = 0;
bmp->img_size = FB_SIZE;
bmp->res_h = 2834;
bmp->res_v = 2834;
bmp->rsvd2 = 0;
sd_save_to_file(bitmap, file_size, path);
free(bitmap);
free(fb_copy);
/* Brief backlight flash (same as TegraExplorer) */
display_backlight_brightness(255, 1000);
msleep(100);
display_backlight_brightness(100, 1000);
}

13
source/screenshot.h Normal file
View File

@@ -0,0 +1,13 @@
/*
* Screenshot support for OmniNX Installer Payload
* Based on TegraExplorer (AllgemeinerProblemLoeser) TakeScreenshot implementation.
*/
#pragma once
#include <utils/types.h>
/* Take a screenshot of the current framebuffer and save as BMP to SD.
* Triggered by Capture button (jc->cap). Uses 3-second cooldown.
* Saves to sd:/switch/screenshot/omninx_installer_YYYYMMDD_HHMMSS.bmp */
void take_screenshot(void);

View File

@@ -50,11 +50,11 @@ static omninx_variant_t read_manifest_variant(const char *manifest_path) {
LIST_FOREACH_ENTRY(ini_kv_t, kv, &sec->kvs, link) {
if (kv->key && !strcmp(kv->key, "current_pack")) {
if (kv->val) {
if (!strcmp(kv->val, "standard")) {
if (!strcmp(kv->val, "Standard")) {
variant = VARIANT_STANDARD;
} else if (!strcmp(kv->val, "light")) {
} else if (!strcmp(kv->val, "Light")) {
variant = VARIANT_LIGHT;
} else if (!strcmp(kv->val, "oc")) {
} else if (!strcmp(kv->val, "OC")) {
variant = VARIANT_OC;
}
}
@@ -156,6 +156,20 @@ omninx_variant_t detect_pack_variant(void) {
return VARIANT_NONE;
}
// Detect all pack variants present on SD card; returns count, fills out_variants[]
int detect_present_variants(omninx_variant_t *out_variants, int max_count) {
int count = 0;
if (max_count <= 0 || !out_variants)
return 0;
if (file_exists(STAGING_STANDARD) && count < max_count)
out_variants[count++] = VARIANT_STANDARD;
if (file_exists(STAGING_LIGHT) && count < max_count)
out_variants[count++] = VARIANT_LIGHT;
if (file_exists(STAGING_OC) && count < max_count)
out_variants[count++] = VARIANT_OC;
return count;
}
// Get human-readable variant name
const char* get_variant_name(omninx_variant_t variant) {
switch (variant) {

View File

@@ -23,9 +23,12 @@ typedef struct {
// Detect current OmniNX installation status
omninx_status_t detect_omninx_installation(void);
// Detect which pack variant is present on SD card
// Detect which pack variant is present on SD card (first found in fixed order)
omninx_variant_t detect_pack_variant(void);
// Detect all pack variants present on SD card; returns count, fills out_variants[] (max max_count)
int detect_present_variants(omninx_variant_t *out_variants, int max_count);
// Get human-readable variant name
const char* get_variant_name(omninx_variant_t variant);

70
tools/ram_test_main.c Normal file
View File

@@ -0,0 +1,70 @@
/*
* Minimal RAM info test payload (fuse DRAM ID + SK table -> MiB).
* Build: make ram-test -> output/RAM-Test.bin
*/
#include <display/di.h>
#include <mem/heap.h>
#include <mem/minerva.h>
#include <memory_map.h>
#include <soc/bpmp.h>
#include <soc/fuse.h>
#include <soc/hw_init.h>
#include <soc/t210.h>
#include <utils/util.h>
#include "dram_fuse.h"
#include "gfx.h"
#undef COLOR_CYAN
#undef COLOR_WHITE
#define COLOR_CYAN 0xFF00FFFF
#define COLOR_WHITE 0xFFFFFFFF
/* Required by BDK */
boot_cfg_t __attribute__((section("._boot_cfg"))) b_cfg;
volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR;
extern void pivot_stack(u32 stack_top);
void ipl_main(void)
{
hw_init();
pivot_stack(IPL_STACK_TOP);
heap_init(IPL_HEAP_START);
minerva_init();
minerva_change_freq(FREQ_800);
display_init();
u32 *fb = display_init_framebuffer_pitch();
gfx_init_ctxt(fb, 720, 1280, 720);
gfx_con_init();
display_backlight_pwm_init();
display_backlight_brightness(100, 1000);
bpmp_clk_rate_set(BPMP_CLK_DEFAULT_BOOST);
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
gfx_con_setcol(COLOR_CYAN, gfx_con.fillbg, gfx_con.bgcol);
gfx_printf("RAM test payload\n\n");
gfx_con_setcol(COLOR_WHITE, gfx_con.fillbg, gfx_con.bgcol);
u32 raw = fuse_read_dramid(true);
u32 nid = fuse_read_dramid(false);
u32 chip = hw_get_chip_id();
int mib = dram_capacity_mib_from_fuse();
gfx_printf("SoC: %s\n", chip == GP_HIDREV_MAJOR_T210B01 ? "Mariko (T210B01)" : "Erista (T210)");
gfx_printf("DRAM fuse: raw %d norm %d\n", raw, nid);
if (mib > 0)
gfx_printf("Table MiB: %d (fuse SKU, not probe)\n", mib);
else
gfx_printf("Table MiB: (unknown id for this SoC)\n");
gfx_printf("\nHang — power off or inject payload.\n");
while (1)
msleep(500);
}