Compare commits

...

39 Commits

Author SHA1 Message Date
05926f7828 Show Install/Update completion based on detected mode.
All checks were successful
Build / Build (push) Successful in 14s
Drop the version header from the final summary and label the result as Update or Installation.
2026-07-17 13:43:00 +02:00
7b85d428a8 Add missing pack apps to update-mode deletion lists.
Include Benchmark-Toolbox, Furmark-NX, and other switch apps from the current OmniNX pack so updates replace them cleanly.
2026-07-17 13:42:42 +02:00
dce83bc3be Update FatFs NORTC fixed timestamp to 2026-07-17. 2026-07-17 13:42:24 +02:00
a26531c702 Add extras post-copy flow for RAM and Hekate-Pro selection.
All checks were successful
Build / Build (push) Successful in 14s
Copy sd:/extras from the pack, prompt for RAM and Hekate/AMS choices, persist them in config.ini, apply Pro/8GB payloads, then remove extras/. Use safe config.ini I/O and Joy-Con input clearing between menus.
2026-07-16 10:01:42 +02:00
c0741beda3 Preserve CyberFoil across clean install backup/restore.
All checks were successful
Build / Build (push) Successful in 13s
Back up cyberfoil.nro, config.json, and remotes/ when sd:/switch/CyberFoil exists, then restore them after the wipe.
2026-06-14 17:31:46 +02:00
d6ec345cce only check UHS on clean install
All checks were successful
Build / Build (push) Successful in 12s
2026-06-07 23:16:04 +02:00
9990dfb2ba install: shortend finish delay to 1,5 seconds
All checks were successful
Build / Build (push) Successful in 13s
2026-06-04 22:02:22 +02:00
c7ad761592 Added NiklasCFW Pack 1.6.2 Marker 2026-06-04 21:52:52 +02:00
59f103874c Preserve.ini for update cleanup, Hekate UI strings, docs
All checks were successful
Build / Build (push) Successful in 17s
- Load sd:/config/omninx/preserve.ini during update mode cleanup to skip
  delete paths that overlap user-preserved prefixes (heap-backed path list).
- Document preserve.ini in PRESERVE_INI.md, README, and INSTALLATION_PROCESS.
- Prefer Hekate wording in main.c user messages for bootloader launch/errors.
- Extend update deletion list for exefs_patches (audio_mastervolume, SaltyNX_Fixes, logo).
2026-05-17 11:21:14 +02:00
dce11538d1 docs: README and install process for debug.ini and backup
All checks were successful
Build / Build (push) Successful in 17s
- Link DEBUG_INI.md; describe clean install backup (dbi.config, prod.keys)
- Update INSTALLATION_PROCESS backup/restore steps

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

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

Made-with: Cursor
2026-04-12 16:22:41 +02:00
6780936d2d chore: normalize line endings in fs.c and fs.h
Made-with: Cursor
2026-04-12 16:22:39 +02:00
82639a7a86 Clean install: align deletions with NiklasCFW pack script
All checks were successful
Build / Build (push) Successful in 17s
- Remove entire sd:/atmosphere and sd:/switch (lists simplified)
- Add sd:/SaltySD/patches; drop nyx.ini and config/quickntp to match script
- Delete sd:/switch/tinfoil/db after backup restore via clean_post_restore_dirs_to_delete

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

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

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

Made-with: Cursor
2026-04-11 00:12:45 +02:00
4535c4508f Added TegraExplorer 2026-04-10 22:32:01 +02:00
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
27 changed files with 3080 additions and 1632 deletions

3
.gitignore vendored
View File

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

90
DEBUG_INI.md Normal file
View File

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

View File

@@ -2,6 +2,8 @@
This document provides a detailed, step-by-step breakdown of everything that is checked and done during the OmniNX installation/update process. 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 ## Overview
The OmniNX Installer Payload operates in two modes: The OmniNX Installer Payload operates in two modes:
@@ -88,7 +90,9 @@ The OmniNX Installer Payload operates in two modes:
**Trigger**: `INSTALL_MODE_UPDATE` (OmniNX already installed) **Trigger**: `INSTALL_MODE_UPDATE` (OmniNX already installed)
### Step 1: Cleanup (Selective Deletion) ### Step 1: Cleanup (Selective Deletion)
**Location**: `install.c:321-380` **Location**: `install_update.c` (`update_mode_cleanup`), `install.c` (`delete_path_lists_grouped`)
Before deletion, the payload reads optional **`sd:/config/omninx/preserve.ini`** (section `[Preserve]`). Paths enabled there are skipped, as are delete targets that would remove them (parent/child overlap). See **[PRESERVE_INI.md](PRESERVE_INI.md)**. Clean install does not use this file.
#### 10. Clean Atmosphere Subdirectories #### 10. Clean Atmosphere Subdirectories
**Location**: `install.c:325-327`, `deletion_lists.h:9-34` **Location**: `install.c:325-327`, `deletion_lists.h:9-34`
@@ -305,17 +309,12 @@ channel_pack={variant} # Same as current_pack
#### 32. Create Backup Directory #### 32. Create Backup Directory
- Creates `sd:/temp_backup/` directory - Creates `sd:/temp_backup/` directory
#### 33. Backup DBI #### 33. Backup DBI settings
- **Source**: `sd:/switch/DBI/` - **Source**: `sd:/switch/DBI/dbi.config` (file only, not the whole `DBI` tree)
- **Destination**: `sd:/temp_backup/DBI/` - **Destination**: `sd:/temp_backup/dbi.config`
- Only if source exists - Only if the source file exists
#### 34. Backup Tinfoil #### 34. Backup prod.keys
- **Source**: `sd:/switch/tinfoil/`
- **Destination**: `sd:/temp_backup/tinfoil/`
- Only if source exists
#### 35. Backup prod.keys
- **Source**: `sd:/switch/prod.keys` - **Source**: `sd:/switch/prod.keys`
- **Destination**: `sd:/temp_backup/prod.keys` - **Destination**: `sd:/temp_backup/prod.keys`
- Only if source exists - Only if source exists
@@ -325,22 +324,22 @@ channel_pack={variant} # Same as current_pack
### Step 2: Wipe Directories ### Step 2: Wipe Directories
**Location**: `install.c:543-602` **Location**: `install.c:543-602`
#### 36. Delete Entire Directories #### 35. Delete Entire Directories
Recursively deletes (if they exist): Recursively deletes (if they exist):
- `sd:/atmosphere/` (entire directory tree) - `sd:/atmosphere/` (entire directory tree)
- `sd:/bootloader/` (entire directory tree) - `sd:/bootloader/` (entire directory tree)
- `sd:/config/` (entire directory tree) - `sd:/config/` (entire directory tree)
- `sd:/switch/` (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) - Uses same deletion list as update mode (Step 19)
- Deletes `boot.dat`, `boot.ini`, `exosphere.ini`, etc. - 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) - Uses same deletion lists as update mode (Steps 20-21)
- Deletes `argon/`, `games/`, `SaltySD/`, etc. - Deletes `argon/`, `games/`, `SaltySD/`, etc.
#### 39. Recreate Switch Directory #### 38. Recreate Switch Directory
- Creates empty `sd:/switch/` directory for restoration - Creates empty `sd:/switch/` directory for restoration
--- ---
@@ -348,27 +347,17 @@ Recursively deletes (if they exist):
### Step 3: Restore User Data ### Step 3: Restore User Data
**Location**: `backup.c:55-95` **Location**: `backup.c:55-95`
#### 40. Restore DBI #### 39. Restore DBI settings
- **Source**: `sd:/temp_backup/DBI/` - **Source**: `sd:/temp_backup/dbi.config`
- **Destination**: `sd:/switch/DBI/` - **Destination**: `sd:/switch/DBI/dbi.config` (creates `sd:/switch/DBI/` if needed)
- **Cleanup**: After restoration, deletes old DBI NRO files: - No longer copies the full `DBI` folder; old `.nro` cleanup is unnecessary
- `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`
#### 41. Restore Tinfoil #### 40. Restore prod.keys
- **Source**: `sd:/temp_backup/tinfoil/`
- **Destination**: `sd:/switch/tinfoil/`
- **Cleanup**: After restoration, deletes `sd:/switch/tinfoil/tinfoil.nro`
#### 42. Restore prod.keys
- **Source**: `sd:/temp_backup/prod.keys` - **Source**: `sd:/temp_backup/prod.keys`
- **Destination**: `sd:/switch/prod.keys` - **Destination**: `sd:/switch/prod.keys`
#### 43. Cleanup Backup Directory #### 41. Cleanup Backup Directory
**Location**: `backup.c:98-103` **Location**: `backup.c` (`cleanup_backup`)
- Deletes `sd:/temp_backup/` directory after restoration - 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)) rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
ifeq ($(strip $(DEVKITARM)),) ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM") $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif endif
include $(DEVKITARM)/base_rules include $(DEVKITARM)/base_rules
################################################################################ ################################################################################
IPL_LOAD_ADDR := 0x40008000 IPL_LOAD_ADDR := 0x40008000
VERSION := $(shell cat VERSION) VERSION := $(shell cat VERSION)
################################################################################ ################################################################################
TARGET := omninx-installer TARGET := omninx-installer
OUTPUT_NAME := OmniNX-Installer.bin OUTPUT_NAME := OmniNX-Installer.bin
BUILDDIR := build BUILDDIR := build
OUTPUTDIR := output OUTPUTDIR := output
SOURCEDIR := source SOURCEDIR := source
BDKDIR := bdk BDKDIR := bdk
BDKINC := -I./$(BDKDIR) BDKINC := -I./$(BDKDIR)
VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/)) VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/))
VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/)) VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/))
# All source files # All source files
OBJS = $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \ OBJS = $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \ $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(SOURCEDIR), *.S *.c))) $(call rwildcard, $(SOURCEDIR), *.S *.c)))
OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \ OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \
$(patsubst $(BDKDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \ $(patsubst $(BDKDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \
$(call rwildcard, $(BDKDIR), *.S *.c))) $(call rwildcard, $(BDKDIR), *.S *.c)))
GFX_INC := '"../$(SOURCEDIR)/gfx.h"' # Optional vendored tree under source/TegraExplorer/ is not part of this payload build.
FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"' 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)"'
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
CFLAGS = $(ARCH) -Os -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11 -Wall -Wno-missing-braces $(CUSTOMDEFINES) CUSTOMDEFINES += -DVERSION='"$(VERSION)"'
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections -Xlinker --defsym=IPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
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))) RAMTEST_BIN := $(OUTPUTDIR)/RAM-Test.bin
@echo "Payload size is $(BIN_SIZE) bytes" OBJS_RAMTEST := $(filter-out $(BUILDDIR)/$(TARGET)/main.o,$(OBJS)) \
@echo "Max size is 126296 bytes." $(BUILDDIR)/$(TARGET)/ram_test_main.o
clean: .PHONY: all clean release ram-test
@rm -rf $(BUILDDIR)
@rm -rf $(OUTPUTDIR) all: $(OUTPUTDIR)/$(OUTPUT_NAME)
@rm -rf release $(eval BIN_SIZE = $(shell wc -c < $(OUTPUTDIR)/$(OUTPUT_NAME)))
@rm -f $(TARGET)-*.zip @echo "Payload size is $(BIN_SIZE) bytes"
@echo "Max size is 126296 bytes."
$(OUTPUTDIR)/$(OUTPUT_NAME): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
@mkdir -p "$(@D)" clean:
$(OBJCOPY) -S -O binary $< $(OUTPUTDIR)/$(TARGET).bin @rm -rf $(BUILDDIR)
@mv $(OUTPUTDIR)/$(TARGET).bin $(OUTPUTDIR)/$(OUTPUT_NAME) @rm -rf $(OUTPUTDIR)
@rm -f $(RAMTEST_BIN)
$(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS) @rm -rf release
$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@ @rm -f $(TARGET)-*.zip
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.c $(OUTPUTDIR)/$(OUTPUT_NAME): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@ $(OBJCOPY) -S -O binary $< $(OUTPUTDIR)/$(TARGET).bin
@mv $(OUTPUTDIR)/$(TARGET).bin $(OUTPUTDIR)/$(OUTPUT_NAME)
$(BUILDDIR)/$(TARGET)/%.o: $(SOURCEDIR)/%.S
@mkdir -p "$(@D)" $(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS)
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.c $(BUILDDIR)/$(TARGET)/ram_test_main.o: tools/ram_test_main.c
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
$(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@ $(CC) $(CFLAGS) $(BDKINC) -I$(SOURCEDIR) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.S $(BUILDDIR)/$(TARGET)/ram_test.elf: $(OBJS_RAMTEST)
@mkdir -p "$(@D)" $(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(CC) $(CFLAGS) -c $< -o $@
$(RAMTEST_BIN): $(BUILDDIR)/$(TARGET)/ram_test.elf
release: $(OUTPUTDIR)/$(OUTPUT_NAME) @mkdir -p "$(@D)"
@mkdir -p release/bootloader/payloads $(OBJCOPY) -S -O binary $< $@
@cp $(OUTPUTDIR)/$(OUTPUT_NAME) release/bootloader/payloads/$(OUTPUT_NAME) @echo "RAM-Test payload: $(RAMTEST_BIN) ($$(wc -c < $@) bytes)"
@cd release && zip -r ../$(TARGET)-$(VERSION).zip bootloader
@echo "Release package created: $(TARGET)-$(VERSION).zip" 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"

59
PRESERVE_INI.md Normal file
View File

@@ -0,0 +1,59 @@
# User preserve list (`preserve.ini`)
**Update mode only.** During OmniNX update cleanup (selective deletion), any path listed here with a truthy value is **not** deleted. Paths that would remove a preserved path (parent directory) are skipped as well.
- **File:** `sd:/config/omninx/preserve.ini`
- If missing or invalid, behavior is unchanged (full update deletion list applies).
## Format
Section **`[Preserve]`** (case-sensitive). Each line is **path as key**, **enable flag as value**:
```ini
[Preserve]
'sd:/ROMs'=1
sd:/switch/MyHomebrewFolder=0
sd:/backup/tools=1
```
### Path (key)
- Must start with **`sd:/`** (after optional quoting).
- **Optional** matching **single** or **double** quotes around the key are stripped (helps Ultrahand `set-ini-val` and readability).
- Trailing slashes are normalized away (except the volume root).
- **`sd:/` alone** is rejected (too broad).
- Minimum useful length after `sd:/` is enforced (at least one more character).
### Value (enable)
**On** if the value starts with `1`, `t`, `T`, `y`, or `Y` (e.g. `1`, `true`, `yes`). Anything else is **off** (path ignored).
### Limits
- Up to **96** preserved paths; longer keys truncated internally (~224 bytes per path).
- Duplicate paths after normalization are skipped.
- The list is allocated on the **heap** only while update cleanup runs (not a large static buffer in IPL IRAM), to avoid crowding hardware-adjacent memory used by Joy-Con UART and similar peripherals.
## Overlap rules
For each delete candidate from `deletion_lists_update.h`, deletion is skipped if it **overlaps** any enabled preserve path:
- Same path, or
- Delete target is **inside** a preserved tree (preserved path is a prefix), or
- Delete target is an **ancestor** of a preserved path (deleting it would remove the preserved data).
So preserving `sd:/ROMs` also blocks deleting `sd:/ROMs/title` if that were on the delete list, and blocks deleting `sd:/` if `sd:/ROMs` is preserved.
## Ultrahand
You can toggle entries with:
```text
set-ini-val sd:/config/omninx/preserve.ini Preserve 'sd:/ROMs' 1
```
and `0` to disable. Use the same quoted path in the key argument as in your INI.
## Clean install
**Not used** in clean install mode. `preserve.ini` is only read for **update** cleanup (`update_mode_cleanup`).

328
README.md
View File

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

View File

@@ -1 +1 @@
1.0.0 1.0.1

View File

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

View File

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

View File

@@ -24,17 +24,9 @@ int backup_user_data(void) {
return res; return res;
} }
// Backup DBI if it exists // Backup DBI settings file only (not .nro installers)
if (path_exists("sd:/switch/DBI")) { if (path_exists(DBI_CONFIG_PATH)) {
res = folder_copy("sd:/switch/DBI", TEMP_BACKUP_PATH); res = file_copy(DBI_CONFIG_PATH, TEMP_BACKUP_DBI_CONFIG);
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);
if (res != FR_OK) { if (res != FR_OK) {
return res; return res;
} }
@@ -47,6 +39,35 @@ int backup_user_data(void) {
return res; return res;
} }
} }
// Backup CyberFoil (nro, config, remotes) if installed
if (path_exists(CYBERFOIL_DIR)) {
res = f_mkdir(TEMP_BACKUP_CYBERFOIL);
if (res != FR_OK && res != FR_EXIST) {
return res;
}
if (path_exists(CYBERFOIL_NRO)) {
res = file_copy(CYBERFOIL_NRO, TEMP_BACKUP_CYBERFOIL_NRO);
if (res != FR_OK) {
return res;
}
}
if (path_exists(CYBERFOIL_CONFIG)) {
res = file_copy(CYBERFOIL_CONFIG, TEMP_BACKUP_CYBERFOIL_CONFIG);
if (res != FR_OK) {
return res;
}
}
if (path_exists(CYBERFOIL_REMOTES)) {
res = folder_copy(CYBERFOIL_REMOTES, TEMP_BACKUP_CYBERFOIL);
if (res != FR_OK) {
return res;
}
}
}
return FR_OK; return FR_OK;
} }
@@ -61,25 +82,15 @@ int restore_user_data(void) {
return res; return res;
} }
// Restore DBI if backup exists // Restore DBI settings if backed up
if (path_exists("sd:/temp_backup/DBI")) { if (path_exists(TEMP_BACKUP_DBI_CONFIG)) {
res = folder_copy("sd:/temp_backup/DBI", "sd:/switch"); res = f_mkdir("sd:/switch/DBI");
if (res == FR_OK) { if (res != FR_OK && res != FR_EXIST) {
// Delete old DBI .nro files return res;
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");
} }
} res = file_copy(TEMP_BACKUP_DBI_CONFIG, DBI_CONFIG_PATH);
if (res != FR_OK) {
// Restore Tinfoil if backup exists return res;
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");
} }
} }
@@ -90,6 +101,35 @@ int restore_user_data(void) {
return res; return res;
} }
} }
// Restore CyberFoil if backed up
if (path_exists(TEMP_BACKUP_CYBERFOIL)) {
res = f_mkdir(CYBERFOIL_DIR);
if (res != FR_OK && res != FR_EXIST) {
return res;
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_NRO)) {
res = file_copy(TEMP_BACKUP_CYBERFOIL_NRO, CYBERFOIL_NRO);
if (res != FR_OK) {
return res;
}
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_CONFIG)) {
res = file_copy(TEMP_BACKUP_CYBERFOIL_CONFIG, CYBERFOIL_CONFIG);
if (res != FR_OK) {
return res;
}
}
if (path_exists(TEMP_BACKUP_CYBERFOIL_REMOTES)) {
res = folder_copy(TEMP_BACKUP_CYBERFOIL_REMOTES, CYBERFOIL_DIR);
if (res != FR_OK) {
return res;
}
}
}
return FR_OK; return FR_OK;
} }
@@ -101,3 +141,34 @@ int cleanup_backup(void) {
} }
return FR_OK; 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,27 @@
#include <utils/types.h> #include <utils/types.h>
#define TEMP_BACKUP_PATH "sd:/temp_backup" #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 /** CyberFoil homebrew; preserved across clean install (nro, config, remotes only). */
#define CYBERFOIL_DIR "sd:/switch/CyberFoil"
#define CYBERFOIL_NRO CYBERFOIL_DIR "/cyberfoil.nro"
#define CYBERFOIL_CONFIG CYBERFOIL_DIR "/config.json"
#define CYBERFOIL_REMOTES CYBERFOIL_DIR "/remotes"
#define TEMP_BACKUP_CYBERFOIL TEMP_BACKUP_PATH "/CyberFoil"
#define TEMP_BACKUP_CYBERFOIL_NRO TEMP_BACKUP_CYBERFOIL "/cyberfoil.nro"
#define TEMP_BACKUP_CYBERFOIL_CONFIG TEMP_BACKUP_CYBERFOIL "/config.json"
#define TEMP_BACKUP_CYBERFOIL_REMOTES TEMP_BACKUP_CYBERFOIL "/remotes"
/** Live HorizonOC settings (Overclock tool). */
#define HORIZON_OC_CONFIG_PATH "sd:/config/horizon-oc/config.ini"
/** 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 (CyberFoil, DBI dbi.config, prod.keys) before clean install
int backup_user_data(void); int backup_user_data(void);
// Restore user data after clean install // Restore user data after clean install
@@ -16,3 +35,10 @@ int restore_user_data(void);
// Clean up temporary backup directory // Clean up temporary backup directory
int cleanup_backup(void); 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,295 +1,133 @@
/* /*
* OmniNX Installer - Deletion Lists for Clean Install Mode * OmniNX Installer - Deletion Lists for Clean Install Mode
* Selective deletion when no OmniNX install was found. * Deletion policy aligned with NiklasCFW pack clean install (TegraExplorer script):
* Only listed paths are removed; does not wipe whole card or user configs. * full sd:/atmosphere, bootloader/config subsets, sd:/switch, root + misc, then
*/ * post-restore paths (e.g. tinfoil db). CyberFoil/DBI/prod.keys backup is in backup.c.
*/
#pragma once
#pragma once
// Atmosphere subdirectories to delete
static const char* clean_atmosphere_dirs_to_delete[] = { // Entire atmosphere/ tree (NiklasCFW: deldir("sd:/atmosphere"))
"sd:/atmosphere/config", static const char* clean_atmosphere_dirs_to_delete[] = {
"sd:/atmosphere/crash_reports", "sd:/atmosphere",
"sd:/atmosphere/erpt_reports", NULL
"sd:/atmosphere/exefs_patches/CrunchPatch", };
"sd:/atmosphere/exefs_patches/Crunchyroll Patch 1.10.0",
"sd:/atmosphere/exefs_patches/bluetooth_patches", static const char* clean_atmosphere_contents_dirs_to_delete[] = {
"sd:/atmosphere/exefs_patches/bootlogo", NULL
"sd:/atmosphere/exefs_patches/btm_patches", };
"sd:/atmosphere/exefs_patches/es_patches",
"sd:/atmosphere/exefs_patches/hid_patches", static const char* clean_atmosphere_files_to_delete[] = {
"sd:/atmosphere/exefs_patches/logo1", NULL
"sd:/atmosphere/exefs_patches/nfim_ctest", };
"sd:/atmosphere/exefs_patches/nim_ctest",
"sd:/atmosphere/exefs_patches/nvnflinger_cmu", // Bootloader directories to delete
"sd:/atmosphere/extrazz", static const char* clean_bootloader_dirs_to_delete[] = {
"sd:/atmosphere/fatal_errors", "sd:/bootloader/boot",
"sd:/atmosphere/fatal_reports", "sd:/bootloader/bootlogo",
"sd:/atmosphere/flags", "sd:/bootloader/ini2",
"sd:/atmosphere/hbl_html", "sd:/bootloader/payloads",
"sd:/atmosphere/hosts", "sd:/bootloader/reboot",
"sd:/atmosphere/kips", "sd:/bootloader/res",
"sd:/atmosphere/kip1", "sd:/bootloader/sys",
"sd:/atmosphere/kip_patches", NULL
"sd:/atmosphere/logs", };
NULL
}; // Bootloader files to delete
static const char* clean_bootloader_files_to_delete[] = {
// Atmosphere contents directories (title IDs; only under atmosphere/contents/) "sd:/bootloader/ArgonNX.bin",
static const char* clean_atmosphere_contents_dirs_to_delete[] = { "sd:/bootloader/bootlogo.bmp",
"sd:/atmosphere/contents/0000000000534C56", "sd:/bootloader/hekate_ipl.ini",
"sd:/atmosphere/contents/00FF0000B378D640", "sd:/bootloader/patches.ini",
"sd:/atmosphere/contents/00FF0000636C6BFF", "sd:/bootloader/update.bin",
"sd:/atmosphere/contents/00FF0000A53BB665", "sd:/bootloader/ini/EmuMMC ohne Mods.ini",
"sd:/atmosphere/contents/0100000000000008", NULL
"sd:/atmosphere/contents/010000000000000D", };
"sd:/atmosphere/contents/010000000000002B",
"sd:/atmosphere/contents/0100000000000032", // Config directories to delete
"sd:/atmosphere/contents/0100000000000034", static const char* clean_config_dirs_to_delete[] = {
"sd:/atmosphere/contents/0100000000000036", "sd:/config/aio-switch-updater",
"sd:/atmosphere/contents/0100000000000037", "sd:/config/blue_pack_updater",
"sd:/atmosphere/contents/010000000000003C", "sd:/config/kefir-updater",
"sd:/atmosphere/contents/0100000000000042", "sd:/config/nx-hbmenu",
"sd:/atmosphere/contents/0100000000000895", "sd:/config/sys-con",
"sd:/atmosphere/contents/0100000000000F12", "sd:/config/sys-patch",
"sd:/atmosphere/contents/0100000000001000", "sd:/config/uberhand",
"sd:/atmosphere/contents/0100000000001007", "sd:/config/ultrahand",
"sd:/atmosphere/contents/0100000000001013", NULL
"sd:/atmosphere/contents/010000000000DA7A", };
"sd:/atmosphere/contents/010000000000bd00",
"sd:/atmosphere/contents/01006a800016e000", // Entire switch/ tree (clean install recreates sd:/switch after wipe)
"sd:/atmosphere/contents/01009D901BC56000", static const char* clean_switch_dirs_to_delete[] = {
"sd:/atmosphere/contents/0100A3900C3E2000", "sd:/switch",
"sd:/atmosphere/contents/0100F43008C44000", NULL
"sd:/atmosphere/contents/050000BADDAD0000", };
"sd:/atmosphere/contents/4200000000000000",
"sd:/atmosphere/contents/420000000000000B", static const char* clean_switch_files_to_delete[] = {
"sd:/atmosphere/contents/420000000000000E", NULL
"sd:/atmosphere/contents/4200000000000010", };
"sd:/atmosphere/contents/4200000000000FFF",
"sd:/atmosphere/contents/420000000007E51A", // Root CFW files to delete
"sd:/atmosphere/contents/420000000007E51B", static const char* clean_root_files_to_delete[] = {
"sd:/atmosphere/contents/690000000000000D", "sd:/boot.dat",
NULL "sd:/boot.ini",
}; "sd:/exosphere.bin",
"sd:/exosphere.ini",
// Atmosphere files to delete "sd:/hbmenu.nro",
static const char* clean_atmosphere_files_to_delete[] = { "sd:/install.bat",
"sd:/atmosphere/config/exosphere.ini", "sd:/license",
"sd:/atmosphere/config/stratosphere.ini", "sd:/loader.bin",
"sd:/atmosphere/hbl.nsp", "sd:/mc-mitm.log",
"sd:/atmosphere/package3", "sd:/payload.bin",
"sd:/atmosphere/reboot_payload.bin", "sd:/update.bin",
"sd:/atmosphere/stratosphere.romfs", "sd:/version",
NULL NULL
}; };
// Bootloader directories to delete // Miscellaneous directories to delete
static const char* clean_bootloader_dirs_to_delete[] = { static const char* clean_misc_dirs_to_delete[] = {
"sd:/bootloader/boot", "sd:/argon",
"sd:/bootloader/bootlogo", "sd:/games",
"sd:/bootloader/ini2", "sd:/NSPs (Tools)",
"sd:/bootloader/payloads", "sd:/Patched Apps",
"sd:/bootloader/reboot", "sd:/SaltySD/flags",
"sd:/bootloader/res", "sd:/SaltySD/patches",
"sd:/bootloader/sys", "sd:/scripts",
NULL "sd:/TegraExplorer",
}; "sd:/themes/systemData",
"sd:/tools",
// Bootloader files to delete "sd:/warmboot_mariko",
static const char* clean_bootloader_files_to_delete[] = { NULL
"sd:/bootloader/ArgonNX.bin", };
"sd:/bootloader/bootlogo.bmp",
"sd:/bootloader/hekate_ipl.ini", // Miscellaneous files to delete
"sd:/bootloader/nyx.ini", static const char* clean_misc_files_to_delete[] = {
"sd:/bootloader/patches.ini", "sd:/fusee-primary.bin",
"sd:/bootloader/update.bin", "sd:/fusee.bin",
"sd:/bootloader/ini/EmuMMC ohne Mods.ini", "sd:/SaltySD/exceptions.txt",
NULL "sd:/SaltySD/saltysd_bootstrap.elf",
}; "sd:/SaltySD/saltysd_bootstrap32_3k.elf",
"sd:/SaltySD/saltysd_bootstrap32_5k.elf",
// Config directories to delete "sd:/SaltySD/saltysd_core.elf",
static const char* clean_config_dirs_to_delete[] = { "sd:/SaltySD/saltysd_core32.elf",
"sd:/config/aio-switch-updater", NULL
"sd:/config/blue_pack_updater", };
"sd:/config/kefir-updater",
"sd:/config/nx-hbmenu", // After DBI/prod.keys restore (NiklasCFW: deldir("sd:/switch/tinfoil/db"))
"sd:/config/quickntp", static const char* clean_post_restore_dirs_to_delete[] = {
"sd:/config/sys-con", "sd:/switch/tinfoil/db",
"sd:/config/sys-patch", NULL
"sd:/config/uberhand", };
"sd:/config/ultrahand",
NULL // Old version marker files to delete (clean install only)
}; static const char* old_version_files_to_delete[] = {
"sd:/1.4.0-pre",
// Switch directories to delete "sd:/1.4.0-pre-c",
static const char* clean_switch_dirs_to_delete[] = { "sd:/1.4.0-pre-d",
"sd:/switch/.overlays", "sd:/1.4.1",
"sd:/switch/.packages", "sd:/1.5.0",
"sd:/switch/90DNS_tester", "sd:/1.6.0",
"sd:/switch/aio-switch-updater", "sd:/1.6.1",
"sd:/switch/amsPLUS-downloader", "sd:/1.6.2",
"sd:/switch/appstore", NULL
"sd:/switch/AtmoXL-Titel-Installer", };
"sd:/switch/breeze",
"sd:/switch/checkpoint",
"sd:/switch/cheats-updater",
"sd:/switch/chiaki",
"sd:/switch/ChoiDujourNX",
"sd:/switch/crash_ams",
"sd:/switch/Daybreak",
"sd:/switch/DNS_mitm Tester",
"sd:/switch/EdiZon",
"sd:/switch/Fizeau",
"sd:/switch/FTPD",
"sd:/switch/fw-downloader",
"sd:/switch/gamecard_installer",
"sd:/switch/Goldleaf",
"sd:/switch/haze",
"sd:/switch/JKSV",
"sd:/switch/kefir-updater",
"sd:/switch/ldnmitm_config",
"sd:/switch/Linkalho",
"sd:/switch/Moonlight-Switch",
"sd:/switch/Neumann",
"sd:/switch/NX-Activity-Log",
"sd:/switch/NX-Save-Sync",
"sd:/switch/NX-Shell",
"sd:/switch/NX-Update-Checker ",
"sd:/switch/NXGallery",
"sd:/switch/NXRemoteLauncher",
"sd:/switch/NXThemesInstaller",
"sd:/switch/nxdumptool",
"sd:/switch/nxmtp",
"sd:/switch/Payload_launcher",
"sd:/switch/Reboot",
"sd:/switch/reboot_to_argonNX",
"sd:/switch/reboot_to_hekate",
"sd:/switch/Shutdown_System",
"sd:/switch/SimpleModDownloader",
"sd:/switch/SimpleModManager",
"sd:/switch/sphaira",
"sd:/switch/studious-pancake",
"sd:/switch/Switch-Time",
"sd:/switch/SwitchIdent",
"sd:/switch/Switch_themes_Installer",
"sd:/switch/Switchfin",
"sd:/switch/Sys-Clk Manager",
"sd:/switch/Sys-Con",
"sd:/switch/sys-clk-manager",
"sd:/switch/themezer-nx",
"sd:/switch/themezernx",
"sd:/switch/tinwoo",
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/DBI/DBI_EN.nro",
"sd:/switch/DBI_DE/DBI_DE.nro",
"sd:/switch/DNS_mitm Tester.nro",
"sd:/switch/EdiZon.nro",
"sd:/switch/Fizeau.nro",
"sd:/switch/Goldleaf.nro",
"sd:/switch/haze.nro",
"sd:/switch/JKSV.nro",
"sd:/switch/ldnmitm_config.nro",
"sd:/switch/linkalho.nro",
"sd:/switch/Moonlight-Switch.nro",
"sd:/switch/Neumann.nro",
"sd:/switch/NX-Shell.nro",
"sd:/switch/NXGallery.nro",
"sd:/switch/NXThemesInstaller.nro",
"sd:/switch/nxdumptool.nro",
"sd:/switch/nxtc.bin",
"sd:/switch/reboot_to_payload.nro",
"sd:/switch/SimpleModDownloader.nro",
"sd:/switch/SimpleModManager.nro",
"sd:/switch/sphaira.nro",
"sd:/switch/SwitchIdent.nro",
"sd:/switch/Switch_themes_Installer/NXThemesInstaller.nro",
"sd:/switch/Switchfin.nro",
"sd:/switch/Sys-Clk Manager/sys-clk-manager.nro",
"sd:/switch/Sys-Con.nro",
"sd:/switch/sys-clk-manager.nro",
"sd:/switch/tinfoil.nro",
"sd:/switch/tinfoil/tinfoil.nro",
"sd:/switch/tinwoo.nro",
"sd:/switch/tinwoo/tinwoo.nro",
NULL
};
// 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
};

View File

@@ -10,14 +10,16 @@ static const char* atmosphere_dirs_to_delete[] = {
"sd:/atmosphere/config", "sd:/atmosphere/config",
"sd:/atmosphere/crash_reports", "sd:/atmosphere/crash_reports",
"sd:/atmosphere/erpt_reports", "sd:/atmosphere/erpt_reports",
"sd:/atmosphere/exefs_patches/audio_mastervolume",
"sd:/atmosphere/exefs_patches/CrunchPatch", "sd:/atmosphere/exefs_patches/CrunchPatch",
"sd:/atmosphere/exefs_patches/SaltyNX_Fixes",
"sd:/atmosphere/exefs_patches/Crunchyroll Patch 1.10.0", "sd:/atmosphere/exefs_patches/Crunchyroll Patch 1.10.0",
"sd:/atmosphere/exefs_patches/bluetooth_patches", "sd:/atmosphere/exefs_patches/bluetooth_patches",
"sd:/atmosphere/exefs_patches/bootlogo", "sd:/atmosphere/exefs_patches/bootlogo",
"sd:/atmosphere/exefs_patches/btm_patches", "sd:/atmosphere/exefs_patches/btm_patches",
"sd:/atmosphere/exefs_patches/es_patches", "sd:/atmosphere/exefs_patches/es_patches",
"sd:/atmosphere/exefs_patches/hid_patches", "sd:/atmosphere/exefs_patches/hid_patches",
"sd:/atmosphere/exefs_patches/logo1", "sd:/atmosphere/exefs_patches/logo",
"sd:/atmosphere/exefs_patches/nfim_ctest", "sd:/atmosphere/exefs_patches/nfim_ctest",
"sd:/atmosphere/exefs_patches/nim_ctest", "sd:/atmosphere/exefs_patches/nim_ctest",
"sd:/atmosphere/exefs_patches/nvnflinger_cmu", "sd:/atmosphere/exefs_patches/nvnflinger_cmu",
@@ -80,6 +82,8 @@ static const char* atmosphere_files_to_delete[] = {
"sd:/atmosphere/package3", "sd:/atmosphere/package3",
"sd:/atmosphere/reboot_payload.bin", "sd:/atmosphere/reboot_payload.bin",
"sd:/atmosphere/stratosphere.romfs", "sd:/atmosphere/stratosphere.romfs",
"sd:/atmosphere/kips/loader.kip",
"sd:/atmosphere/kips/hoc.kip",
NULL NULL
}; };
@@ -122,14 +126,15 @@ static const char* config_dirs_to_delete[] = {
}; };
// Switch directories to delete // Switch directories to delete
// NOTE: .packages is intentionally excluded - UltraHand package cache, preserve during updates
static const char* switch_dirs_to_delete[] = { static const char* switch_dirs_to_delete[] = {
"sd:/switch/.packages",
"sd:/switch/.overlays", "sd:/switch/.overlays",
"sd:/switch/90DNS_tester", "sd:/switch/90DNS_tester",
"sd:/switch/aio-switch-updater", "sd:/switch/aio-switch-updater",
"sd:/switch/amsPLUS-downloader", "sd:/switch/amsPLUS-downloader",
"sd:/switch/appstore", "sd:/switch/appstore",
"sd:/switch/AtmoXL-Titel-Installer", "sd:/switch/AtmoXL-Titel-Installer",
"sd:/switch/Benchmark-Toolbox",
"sd:/switch/breeze", "sd:/switch/breeze",
"sd:/switch/checkpoint", "sd:/switch/checkpoint",
"sd:/switch/cheats-updater", "sd:/switch/cheats-updater",
@@ -137,10 +142,12 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/ChoiDujourNX", "sd:/switch/ChoiDujourNX",
"sd:/switch/crash_ams", "sd:/switch/crash_ams",
"sd:/switch/Daybreak", "sd:/switch/Daybreak",
"sd:/switch/DNS-Block_Tester",
"sd:/switch/DNS_mitm Tester", "sd:/switch/DNS_mitm Tester",
"sd:/switch/EdiZon", "sd:/switch/EdiZon",
"sd:/switch/Fizeau", "sd:/switch/Fizeau",
"sd:/switch/FTPD", "sd:/switch/FTPD",
"sd:/switch/Furmark-NX",
"sd:/switch/fw-downloader", "sd:/switch/fw-downloader",
"sd:/switch/gamecard_installer", "sd:/switch/gamecard_installer",
"sd:/switch/Goldleaf", "sd:/switch/Goldleaf",
@@ -165,6 +172,7 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/reboot_to_argonNX", "sd:/switch/reboot_to_argonNX",
"sd:/switch/reboot_to_hekate", "sd:/switch/reboot_to_hekate",
"sd:/switch/Shutdown_System", "sd:/switch/Shutdown_System",
"sd:/switch/SimpleModAlchemist",
"sd:/switch/SimpleModDownloader", "sd:/switch/SimpleModDownloader",
"sd:/switch/SimpleModManager", "sd:/switch/SimpleModManager",
"sd:/switch/sphaira", "sd:/switch/sphaira",
@@ -172,19 +180,22 @@ static const char* switch_dirs_to_delete[] = {
"sd:/switch/Switch-Time", "sd:/switch/Switch-Time",
"sd:/switch/SwitchIdent", "sd:/switch/SwitchIdent",
"sd:/switch/Switch_themes_Installer", "sd:/switch/Switch_themes_Installer",
"sd:/switch/Switchfin", "sd:/switch/swr-ini-tool",
"sd:/switch/Sys-Clk Manager", "sd:/switch/Sys-Clk Manager",
"sd:/switch/Sys-Con", "sd:/switch/Sys-Con",
"sd:/switch/sys-clk-manager", "sd:/switch/sys-clk-manager",
"sd:/switch/themezer-nx", "sd:/switch/themezer-nx",
"sd:/switch/ThemezerNX",
"sd:/switch/themezernx", "sd:/switch/themezernx",
"sd:/switch/tinwoo", "sd:/switch/tinwoo",
"sd:/switch/Ultrahand-Reload",
NULL NULL
}; };
// Switch files (NRO) to delete // Switch files (NRO) to delete
static const char* switch_files_to_delete[] = { static const char* switch_files_to_delete[] = {
"sd:/switch/90DNS_tester/90DNS_tester.nro", "sd:/switch/90DNS_tester/90DNS_tester.nro",
"sd:/switch/Benchmark-Toolbox/Benchmark-Toolbox.nro",
"sd:/switch/breeze.nro", "sd:/switch/breeze.nro",
"sd:/switch/cheats-updater.nro", "sd:/switch/cheats-updater.nro",
"sd:/switch/chiaki.nro", "sd:/switch/chiaki.nro",
@@ -204,9 +215,11 @@ static const char* switch_files_to_delete[] = {
"sd:/switch/DBI_RU/DBI_RU.nro", "sd:/switch/DBI_RU/DBI_RU.nro",
"sd:/switch/DBI/DBI_EN.nro", "sd:/switch/DBI/DBI_EN.nro",
"sd:/switch/DBI_DE/DBI_DE.nro", "sd:/switch/DBI_DE/DBI_DE.nro",
"sd:/switch/DNS-Block_Tester/dns_tester.nro",
"sd:/switch/DNS_mitm Tester.nro", "sd:/switch/DNS_mitm Tester.nro",
"sd:/switch/EdiZon.nro", "sd:/switch/EdiZon.nro",
"sd:/switch/Fizeau.nro", "sd:/switch/Fizeau.nro",
"sd:/switch/Furmark-NX/Furmark-NX.nro",
"sd:/switch/Goldleaf.nro", "sd:/switch/Goldleaf.nro",
"sd:/switch/haze.nro", "sd:/switch/haze.nro",
"sd:/switch/JKSV.nro", "sd:/switch/JKSV.nro",
@@ -220,19 +233,22 @@ static const char* switch_files_to_delete[] = {
"sd:/switch/nxdumptool.nro", "sd:/switch/nxdumptool.nro",
"sd:/switch/nxtc.bin", "sd:/switch/nxtc.bin",
"sd:/switch/reboot_to_payload.nro", "sd:/switch/reboot_to_payload.nro",
"sd:/switch/SimpleModAlchemist/Simple_Mod_Alchemist.nro",
"sd:/switch/SimpleModDownloader.nro", "sd:/switch/SimpleModDownloader.nro",
"sd:/switch/SimpleModManager.nro", "sd:/switch/SimpleModManager.nro",
"sd:/switch/sphaira.nro", "sd:/switch/sphaira.nro",
"sd:/switch/swr-ini-tool/swr-ini-tool.nro",
"sd:/switch/SwitchIdent.nro", "sd:/switch/SwitchIdent.nro",
"sd:/switch/Switch_themes_Installer/NXThemesInstaller.nro", "sd:/switch/Switch_themes_Installer/NXThemesInstaller.nro",
"sd:/switch/Switchfin.nro",
"sd:/switch/Sys-Clk Manager/sys-clk-manager.nro", "sd:/switch/Sys-Clk Manager/sys-clk-manager.nro",
"sd:/switch/Sys-Con.nro", "sd:/switch/Sys-Con.nro",
"sd:/switch/sys-clk-manager.nro", "sd:/switch/sys-clk-manager.nro",
"sd:/switch/ThemezerNX/themezer-nx.nro",
"sd:/switch/tinfoil.nro", "sd:/switch/tinfoil.nro",
"sd:/switch/tinfoil/tinfoil.nro", "sd:/switch/tinfoil/tinfoil.nro",
"sd:/switch/tinwoo.nro", "sd:/switch/tinwoo.nro",
"sd:/switch/tinwoo/tinwoo.nro", "sd:/switch/tinwoo/tinwoo.nro",
"sd:/switch/Ultrahand-Reload/Ultrahand-Reload.nro",
NULL NULL
}; };
@@ -262,6 +278,7 @@ static const char* misc_dirs_to_delete[] = {
"sd:/SaltySD/flags", "sd:/SaltySD/flags",
"sd:/scripts", "sd:/scripts",
"sd:/switch/tinfoil/db", "sd:/switch/tinfoil/db",
"sd:/themes/systemData",
"sd:/tools", "sd:/tools",
"sd:/warmboot_mariko", "sd:/warmboot_mariko",
NULL 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,351 +1,351 @@
/* /*
* OmniNX Installer - Filesystem operations with file logging * OmniNX Installer - Filesystem operations with file logging
* Based on HATS Installer * Based on HATS Installer
*/ */
#include "fs.h" #include "fs.h"
#include <libs/fatfs/ff.h> #include <libs/fatfs/ff.h>
#include <mem/heap.h> #include <mem/heap.h>
#include <string.h> #include <string.h>
#include <utils/sprintf.h> #include <utils/sprintf.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer #define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer
#define LOG_BUFFER_SIZE 512 #define LOG_BUFFER_SIZE 512
// Log file handle // Log file handle
static FIL log_file; static FIL log_file;
static bool log_enabled = false; static bool log_enabled = false;
static char log_buf[LOG_BUFFER_SIZE]; static char log_buf[LOG_BUFFER_SIZE];
// Initialize log file // Initialize log file
void log_init(const char *path) { void log_init(const char *path) {
int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS); int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS);
if (res == FR_OK) { if (res == FR_OK) {
log_enabled = true; log_enabled = true;
log_write("=== OmniNX Installer Log ===\n\n"); log_write("=== OmniNX Installer Log ===\n\n");
} }
} }
// Close log file // Close log file
void log_close(void) { void log_close(void) {
if (log_enabled) { if (log_enabled) {
f_sync(&log_file); f_sync(&log_file);
f_close(&log_file); f_close(&log_file);
log_enabled = false; log_enabled = false;
} }
} }
// Write to log file (variadic version) // Write to log file (variadic version)
void log_write(const char *fmt, ...) { void log_write(const char *fmt, ...) {
if (!log_enabled) return; if (!log_enabled) return;
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
// Format the message // Format the message
vsnprintf(log_buf, LOG_BUFFER_SIZE, fmt, args); vsnprintf(log_buf, LOG_BUFFER_SIZE, fmt, args);
va_end(args); va_end(args);
// Write to file // Write to file
UINT bw; UINT bw;
f_write(&log_file, log_buf, strlen(log_buf), &bw); f_write(&log_file, log_buf, strlen(log_buf), &bw);
f_sync(&log_file); // Flush to ensure data is written f_sync(&log_file); // Flush to ensure data is written
} }
// Convert FatFS error code to string // Convert FatFS error code to string
const char *fs_error_str(int err) { const char *fs_error_str(int err) {
switch (err) { switch (err) {
case FR_OK: return "OK"; case FR_OK: return "OK";
case FR_DISK_ERR: return "DISK_ERR: Low level disk error"; case FR_DISK_ERR: return "DISK_ERR: Low level disk error";
case FR_INT_ERR: return "INT_ERR: Internal error"; case FR_INT_ERR: return "INT_ERR: Internal error";
case FR_NOT_READY: return "NOT_READY: Drive not ready"; case FR_NOT_READY: return "NOT_READY: Drive not ready";
case FR_NO_FILE: return "NO_FILE: File not found"; case FR_NO_FILE: return "NO_FILE: File not found";
case FR_NO_PATH: return "NO_PATH: Path not found"; case FR_NO_PATH: return "NO_PATH: Path not found";
case FR_INVALID_NAME: return "INVALID_NAME: Invalid path name"; case FR_INVALID_NAME: return "INVALID_NAME: Invalid path name";
case FR_DENIED: return "DENIED: Access denied"; case FR_DENIED: return "DENIED: Access denied";
case FR_EXIST: return "EXIST: Already exists"; case FR_EXIST: return "EXIST: Already exists";
case FR_INVALID_OBJECT: return "INVALID_OBJECT: Invalid object"; case FR_INVALID_OBJECT: return "INVALID_OBJECT: Invalid object";
case FR_WRITE_PROTECTED: return "WRITE_PROTECTED: Write protected"; case FR_WRITE_PROTECTED: return "WRITE_PROTECTED: Write protected";
case FR_INVALID_DRIVE: return "INVALID_DRIVE: Invalid drive"; case FR_INVALID_DRIVE: return "INVALID_DRIVE: Invalid drive";
case FR_NOT_ENABLED: return "NOT_ENABLED: Volume not mounted"; case FR_NOT_ENABLED: return "NOT_ENABLED: Volume not mounted";
case FR_NO_FILESYSTEM: return "NO_FILESYSTEM: No valid FAT"; case FR_NO_FILESYSTEM: return "NO_FILESYSTEM: No valid FAT";
case FR_MKFS_ABORTED: return "MKFS_ABORTED: mkfs aborted"; case FR_MKFS_ABORTED: return "MKFS_ABORTED: mkfs aborted";
case FR_TIMEOUT: return "TIMEOUT: Timeout"; case FR_TIMEOUT: return "TIMEOUT: Timeout";
case FR_LOCKED: return "LOCKED: File locked"; case FR_LOCKED: return "LOCKED: File locked";
case FR_NOT_ENOUGH_CORE: return "NOT_ENOUGH_CORE: Out of memory"; 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_TOO_MANY_OPEN_FILES: return "TOO_MANY_OPEN_FILES";
case FR_INVALID_PARAMETER: return "INVALID_PARAMETER"; case FR_INVALID_PARAMETER: return "INVALID_PARAMETER";
default: return "UNKNOWN_ERROR"; default: return "UNKNOWN_ERROR";
} }
} }
// Combine two paths // Combine two paths
static char *combine_paths(const char *base, const char *add) { static char *combine_paths(const char *base, const char *add) {
size_t base_len = strlen(base); size_t base_len = strlen(base);
size_t add_len = strlen(add); size_t add_len = strlen(add);
char *result = malloc(base_len + add_len + 2); char *result = malloc(base_len + add_len + 2);
if (!result) return NULL; if (!result) return NULL;
if (base_len > 0 && base[base_len - 1] == '/') { if (base_len > 0 && base[base_len - 1] == '/') {
s_printf(result, "%s%s", base, add); s_printf(result, "%s%s", base, add);
} else { } else {
s_printf(result, "%s/%s", base, add); s_printf(result, "%s/%s", base, add);
} }
return result; return result;
} }
// Copy a single file with logging // Copy a single file with logging
int file_copy(const char *src, const char *dst) { int file_copy(const char *src, const char *dst) {
FIL fin, fout; FIL fin, fout;
FILINFO fno; FILINFO fno;
int res; int res;
log_write("COPY: %s -> %s\n", src, dst); log_write("COPY: %s -> %s\n", src, dst);
res = f_open(&fin, src, FA_READ | FA_OPEN_EXISTING); res = f_open(&fin, src, FA_READ | FA_OPEN_EXISTING);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR open src: %s\n", fs_error_str(res)); log_write(" ERROR open src: %s\n", fs_error_str(res));
return res; return res;
} }
f_stat(src, &fno); f_stat(src, &fno);
u64 file_size = f_size(&fin); u64 file_size = f_size(&fin);
log_write(" Size: %d bytes\n", (u32)file_size); log_write(" Size: %d bytes\n", (u32)file_size);
res = f_open(&fout, dst, FA_WRITE | FA_CREATE_ALWAYS); res = f_open(&fout, dst, FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) { if (res != FR_OK) {
f_close(&fin); f_close(&fin);
log_write(" ERROR open dst: %s\n", fs_error_str(res)); log_write(" ERROR open dst: %s\n", fs_error_str(res));
return res; return res;
} }
u8 *buf = malloc(FS_BUFFER_SIZE); u8 *buf = malloc(FS_BUFFER_SIZE);
if (!buf) { if (!buf) {
f_close(&fin); f_close(&fin);
f_close(&fout); f_close(&fout);
log_write(" ERROR: Out of memory for buffer\n"); log_write(" ERROR: Out of memory for buffer\n");
return FR_NOT_ENOUGH_CORE; return FR_NOT_ENOUGH_CORE;
} }
u64 remaining = file_size; u64 remaining = file_size;
UINT br, bw; UINT br, bw;
while (remaining > 0) { while (remaining > 0) {
UINT to_copy = (remaining > FS_BUFFER_SIZE) ? FS_BUFFER_SIZE : (UINT)remaining; UINT to_copy = (remaining > FS_BUFFER_SIZE) ? FS_BUFFER_SIZE : (UINT)remaining;
res = f_read(&fin, buf, to_copy, &br); res = f_read(&fin, buf, to_copy, &br);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR read: %s\n", fs_error_str(res)); log_write(" ERROR read: %s\n", fs_error_str(res));
break; break;
} }
if (br != to_copy) { if (br != to_copy) {
log_write(" ERROR: Read %d bytes, expected %d\n", br, to_copy); log_write(" ERROR: Read %d bytes, expected %d\n", br, to_copy);
res = FR_DISK_ERR; res = FR_DISK_ERR;
break; break;
} }
res = f_write(&fout, buf, to_copy, &bw); res = f_write(&fout, buf, to_copy, &bw);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR write: %s\n", fs_error_str(res)); log_write(" ERROR write: %s\n", fs_error_str(res));
break; break;
} }
if (bw != to_copy) { if (bw != to_copy) {
log_write(" ERROR: Wrote %d bytes, expected %d\n", bw, to_copy); log_write(" ERROR: Wrote %d bytes, expected %d\n", bw, to_copy);
res = FR_DISK_ERR; res = FR_DISK_ERR;
break; break;
} }
remaining -= to_copy; remaining -= to_copy;
} }
free(buf); free(buf);
f_close(&fin); f_close(&fin);
f_close(&fout); f_close(&fout);
if (res == FR_OK) { if (res == FR_OK) {
f_chmod(dst, fno.fattrib, 0x3A); f_chmod(dst, fno.fattrib, 0x3A);
log_write(" OK\n"); log_write(" OK\n");
} }
return res; return res;
} }
// Recursively delete a folder with logging // Recursively delete a folder with logging
int folder_delete(const char *path) { int folder_delete(const char *path) {
DIR dir; DIR dir;
FILINFO fno; FILINFO fno;
int res; int res;
log_write("DELETE: %s\n", path); log_write("DELETE: %s\n", path);
res = f_opendir(&dir, path); res = f_opendir(&dir, path);
if (res != FR_OK) { if (res != FR_OK) {
// Maybe it's a file, try to delete it // Maybe it's a file, try to delete it
log_write(" Not a dir, trying as file...\n"); log_write(" Not a dir, trying as file...\n");
res = f_unlink(path); res = f_unlink(path);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR unlink: %s\n", fs_error_str(res)); log_write(" ERROR unlink: %s\n", fs_error_str(res));
} else { } else {
log_write(" OK (file deleted)\n"); log_write(" OK (file deleted)\n");
} }
return res; return res;
} }
int file_count = 0; int file_count = 0;
int dir_count = 0; int dir_count = 0;
while (1) { while (1) {
res = f_readdir(&dir, &fno); res = f_readdir(&dir, &fno);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res)); log_write(" ERROR readdir: %s\n", fs_error_str(res));
break; break;
} }
if (fno.fname[0] == 0) break; // End of directory if (fno.fname[0] == 0) break; // End of directory
char *full_path = combine_paths(path, fno.fname); char *full_path = combine_paths(path, fno.fname);
if (!full_path) { if (!full_path) {
res = FR_NOT_ENOUGH_CORE; res = FR_NOT_ENOUGH_CORE;
break; break;
} }
if (fno.fattrib & AM_DIR) { if (fno.fattrib & AM_DIR) {
dir_count++; dir_count++;
res = folder_delete(full_path); res = folder_delete(full_path);
} else { } else {
file_count++; file_count++;
log_write(" DEL: %s\n", fno.fname); log_write(" DEL: %s\n", fno.fname);
// Clear read-only attribute if set (common with some CFW packs) // Clear read-only attribute if set (common with some CFW packs)
if (fno.fattrib & AM_RDO) { if (fno.fattrib & AM_RDO) {
f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO); f_chmod(full_path, fno.fattrib & ~AM_RDO, AM_RDO);
} }
res = f_unlink(full_path); res = f_unlink(full_path);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR: %s\n", fs_error_str(res)); log_write(" ERROR: %s\n", fs_error_str(res));
} }
} }
free(full_path); free(full_path);
if (res != FR_OK) break; if (res != FR_OK) break;
} }
f_closedir(&dir); f_closedir(&dir);
if (res == FR_OK || res == FR_NO_FILE) { if (res == FR_OK || res == FR_NO_FILE) {
log_write(" Removing dir: %s (%d files, %d subdirs)\n", path, file_count, dir_count); 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 // Check and clear read-only attribute on directory if set
FILINFO dir_info; FILINFO dir_info;
if (f_stat(path, &dir_info) == FR_OK && (dir_info.fattrib & AM_RDO)) { if (f_stat(path, &dir_info) == FR_OK && (dir_info.fattrib & AM_RDO)) {
f_chmod(path, dir_info.fattrib & ~AM_RDO, AM_RDO); f_chmod(path, dir_info.fattrib & ~AM_RDO, AM_RDO);
} }
res = f_unlink(path); res = f_unlink(path);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR rmdir: %s\n", fs_error_str(res)); log_write(" ERROR rmdir: %s\n", fs_error_str(res));
} else { } else {
log_write(" OK\n"); log_write(" OK\n");
} }
} }
return res; return res;
} }
// Recursively copy a folder with logging // Recursively copy a folder with logging
int folder_copy(const char *src, const char *dst) { int folder_copy(const char *src, const char *dst) {
DIR dir; DIR dir;
FILINFO fno; FILINFO fno;
int res; int res;
log_write("FOLDER COPY: %s -> %s\n", src, dst); log_write("FOLDER COPY: %s -> %s\n", src, dst);
res = f_opendir(&dir, src); res = f_opendir(&dir, src);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR opendir src: %s\n", fs_error_str(res)); log_write(" ERROR opendir src: %s\n", fs_error_str(res));
return res; return res;
} }
// Get folder name from src path // Get folder name from src path
const char *folder_name = strrchr(src, '/'); const char *folder_name = strrchr(src, '/');
if (folder_name) { if (folder_name) {
folder_name++; folder_name++;
} else { } else {
folder_name = src; folder_name = src;
} }
// Create destination folder // Create destination folder
char *dst_path = combine_paths(dst, folder_name); char *dst_path = combine_paths(dst, folder_name);
if (!dst_path) { if (!dst_path) {
f_closedir(&dir); f_closedir(&dir);
return FR_NOT_ENOUGH_CORE; return FR_NOT_ENOUGH_CORE;
} }
log_write(" Creating: %s\n", dst_path); log_write(" Creating: %s\n", dst_path);
res = f_mkdir(dst_path); res = f_mkdir(dst_path);
if (res == FR_EXIST) { if (res == FR_EXIST) {
log_write(" (already exists)\n"); log_write(" (already exists)\n");
res = FR_OK; res = FR_OK;
} else if (res != FR_OK) { } else if (res != FR_OK) {
log_write(" ERROR mkdir: %s\n", fs_error_str(res)); log_write(" ERROR mkdir: %s\n", fs_error_str(res));
f_closedir(&dir); f_closedir(&dir);
free(dst_path); free(dst_path);
return res; return res;
} }
int file_count = 0; int file_count = 0;
int dir_count = 0; int dir_count = 0;
// Copy contents // Copy contents
while (1) { while (1) {
res = f_readdir(&dir, &fno); res = f_readdir(&dir, &fno);
if (res != FR_OK) { if (res != FR_OK) {
log_write(" ERROR readdir: %s\n", fs_error_str(res)); log_write(" ERROR readdir: %s\n", fs_error_str(res));
break; break;
} }
if (fno.fname[0] == 0) break; // End of directory if (fno.fname[0] == 0) break; // End of directory
char *src_full = combine_paths(src, fno.fname); char *src_full = combine_paths(src, fno.fname);
char *dst_full = combine_paths(dst_path, fno.fname); char *dst_full = combine_paths(dst_path, fno.fname);
if (!src_full || !dst_full) { if (!src_full || !dst_full) {
if (src_full) free(src_full); if (src_full) free(src_full);
if (dst_full) free(dst_full); if (dst_full) free(dst_full);
res = FR_NOT_ENOUGH_CORE; res = FR_NOT_ENOUGH_CORE;
break; break;
} }
if (fno.fattrib & AM_DIR) { if (fno.fattrib & AM_DIR) {
dir_count++; dir_count++;
res = folder_copy(src_full, dst_path); res = folder_copy(src_full, dst_path);
} else { } else {
file_count++; file_count++;
res = file_copy(src_full, dst_full); res = file_copy(src_full, dst_full);
} }
free(src_full); free(src_full);
free(dst_full); free(dst_full);
if (res != FR_OK) break; if (res != FR_OK) break;
} }
f_closedir(&dir); f_closedir(&dir);
// Copy folder attributes // Copy folder attributes
if (res == FR_OK) { if (res == FR_OK) {
FILINFO src_info; FILINFO src_info;
if (f_stat(src, &src_info) == FR_OK) { if (f_stat(src, &src_info) == FR_OK) {
f_chmod(dst_path, src_info.fattrib, 0x3A); f_chmod(dst_path, src_info.fattrib, 0x3A);
} }
log_write(" Done: %d files, %d subdirs\n", file_count, dir_count); log_write(" Done: %d files, %d subdirs\n", file_count, dir_count);
} }
free(dst_path); free(dst_path);
return res; return res;
} }

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,10 @@ typedef enum {
INSTALL_MODE_CLEAN // No OmniNX - selective deletion from clean list INSTALL_MODE_CLEAN // No OmniNX - selective deletion from clean list
} install_mode_t; } 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); int perform_installation(omninx_variant_t pack_variant, install_mode_t mode);
// Update mode operations (install_update.c) // Update mode operations (install_update.c)
@@ -29,12 +32,16 @@ int clean_mode_wipe(void);
int clean_mode_restore(void); int clean_mode_restore(void);
int clean_mode_install(omninx_variant_t variant); int clean_mode_install(omninx_variant_t variant);
void install_preserve_update_cleanup_begin(void);
void install_preserve_update_cleanup_end(void);
// Shared helpers (install.c) - used by install_update.c and install_clean.c // Shared helpers (install.c) - used by install_update.c and install_clean.c
void install_set_color(u32 color); void install_set_color(u32 color);
void install_check_and_clear_screen_if_needed(void); void install_check_and_clear_screen_if_needed(void);
bool install_path_exists(const char *path); bool install_path_exists(const char *path);
int install_count_directory_items(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); 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_list(const char* paths[], const char* description);
int delete_path_lists_grouped(const char *folder_display_name, ...); 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_single_with_progress(const char *path, const char *display_name);

View File

@@ -34,7 +34,7 @@
// Clean mode: Backup user data // Clean mode: Backup user data
int clean_mode_backup(void) { int clean_mode_backup(void) {
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf(" Sichere: DBI, Tinfoil, prod.keys\n"); gfx_printf(" Sichere: CyberFoil, DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
int res = backup_user_data(); int res = backup_user_data();
if (res == FR_OK) { if (res == FR_OK) {
@@ -95,13 +95,14 @@ int clean_mode_wipe(void) {
// Clean mode: Restore user data // Clean mode: Restore user data
int clean_mode_restore(void) { int clean_mode_restore(void) {
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf(" Stelle wieder her: DBI, Tinfoil, prod.keys\n"); gfx_printf(" Stelle wieder her: CyberFoil, DBI (dbi.config), prod.keys\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
int res = restore_user_data(); int res = restore_user_data();
if (res == FR_OK) { if (res == FR_OK) {
set_color(COLOR_GREEN); set_color(COLOR_GREEN);
gfx_printf(" [OK] Wiederherstellung abgeschlossen\n"); gfx_printf(" [OK] Wiederherstellung abgeschlossen\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
delete_path_list(clean_post_restore_dirs_to_delete, "switch/ (nach Restore)");
} }
cleanup_backup(); cleanup_backup();
return res; return res;

View File

@@ -12,10 +12,6 @@
#include <string.h> #include <string.h>
#include <utils/sprintf.h> #include <utils/sprintf.h>
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#undef COLOR_CYAN #undef COLOR_CYAN
#undef COLOR_WHITE #undef COLOR_WHITE
#undef COLOR_GREEN #undef COLOR_GREEN
@@ -37,6 +33,8 @@
// Update mode: Cleanup specific directories/files // Update mode: Cleanup specific directories/files
int update_mode_cleanup(omninx_variant_t variant) { int update_mode_cleanup(omninx_variant_t variant) {
(void)variant; (void)variant;
install_preserve_update_cleanup_begin();
check_and_clear_screen_if_needed(); check_and_clear_screen_if_needed();
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
@@ -66,6 +64,8 @@ int update_mode_cleanup(omninx_variant_t variant) {
misc_files_to_delete, misc_files_to_delete,
NULL); NULL);
install_preserve_update_cleanup_end();
set_color(COLOR_GREEN); set_color(COLOR_GREEN);
gfx_printf(" Bereinigung abgeschlossen!\n"); gfx_printf(" Bereinigung abgeschlossen!\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
@@ -78,7 +78,6 @@ int update_mode_install(omninx_variant_t variant) {
int res; int res;
const char* staging = get_staging_path(variant); const char* staging = get_staging_path(variant);
char src_path[256]; char src_path[256];
char dst_path[256];
if (!staging) { if (!staging) {
return FR_INVALID_PARAMETER; return FR_INVALID_PARAMETER;
@@ -106,6 +105,10 @@ int update_mode_install(omninx_variant_t variant) {
res = folder_copy_with_progress_v2(src_path, "sd:/", "switch/"); res = folder_copy_with_progress_v2(src_path, "sd:/", "switch/");
if (res != FR_OK && res != FR_NO_FILE) return res; 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); s_printf(src_path, "%s/warmboot_mariko", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "warmboot_mariko/"); res = folder_copy_with_progress_v2(src_path, "sd:/", "warmboot_mariko/");
if (res != FR_OK && res != FR_NO_FILE) return res; if (res != FR_OK && res != FR_NO_FILE) return res;
@@ -116,33 +119,17 @@ int update_mode_install(omninx_variant_t variant) {
if (res != FR_OK && res != FR_NO_FILE) return res; if (res != FR_OK && res != FR_NO_FILE) return res;
} }
s_printf(src_path, "%s/extras", staging);
res = folder_copy_with_progress_v2(src_path, "sd:/", "extras/");
if (res != FR_OK && res != FR_NO_FILE) return res;
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf(" Kopiere Root-Dateien...\n"); gfx_printf(" Kopiere Root-Dateien...\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
s_printf(src_path, "%s/boot.dat", staging); res = install_copy_staging_root_files(staging, "sd:/");
s_printf(dst_path, "sd:/boot.dat"); if (res != FR_OK)
if (path_exists(src_path)) file_copy(src_path, dst_path); return res;
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_GREEN); set_color(COLOR_GREEN);
gfx_printf(" Kopie abgeschlossen!\n"); gfx_printf(" Kopie abgeschlossen!\n");

View File

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

View File

@@ -6,10 +6,6 @@
* Based on TegraExplorer/hekate by CTCaer, naehrwert, shchmue * Based on TegraExplorer/hekate by CTCaer, naehrwert, shchmue
*/ */
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#include <string.h> #include <string.h>
#include <display/di.h> #include <display/di.h>
@@ -35,6 +31,7 @@
#include "fs.h" #include "fs.h"
#include "version.h" #include "version.h"
#include "install.h" #include "install.h"
#include "screenshot.h"
// Configuration // Configuration
#define PAYLOAD_PATH "sd:/bootloader/update.bin" #define PAYLOAD_PATH "sd:/bootloader/update.bin"
@@ -115,6 +112,12 @@ static void print_header(void) {
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
} }
/* Console Vol+ and Vol- together (exit / cancel where documented) */
static inline bool cancel_combo_pressed(u8 btn)
{
return (btn & (BTN_VOL_UP | BTN_VOL_DOWN)) == (BTN_VOL_UP | BTN_VOL_DOWN);
}
void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size) { void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size) {
memcpy((u8 *)payload_src, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ); memcpy((u8 *)payload_src, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ);
@@ -138,7 +141,7 @@ static int launch_payload(const char *path) {
if (sd_mount()) { if (sd_mount()) {
FIL fp; FIL fp;
if (f_open(&fp, path, FA_READ)) { if (f_open(&fp, path, FA_READ)) {
gfx_printf("Payload nicht gefunden: %s\n", path); gfx_printf("Hekate nicht gefunden: %s\n", path);
return 1; return 1;
} }
@@ -182,6 +185,12 @@ static int file_exists(const char *path) {
return (f_stat(path, &fno) == FR_OK); return (f_stat(path, &fno) == FR_OK);
} }
void installer_launch_hekate_payload(void) {
if (file_exists(PAYLOAD_PATH))
launch_payload(PAYLOAD_PATH);
power_set_state(POWER_OFF_REBOOT);
}
extern void pivot_stack(u32 stack_top); extern void pivot_stack(u32 stack_top);
void ipl_main(void) { void ipl_main(void) {
@@ -243,11 +252,13 @@ void ipl_main(void) {
set_color(COLOR_GREEN); set_color(COLOR_GREEN);
gfx_printf("Druecke A-Taste (rechter Joy-Con) oder Power-Taste,\n"); gfx_printf("Druecke A-Taste (rechter Joy-Con) oder Power-Taste,\n");
gfx_printf("um Hekate zu starten...\n"); gfx_printf("um Hekate zu starten...\n");
gfx_printf("Oder Vol+ und Vol- (Konsole) gleichzeitig.\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
} else { } else {
set_color(COLOR_GREEN); set_color(COLOR_GREEN);
gfx_printf("Druecke A-Taste (rechter Joy-Con) oder Power-Taste,\n"); gfx_printf("Druecke A-Taste (rechter Joy-Con) oder Power-Taste,\n");
gfx_printf("um den Neustart zu starten...\n"); gfx_printf("um den Neustart zu starten...\n");
gfx_printf("Oder Vol+ und Vol- (Konsole) gleichzeitig.\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
} }
@@ -264,13 +275,21 @@ void ipl_main(void) {
break; break;
} }
// Check joycon A button // Check joycon A button; Capture = screenshot
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->a) { if (jc) {
if (jc->cap)
take_screenshot();
if (jc->a) {
button_pressed = true;
break;
}
}
if (cancel_combo_pressed(btn_state)) {
button_pressed = true; button_pressed = true;
break; break;
} }
msleep(50); // Small delay to avoid busy-waiting msleep(50); // Small delay to avoid busy-waiting
} }
@@ -278,7 +297,7 @@ void ipl_main(void) {
if (file_exists(PAYLOAD_PATH)) { if (file_exists(PAYLOAD_PATH)) {
gfx_printf("\n"); gfx_printf("\n");
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf("Payload wird gestartet...\n"); gfx_printf("Hekate wird gestartet...\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
msleep(500); msleep(500);
launch_payload(PAYLOAD_PATH); launch_payload(PAYLOAD_PATH);
@@ -324,12 +343,14 @@ void ipl_main(void) {
gfx_printf("\n"); gfx_printf("\n");
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf("D-Pad / Vol+/-: Auswahl | A oder Power: Bestaetigen\n"); gfx_printf("D-Pad / Vol+/-: Auswahl | A oder Power: Bestaetigen\n");
gfx_printf("Vol+ und Vol- gleichzeitig: Abbrechen (Hekate)\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
// Edge detection: only move on press (not while held) // Edge detection: only move on press (not while held)
bool prev_up = false, prev_down = false; bool prev_up = false, prev_down = false;
bool menu_aborted = false;
while (!confirmed) { while (!confirmed && !menu_aborted) {
// On selection change: redraw only the two affected lines (no full clear) // On selection change: redraw only the two affected lines (no full clear)
if (selected != prev_selected) { if (selected != prev_selected) {
gfx_con_setpos(menu_x, menu_variant_start_y + (u32)prev_selected * 16); gfx_con_setpos(menu_x, menu_variant_start_y + (u32)prev_selected * 16);
@@ -345,6 +366,14 @@ void ipl_main(void) {
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
u8 btn = btn_read(); u8 btn = btn_read();
if (jc && jc->cap)
take_screenshot();
if (cancel_combo_pressed(btn)) {
menu_aborted = true;
break;
}
// D-pad or Vol+ / Vol- for selection (Vol+ = up, Vol- = down) // D-pad or Vol+ / Vol- for selection (Vol+ = up, Vol- = down)
bool cur_up = false, cur_down = false; bool cur_up = false, cur_down = false;
if (jc) { if (jc) {
@@ -369,6 +398,20 @@ void ipl_main(void) {
msleep(50); msleep(50);
} }
if (menu_aborted) {
gfx_printf("\n");
set_color(COLOR_YELLOW);
gfx_printf("Abgebrochen. Starte Hekate...\n");
set_color(COLOR_WHITE);
msleep(500);
if (file_exists(PAYLOAD_PATH)) {
launch_payload(PAYLOAD_PATH);
} else {
power_set_state(POWER_OFF_REBOOT);
}
return;
}
pack_variant = variants_present[selected]; pack_variant = variants_present[selected];
gfx_clear_grey(0x1B); gfx_clear_grey(0x1B);
@@ -395,14 +438,19 @@ void ipl_main(void) {
gfx_printf("mit angeschlossenem Ladegeraet durchgefuehrt werden.\n\n"); gfx_printf("mit angeschlossenem Ladegeraet durchgefuehrt werden.\n\n");
set_color(COLOR_YELLOW); set_color(COLOR_YELLOW);
gfx_printf("Stecke das Ladegeraet an und warte...\n"); gfx_printf("Stecke das Ladegeraet an und warte...\n");
gfx_printf("Oder druecke + und - zum Abbrechen.\n"); gfx_printf("Oder Vol+ und Vol- (Konsole) zum Abbrechen.\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
jc_init_hw(); jc_init_hw();
while (btn_read() & BTN_POWER) { msleep(50); } while (btn_read() & BTN_POWER) { msleep(50); }
bool user_cancelled = false; bool user_cancelled = false;
while (batt_pct < BATT_LOW_THRESHOLD && !bq24193_charger_connected() && !user_cancelled) { while (batt_pct < BATT_LOW_THRESHOLD && !bq24193_charger_connected() && !user_cancelled) {
u8 pbtn = btn_read();
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->plus && jc->minus) { if (jc) {
if (jc->cap)
take_screenshot();
}
if (cancel_combo_pressed(pbtn)) {
user_cancelled = true; user_cancelled = true;
break; break;
} }
@@ -428,6 +476,86 @@ void ipl_main(void) {
gfx_con_setpos(0, 0); gfx_con_setpos(0, 0);
print_header(); print_header();
// UHS class check: recommend U2 / V30 / A2 for emuMMC (clean install only)
if (mode == INSTALL_MODE_CLEAN) {
#define UHS_U2_MIN 2
#define UHS_V30_MIN 30
#define UHS_A2_MIN 2
bool uhs_ok = (sd_storage.ssr.uhs_grade >= UHS_U2_MIN &&
sd_storage.ssr.video_class >= UHS_V30_MIN &&
sd_storage.ssr.app_class >= UHS_A2_MIN);
if (!uhs_ok) {
jc_init_hw();
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
set_color(COLOR_RED);
gfx_printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
gfx_printf("!! WARNUNG - SD-KARTEN-KLASSE !!\n");
gfx_printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
set_color(COLOR_WHITE);
gfx_printf("Diese SD-Karte erfuellt NICHT die empfohlenen\n");
gfx_printf("Mindestanforderungen (U2 | V30 | A2).\n\n");
set_color(COLOR_YELLOW);
gfx_printf("Aktuelle Werte: UHS%d | V%d | A%d\n\n",
(u32)sd_storage.ssr.uhs_grade,
(u32)sd_storage.ssr.video_class,
(u32)sd_storage.ssr.app_class);
set_color(COLOR_WHITE);
gfx_printf("Moegliche Folgen bei schwachen Karten:\n");
gfx_printf(" - Langsamere emuMMC / Spiel-Ladezeiten\n");
gfx_printf(" - Hoheres Risiko von Korruption oder Abstuerzen\n");
gfx_printf(" - Instabilitaet beim Schreiben groesserer Daten\n\n");
set_color(COLOR_CYAN);
gfx_printf("Empfohlen: U2 | V30 | A2 oder besser.\n");
gfx_printf("Empfohlene Karten: Samsung EVO Plus, EVO Select und PRO Plus.\n\n");
set_color(COLOR_GREEN);
gfx_printf("Druecke A (oder Power), um trotzdem fortzufahren.\n");
set_color(COLOR_CYAN);
gfx_printf("Vol+ und Vol- (Konsole) gleichzeitig: Abbrechen -> Hekate.\n");
set_color(COLOR_WHITE);
while (btn_read() & BTN_POWER) { msleep(50); }
bool acknowledged = false;
bool uhs_aborted = false;
while (!acknowledged && !uhs_aborted) {
u8 btn_state = btn_read();
if (btn_state & BTN_POWER) acknowledged = true;
jc_gamepad_rpt_t *jc = joycon_poll();
if (jc) {
if (jc->cap)
take_screenshot();
if (jc->a) acknowledged = true;
}
if (cancel_combo_pressed(btn_state)) {
uhs_aborted = true;
break;
}
msleep(50);
}
if (uhs_aborted) {
gfx_printf("\nAbgebrochen. Starte Hekate...\n");
msleep(500);
if (file_exists(PAYLOAD_PATH)) {
launch_payload(PAYLOAD_PATH);
} else {
power_set_state(POWER_OFF_REBOOT);
}
return;
}
// Wait for A and Power to be released so the same press doesn't start the install
while (btn_read() & BTN_POWER) { msleep(50); }
bool released = false;
while (!released) {
jc_gamepad_rpt_t *jc = joycon_poll();
released = (!jc || !jc->a) && !(btn_read() & BTN_POWER);
if (!released) msleep(50);
}
msleep(300); // Short delay so the next screen is visible before accepting input
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
print_header();
}
}
// Show information // Show information
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf("Installationsmodus: %s\n", mode == INSTALL_MODE_UPDATE ? "Update" : "Saubere Installation"); gfx_printf("Installationsmodus: %s\n", mode == INSTALL_MODE_UPDATE ? "Update" : "Saubere Installation");
@@ -453,7 +581,7 @@ void ipl_main(void) {
gfx_printf("um die Installation zu starten...\n"); gfx_printf("um die Installation zu starten...\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf("Druecke + und - gleichzeitig zum Abbrechen (zurueck zu Hekate).\n"); gfx_printf("Vol+ und Vol- (Konsole) gleichzeitig: Abbrechen -> Hekate.\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
// Wait for A/Power to start, or +/- to cancel // Wait for A/Power to start, or +/- to cancel
@@ -473,18 +601,19 @@ void ipl_main(void) {
break; break;
} }
// Check joycon buttons // Check joycon buttons; Capture = screenshot
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
if (jc) { if (jc) {
if (jc->cap)
take_screenshot();
if (jc->a) { if (jc->a) {
button_pressed = true; button_pressed = true;
break; break;
} }
// + and - simultaneously = cancel, return to hekate }
if (jc->plus && jc->minus) { if (cancel_combo_pressed(btn_state)) {
cancelled = true; cancelled = true;
break; break;
}
} }
msleep(50); // Small delay to avoid busy-waiting msleep(50); // Small delay to avoid busy-waiting
@@ -520,32 +649,28 @@ void ipl_main(void) {
int result = perform_installation(pack_variant, mode); int result = perform_installation(pack_variant, mode);
// Wait 3 seconds before clearing screen to allow errors to be visible // Wait 3 seconds before clearing screen to allow errors to be visible
msleep(3000); msleep(1500);
// take_screenshot(); // Take a screenshot of the installation summary
// Clear screen for final summary to ensure it's visible // Clear screen for final summary to ensure it's visible
gfx_clear_grey(0x1B); gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0); gfx_con_setpos(0, 0);
// Summary // Summary
set_color(COLOR_CYAN); const char *op_name = (mode == INSTALL_MODE_UPDATE) ? " Update" : " Installation";
gfx_printf("========================================\n");
gfx_printf(" OmniNX Installer Payload v%s\n", VERSION);
gfx_printf("========================================\n\n");
set_color(COLOR_WHITE);
if (result == FR_OK && total_errors == 0) { if (result == FR_OK && total_errors == 0) {
set_color(COLOR_GREEN); set_color(COLOR_GREEN);
gfx_printf("========================================\n"); gfx_printf("=======================================\n");
gfx_printf(" Installation abgeschlossen!\n"); gfx_printf(" %s abgeschlossen!\n", op_name);
gfx_printf("========================================\n"); gfx_printf("=======================================\n");
} else { } else {
set_color(COLOR_RED); set_color(COLOR_RED);
gfx_printf("========================================\n"); gfx_printf("=======================================\n");
gfx_printf(" Installation beendet\n"); gfx_printf(" %s beendet\n", op_name);
if (total_errors > 0) { if (total_errors > 0) {
gfx_printf(" %d Fehler\n", total_errors); gfx_printf(" %d Fehler\n", total_errors);
} }
gfx_printf("========================================\n"); gfx_printf("=======================================\n");
} }
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
@@ -573,11 +698,15 @@ void ipl_main(void) {
break; break;
} }
// Check joycon A button // Check joycon A button; Capture = screenshot
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->a) { if (jc) {
button_pressed = true; if (jc->cap)
break; take_screenshot();
if (jc->a) {
button_pressed = true;
break;
}
} }
msleep(50); // Small delay to avoid busy-waiting msleep(50); // Small delay to avoid busy-waiting
@@ -585,16 +714,16 @@ void ipl_main(void) {
gfx_printf("\n"); gfx_printf("\n");
set_color(COLOR_CYAN); set_color(COLOR_CYAN);
gfx_printf("Payload wird gestartet...\n"); gfx_printf("Hekate wird gestartet...\n");
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
msleep(500); // Brief delay before launch msleep(500); // Brief delay before launch
if (file_exists(PAYLOAD_PATH)) { if (file_exists(PAYLOAD_PATH)) {
launch_payload(PAYLOAD_PATH); launch_payload(PAYLOAD_PATH);
} else { } else {
// Payload not found, show error // Hekate (update.bin) not found, show error
set_color(COLOR_RED); set_color(COLOR_RED);
gfx_printf("\nFEHLER: Payload nicht gefunden!\n"); gfx_printf("\nFEHLER: Hekate nicht gefunden!\n");
gfx_printf("Pfad: %s\n", PAYLOAD_PATH); gfx_printf("Pfad: %s\n", PAYLOAD_PATH);
set_color(COLOR_WHITE); set_color(COLOR_WHITE);
gfx_printf("\nDruecke A oder Power zum Neustart...\n"); gfx_printf("\nDruecke A oder Power zum Neustart...\n");
@@ -615,9 +744,13 @@ void ipl_main(void) {
} }
jc_gamepad_rpt_t *jc = joycon_poll(); jc_gamepad_rpt_t *jc = joycon_poll();
if (jc && jc->a) { if (jc) {
button_pressed = true; if (jc->cap)
break; take_screenshot();
if (jc->a) {
button_pressed = true;
break;
}
} }
msleep(50); msleep(50);

View File

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

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);
}