Compare commits
19 Commits
0.3.4
...
e5ab6a2ed0
| Author | SHA1 | Date | |
|---|---|---|---|
| e5ab6a2ed0 | |||
| fa5e32f92d | |||
| 0c898a3293 | |||
| c6b6b42eda | |||
| 9cd8953f78 | |||
| 45c659f1ea | |||
| f82ee15f52 | |||
| 2987a3c88e | |||
| e6676315c7 | |||
| ef9c1eaf7a | |||
| 763cb43983 | |||
| 59f7dde33a | |||
| bc086126e5 | |||
| 5702202ea9 | |||
| 36a08cf42f | |||
| 1a7b0a1667 | |||
| 6ee7d5f892 | |||
| 6239b65472 | |||
| 29d63ffbf7 |
46
.github/workflows/main.yml
vendored
46
.github/workflows/main.yml
vendored
@@ -1,52 +1,30 @@
|
|||||||
name: Release
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issue_comment:
|
push:
|
||||||
types: [ created ]
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_release:
|
build:
|
||||||
name: Build and release
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: devkitpro/devkita64:latest
|
container: devkitpro/devkita64:latest
|
||||||
if: contains(github.event.comment.body, '/release-action')
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Update latest libnx
|
- name: Install dependencies
|
||||||
run: |
|
run: apt-get update && apt-get install -y nodejs
|
||||||
git config --global --add safe.directory "*"
|
|
||||||
git clone --recurse-submodules https://github.com/zdm65477730/libnx.git
|
|
||||||
cd libnx
|
|
||||||
make install -j$(nproc)
|
|
||||||
shell: bash
|
|
||||||
- name: Checkout latest code
|
- name: Checkout latest code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
ref: master
|
|
||||||
clean: true
|
clean: true
|
||||||
fetch-depth: 0
|
|
||||||
fetch-tags: true
|
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Setup ENV parameters
|
|
||||||
run: |
|
|
||||||
VER_FILE=Makefile
|
|
||||||
VERSION=$(awk '/^LPVERSION_MAJOR/{print $3}' $VER_FILE).$(awk '/^LPVERSION_MINOR/{print $3}' $VER_FILE).$(awk '/^LPVERSION_BUGFX/{print $3}' $VER_FILE)
|
|
||||||
echo "TAG=${VERSION}" >> "${GITHUB_ENV}"
|
|
||||||
echo "RELEASE_NAME=CommonProblemResolver ${VERSION}" >> "${GITHUB_ENV}"
|
|
||||||
shell: bash
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
export DEVKITPRO=/opt/devkitpro
|
export DEVKITPRO=/opt/devkitpro
|
||||||
make all
|
make -j4 all
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Upload Release Asset
|
- name: Upload artifact
|
||||||
uses: softprops/action-gh-release@v2.0.9
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.RELEASE_NAME }}
|
name: AllgemeinerProblemLoeser.bin
|
||||||
tag_name: ${{ env.TAG }}
|
path: output/AllgemeinerProblemLoeser.bin
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
generate_release_notes: yes
|
|
||||||
make_latest: true
|
|
||||||
files: |
|
|
||||||
output/CommonProblemResolver.bin
|
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -2,3 +2,16 @@
|
|||||||
build
|
build
|
||||||
output
|
output
|
||||||
research
|
research
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
._*
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.apDisk
|
||||||
|
|||||||
10
Makefile
10
Makefile
@@ -9,13 +9,13 @@ include $(DEVKITARM)/base_rules
|
|||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
IPL_LOAD_ADDR := 0x40008000
|
IPL_LOAD_ADDR := 0x40008000
|
||||||
LPVERSION_MAJOR := 0
|
APLVERSION_MAJOR := 0
|
||||||
LPVERSION_MINOR := 3
|
APLVERSION_MINOR := 3
|
||||||
LPVERSION_BUGFX := 4
|
APLVERSION_BUGFX := 4
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
TARGET := CommonProblemResolver
|
TARGET := AllgemeinerProblemLoeser
|
||||||
BUILDDIR := build
|
BUILDDIR := build
|
||||||
OUTPUTDIR := output
|
OUTPUTDIR := output
|
||||||
SOURCEDIR = source
|
SOURCEDIR = source
|
||||||
@@ -37,7 +37,7 @@ FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"'
|
|||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
|
CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
|
||||||
CUSTOMDEFINES += -DLP_VER_MJ=$(LPVERSION_MAJOR) -DLP_VER_MN=$(LPVERSION_MINOR) -DLP_VER_BF=$(LPVERSION_BUGFX)
|
CUSTOMDEFINES += -DAPL_VER_MJ=$(APLVERSION_MAJOR) -DAPL_VER_MN=$(APLVERSION_MINOR) -DAPL_VER_BF=$(APLVERSION_BUGFX)
|
||||||
CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
|
CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC)
|
||||||
|
|
||||||
# 0: UART_A, 1: UART_B.
|
# 0: UART_A, 1: UART_B.
|
||||||
|
|||||||
55
README.md
55
README.md
@@ -1,34 +1,43 @@
|
|||||||
# CommonProblemResolver (CPR)
|
# AllgemeinerProblemLoeser (APL)
|
||||||
|
|
||||||
Rescue your switch without a pc!
|
Rette deine Switch ohne PC!
|
||||||
|
|
||||||
## Usage
|
**Original-Projekt:** [Common Problem Resolver](https://github.com/Team-Neptune/CommonProblemResolver) von [Team Neptune](https://github.com/Team-Neptune).
|
||||||
1. Get your favorite payload injector
|
|
||||||
2. Inject CPR as a payload
|
|
||||||
|
|
||||||
Navigate around the menus using the joycons.
|
## Nutzung
|
||||||
- A: Select
|
|
||||||
- B: Back
|
|
||||||
- Left Joystick up/down (Dpad or joystick): navigate menus up/down
|
|
||||||
- Right Joystick up/down: fast menu navigation up/down
|
|
||||||
- Capture (Minerva only): Take a screenshot
|
|
||||||
- L3/R3 (Press joysticks in): Recalibrate centerpoint
|
|
||||||
|
|
||||||
If you do not have your joycons connected:
|
1. Besorge dir einen Payload-Injector deiner Wahl
|
||||||
|
2. Lade APL als Payload
|
||||||
|
|
||||||
|
Navigation in den Menues mit den Joy-Cons:
|
||||||
|
- A: Auswaehlen
|
||||||
|
- B: Zurueck
|
||||||
|
- Linker Stick hoch/runter (D-Pad oder Stick): Menue hoch/runter
|
||||||
|
- Rechter Stick hoch/runter: Schnelle Menue-Navigation
|
||||||
|
- Capture (nur Minerva): Screenshot
|
||||||
|
- L3/R3 (Sticks eindruecken): Mittelpunkt neu kalibrieren
|
||||||
|
|
||||||
|
Ohne verbundene Joy-Cons:
|
||||||
- Power -> A
|
- Power -> A
|
||||||
- Vol+ -> Left Joystick up
|
- Vol+ -> Linker Stick hoch
|
||||||
- Vol- -> Left Joystick down
|
- Vol- -> Linker Stick runter
|
||||||
|
|
||||||
## Functions
|
## Funktionen
|
||||||
- Disables all sysmodules on your switch (might fix booting issues after update)
|
|
||||||
- Delete installed themes (will fix booting issues after update)
|
- Deaktiviert automatischen Sysmodule-Start (kann Startprobleme nach Updates beheben)
|
||||||
- Reverts changes made by clingwrap
|
- Loescht installierte Themes (kann Startprobleme nach Updates beheben)
|
||||||
|
- Behebt Archiv-Bit (SD-Karte)
|
||||||
|
- Entfernt Mac-Sonderordner
|
||||||
|
- Mariko Sleep Fix (Warmboot) bei Fuse-Mismatch (Standby/Sleep-Workaround)
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Join [our discord](https://discord.gg/VkaRjYN) for help.
|
Fuer Hilfe: [unser Discord](https://discord.gg/VkaRjYN)
|
||||||
|
|
||||||
## Credits
|
## Danksagungen
|
||||||
|
|
||||||
This is heavily based on [TegraExplorer](https://github.com/suchmememanyskill/TegraExplorer) by suchmememanyskill,
|
- **Deutsche Übersetzung:** [Switch Bros.](https://github.com/Switch-Bros/AllgemeinerProblemLoeser)
|
||||||
which itself is based on [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM), and thus also based on [Hekate](https://github.com/CTCaer/hekate)
|
- Dieses Projekt basiert stark auf dem [Original Common Problem Resolver](https://github.com/Team-Neptune/CommonProblemResolver) von [Team Neptune](https://github.com/Team-Neptune),
|
||||||
|
sowie auf [TegraExplorer](https://github.com/suchmememanyskill/TegraExplorer) von suchmememanyskill,
|
||||||
|
[Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM) und [Hekate](https://github.com/CTCaer/hekate).
|
||||||
|
- Warmboot Extractor (Sthetix): extrahiert Warmboot-Caches bei Fuse-Mismatch (Standby/Sleep-Workaround auf Mariko)
|
||||||
|
|||||||
955
source/apl/apl.c
Normal file
955
source/apl/apl.c
Normal file
@@ -0,0 +1,955 @@
|
|||||||
|
// #include <utils/util.h>
|
||||||
|
// #include "tools.h"
|
||||||
|
#include <storage/sd.h>
|
||||||
|
#include "../fs/readers/folderReader.h"
|
||||||
|
#include "../fs/fstypes.h"
|
||||||
|
#include "../fs/fscopy.h"
|
||||||
|
#include <utils/sprintf.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "../gfx/gfx.h"
|
||||||
|
#include "../gfx/gfxutils.h"
|
||||||
|
#include "../gfx/menu.h"
|
||||||
|
#include "../hid/hid.h"
|
||||||
|
// #include "utils.h"
|
||||||
|
#include "../utils/utils.h"
|
||||||
|
#include "../fs/fsutils.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "../warmboot/warmboot_extractor.h"
|
||||||
|
// #include <dirent.h>
|
||||||
|
// #include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
void _DeleteFileSimple(char *thing){
|
||||||
|
//char *thing = CombinePaths(path, entry.name);
|
||||||
|
int res = f_unlink(thing);
|
||||||
|
if (res)
|
||||||
|
DrawError(newErrCode(res));
|
||||||
|
free(thing);
|
||||||
|
}
|
||||||
|
void _RenameFileSimple(char *sourcePath, char *destPath){
|
||||||
|
int res = f_rename(sourcePath, destPath);
|
||||||
|
if (res){
|
||||||
|
DrawError(newErrCode(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ErrCode_t _FolderDelete(const char *path){
|
||||||
|
int res = 0;
|
||||||
|
ErrCode_t ret = newErrCode(0);
|
||||||
|
u32 x, y;
|
||||||
|
gfx_con_getpos(&x, &y);
|
||||||
|
Vector_t fileVec = ReadFolder(path, &res);
|
||||||
|
if (res){
|
||||||
|
ret = newErrCode(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vecDefArray(FSEntry_t *, fs, fileVec);
|
||||||
|
for (int i = 0; i < fileVec.count && !ret.err; i++){
|
||||||
|
char *temp = CombinePaths(path, fs[i].name);
|
||||||
|
if (fs[i].isDir){
|
||||||
|
ret = _FolderDelete(temp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = f_unlink(temp);
|
||||||
|
if (res){
|
||||||
|
ret = newErrCode(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ret.err){
|
||||||
|
res = f_unlink(path);
|
||||||
|
if (res)
|
||||||
|
ret = newErrCode(res);
|
||||||
|
}
|
||||||
|
clearFileVector(&fileVec);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int _StartsWith(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
if(strncmp(a, b, strlen(b)) == 0) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int listdir(char *path, u32 hos_folder, int *deleted_count)
|
||||||
|
{
|
||||||
|
FRESULT res;
|
||||||
|
DIR dir;
|
||||||
|
u32 dirLength = 0;
|
||||||
|
static FILINFO fno;
|
||||||
|
|
||||||
|
// Open directory.
|
||||||
|
res = f_opendir(&dir, path);
|
||||||
|
if (res != FR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
dirLength = strlen(path);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Clear file or folder path.
|
||||||
|
path[dirLength] = 0;
|
||||||
|
// Read a directory item.
|
||||||
|
res = f_readdir(&dir, &fno);
|
||||||
|
// Break on error or end of dir.
|
||||||
|
if (res != FR_OK || fno.fname[0] == 0)
|
||||||
|
break;
|
||||||
|
// Skip official Nintendo dir if started from root.
|
||||||
|
if (!hos_folder && !strcmp(fno.fname, "Nintendo"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Set new directory or file.
|
||||||
|
memcpy(&path[dirLength], "/", 1);
|
||||||
|
memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1);
|
||||||
|
|
||||||
|
// Is it a directory?
|
||||||
|
if (fno.fattrib & AM_DIR)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
strcmp(fno.fname, ".Trash") == 0 ||
|
||||||
|
strcmp(fno.fname, ".Trashes") == 0 ||
|
||||||
|
strcmp(fno.fname, ".DS_Store") == 0 ||
|
||||||
|
strcmp(fno.fname, ".Spotlight-V100") == 0 ||
|
||||||
|
strcmp(fno.fname, ".apDisk") == 0 ||
|
||||||
|
strcmp(fno.fname, ".VolumeIcon.icns") == 0 ||
|
||||||
|
strcmp(fno.fname, ".fseventsd") == 0 ||
|
||||||
|
strcmp(fno.fname, ".TemporaryItems") == 0
|
||||||
|
) {
|
||||||
|
_FolderDelete(path);
|
||||||
|
if (deleted_count) (*deleted_count)++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Only recurse into directories that were not deleted.
|
||||||
|
listdir(path, 0, deleted_count);
|
||||||
|
}
|
||||||
|
if (res != FR_OK)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
strcmp(fno.fname, ".DS_Store") == 0 ||
|
||||||
|
strcmp(fno.fname, ".Spotlight-V100") == 0 ||
|
||||||
|
strcmp(fno.fname, ".apDisk") == 0 ||
|
||||||
|
strcmp(fno.fname, ".VolumeIcon.icns") == 0 ||
|
||||||
|
strcmp(fno.fname, ".fseventsd") == 0 ||
|
||||||
|
strcmp(fno.fname, ".TemporaryItems") == 0 ||
|
||||||
|
_StartsWith(fno.fname, "._")
|
||||||
|
) {
|
||||||
|
res = f_unlink(path);
|
||||||
|
if (res)
|
||||||
|
DrawError(newErrCode(res));
|
||||||
|
if (deleted_count) (*deleted_count)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f_closedir(&dir);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _fix_attributes(char *path, u32 *total, u32 hos_folder, u32 check_first_run){
|
||||||
|
FRESULT res;
|
||||||
|
DIR dir;
|
||||||
|
u32 dirLength = 0;
|
||||||
|
static FILINFO fno;
|
||||||
|
|
||||||
|
if (check_first_run)
|
||||||
|
{
|
||||||
|
// Read file attributes.
|
||||||
|
res = f_stat(path, &fno);
|
||||||
|
if (res != FR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// Check if archive bit is set.
|
||||||
|
if (fno.fattrib & AM_ARC)
|
||||||
|
{
|
||||||
|
*(u32 *)total = *(u32 *)total + 1;
|
||||||
|
f_chmod(path, 0, AM_ARC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open directory.
|
||||||
|
res = f_opendir(&dir, path);
|
||||||
|
if (res != FR_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
dirLength = strlen(path);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// Clear file or folder path.
|
||||||
|
path[dirLength] = 0;
|
||||||
|
|
||||||
|
// Read a directory item.
|
||||||
|
res = f_readdir(&dir, &fno);
|
||||||
|
|
||||||
|
// Break on error or end of dir.
|
||||||
|
if (res != FR_OK || fno.fname[0] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Skip official Nintendo dir if started from root.
|
||||||
|
if (!hos_folder && !strcmp(fno.fname, "Nintendo"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Set new directory or file.
|
||||||
|
memcpy(&path[dirLength], "/", 1);
|
||||||
|
memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1);
|
||||||
|
|
||||||
|
// Check if archive bit is set.
|
||||||
|
if (fno.fattrib & AM_ARC)
|
||||||
|
{
|
||||||
|
*total = *total + 1;
|
||||||
|
f_chmod(path, 0, AM_ARC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is it a directory?
|
||||||
|
if (fno.fattrib & AM_DIR)
|
||||||
|
{
|
||||||
|
// Set archive bit to NCA folders.
|
||||||
|
if (hos_folder && !strcmp(fno.fname + strlen(fno.fname) - 4, ".nca"))
|
||||||
|
{
|
||||||
|
*total = *total + 1;
|
||||||
|
f_chmod(path, AM_ARC, AM_ARC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter the directory.
|
||||||
|
res = _fix_attributes(path, total, hos_folder, 0);
|
||||||
|
if (res != FR_OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void m_entry_fixArchiveBit(u32 type){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Behebe Archive Bit\n\n");
|
||||||
|
|
||||||
|
char path[256];
|
||||||
|
char label[16];
|
||||||
|
|
||||||
|
u32 total = 0;
|
||||||
|
if (sd_mount())
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
strcpy(path, "/");
|
||||||
|
strcpy(label, "SD-Karte");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
strcpy(path, "/Nintendo");
|
||||||
|
strcpy(label, "Nintendo Ordner");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx_printf("Durchlaufe alle %s Dateien!\nDas kann einige Zeit dauern...\n\n", label);
|
||||||
|
_fix_attributes(path, &total, type, type);
|
||||||
|
gfx_printf("%kAnzahl reparierter Archiv bits: %d!%k", 0xFF96FF00, total, 0xFFCCCCCC);
|
||||||
|
|
||||||
|
gfx_printf("\n\n Fertig! Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _fixAMSErrorFolder(const char *titleId, const char *label){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Fix Fehlercode %s --\n\n", label);
|
||||||
|
if (!sd_mount()) {
|
||||||
|
gfx_printf("SD-Karte konnte nicht gemountet werden.\n");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *path = CpyStr("sd:/atmosphere/contents");
|
||||||
|
char *full = CombinePaths(path, titleId);
|
||||||
|
free(path);
|
||||||
|
if (FileExists(full)) {
|
||||||
|
gfx_printf("Entferne Ordner %s ...\n", titleId);
|
||||||
|
FolderDelete(full);
|
||||||
|
gfx_printf("Ordner erfolgreich geloescht.\n");
|
||||||
|
} else {
|
||||||
|
gfx_printf("Ordner %s nicht gefunden (evtl. bereits behoben).\n", titleId);
|
||||||
|
}
|
||||||
|
free(full);
|
||||||
|
gfx_printf("\n\n Fertig! Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_fixAMSError_4200(void){
|
||||||
|
_fixAMSErrorFolder("4200000000000010", "4200000000000010");
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_fixAMSError_6900(void){
|
||||||
|
_fixAMSErrorFolder("690000000000000D", "690000000000000D");
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_fixAMSError_BD00(void){
|
||||||
|
_fixAMSErrorFolder("010000000000BD00", "010000000000BD00");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _deleteTheme(char* basePath, char* folderId){
|
||||||
|
int removed = 0;
|
||||||
|
char *path = CombinePaths(basePath, folderId);
|
||||||
|
if (FileExists(path)) {
|
||||||
|
gfx_printf("-- Theme gefunden: %s\n", path);
|
||||||
|
FolderDelete(path);
|
||||||
|
removed = 1;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_deleteInstalledThemes(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Loesche installierte Themes.\n\n");
|
||||||
|
int count = 0;
|
||||||
|
count += _deleteTheme("sd:/atmosphere/contents", "0100000000001000");
|
||||||
|
count += _deleteTheme("sd:/atmosphere/contents", "0100000000001007");
|
||||||
|
count += _deleteTheme("sd:/atmosphere/contents", "0100000000001013");
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
gfx_printf("\n%d Theme(s) geloescht.\n", count);
|
||||||
|
else
|
||||||
|
gfx_printf("\nKeine installierten Themes gefunden.\n");
|
||||||
|
gfx_printf("\n\n Fertig! Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_deleteBootFlags(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Automatisches starten der sysmodule deaktivieren.\n\n");
|
||||||
|
char *storedPath = CpyStr("sd:/atmosphere/contents");
|
||||||
|
int readRes = 0;
|
||||||
|
int count = 0;
|
||||||
|
Vector_t fileVec = ReadFolder(storedPath, &readRes);
|
||||||
|
if (readRes){
|
||||||
|
clearFileVector(&fileVec);
|
||||||
|
free(storedPath);
|
||||||
|
DrawError(newErrCode(readRes));
|
||||||
|
} else {
|
||||||
|
vecDefArray(FSEntry_t*, fsEntries, fileVec);
|
||||||
|
for (int i = 0; i < fileVec.count; i++){
|
||||||
|
char *suf = "/flags/boot2.flag";
|
||||||
|
char *subPath = CombinePaths(storedPath, fsEntries[i].name);
|
||||||
|
char *flagPath = CombinePaths(subPath, suf);
|
||||||
|
free(subPath);
|
||||||
|
if (FileExists(flagPath)) {
|
||||||
|
gfx_printf("Loesche: %s\n", flagPath);
|
||||||
|
_DeleteFileSimple(flagPath);
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
free(flagPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearFileVector(&fileVec);
|
||||||
|
free(storedPath);
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
gfx_printf("\n%d Boot-Flag(s) geloescht.\n", count);
|
||||||
|
else if (!readRes)
|
||||||
|
gfx_printf("\nKeine Boot-Flags gefunden.\n");
|
||||||
|
gfx_printf("\n\n Fertig! Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void m_entry_fixMacSpecialFolders(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Mac-Ordner reparieren (dies kann ein wenig dauern, bitte warten.)\n\n");
|
||||||
|
int deleted = 0;
|
||||||
|
char path[1024];
|
||||||
|
strcpy(path, "sd:/");
|
||||||
|
listdir(path, 0, &deleted);
|
||||||
|
if (deleted > 0)
|
||||||
|
gfx_printf("\n%d Mac-Datei(en)/Ordner entfernt.\n", deleted);
|
||||||
|
else
|
||||||
|
gfx_printf("\nKeine Mac-Sonderdateien oder -ordner gefunden.\n");
|
||||||
|
gfx_printf("\n\n Fertig! Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 _hex_nibble_lower(char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return (u32)(c - '0');
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return (u32)(c - 'a' + 10);
|
||||||
|
return 0xFFFFFFFFu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _parse_wb_filename_lower(const char *fname, u32 *fuse_out)
|
||||||
|
{
|
||||||
|
// Expected: wb_xx.bin where xx is lowercase hex (exactly 2 chars).
|
||||||
|
// Example: wb_16.bin
|
||||||
|
if (!fname || !fuse_out)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Length check: "wb_" + 2 + ".bin" = 3 + 2 + 4 = 9
|
||||||
|
if (strlen(fname) != 9)
|
||||||
|
return false;
|
||||||
|
if (strncmp(fname, "wb_", 3) != 0)
|
||||||
|
return false;
|
||||||
|
if (fname[5] != '.')
|
||||||
|
return false;
|
||||||
|
if (strncmp(fname + 6, "bin", 3) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
u32 hi = _hex_nibble_lower(fname[3]);
|
||||||
|
u32 lo = _hex_nibble_lower(fname[4]);
|
||||||
|
if (hi == 0xFFFFFFFFu || lo == 0xFFFFFFFFu)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*fuse_out = (hi << 4) | lo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rstrip(char *s)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
int n = (int)strlen(s);
|
||||||
|
while (n > 0)
|
||||||
|
{
|
||||||
|
char c = s[n - 1];
|
||||||
|
if (c == '\n' || c == '\r' || c == ' ' || c == '\t')
|
||||||
|
s[--n] = 0;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *_ltrim(char *s)
|
||||||
|
{
|
||||||
|
if (!s)
|
||||||
|
return s;
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
s++;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _read_line_no_nl(FIL *fp, char *out, u32 out_sz)
|
||||||
|
{
|
||||||
|
if (!fp || !out || out_sz < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
u32 idx = 0;
|
||||||
|
BYTE b;
|
||||||
|
UINT br = 0;
|
||||||
|
|
||||||
|
// Return false on immediate EOF.
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (idx >= out_sz - 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
FRESULT res = f_read(fp, &b, 1, &br);
|
||||||
|
if (res != FR_OK || br == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (b == '\n')
|
||||||
|
break;
|
||||||
|
if (b == '\r')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
out[idx++] = (char)b;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[idx] = 0;
|
||||||
|
return idx > 0 || br != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _ini_section_exists(const char *ini_path, const char *section_name)
|
||||||
|
{
|
||||||
|
FIL fp;
|
||||||
|
if (f_open(&fp, ini_path, FA_READ) != FR_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
while (_read_line_no_nl(&fp, line, sizeof(line)))
|
||||||
|
{
|
||||||
|
char *t = _ltrim(line);
|
||||||
|
if (*t == '[')
|
||||||
|
{
|
||||||
|
char *end = strchr(t, ']');
|
||||||
|
if (end)
|
||||||
|
{
|
||||||
|
*end = 0;
|
||||||
|
if (!strcmp(t + 1, section_name))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*end = ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_close(&fp);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _ini_section_warmboot_is(const char *ini_path, const char *section_name, const char *warmboot_value_rel)
|
||||||
|
{
|
||||||
|
FIL fp;
|
||||||
|
if (f_open(&fp, ini_path, FA_READ) != FR_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
bool in_section = false;
|
||||||
|
bool is_set = false;
|
||||||
|
|
||||||
|
while (_read_line_no_nl(&fp, line, sizeof(line)))
|
||||||
|
{
|
||||||
|
char *t = _ltrim(line);
|
||||||
|
if (*t == '[')
|
||||||
|
{
|
||||||
|
char *end = strchr(t, ']');
|
||||||
|
if (end)
|
||||||
|
{
|
||||||
|
*end = 0;
|
||||||
|
in_section = !strcmp(t + 1, section_name);
|
||||||
|
*end = ']';
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_section)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *k = _ltrim(line);
|
||||||
|
if (strncmp(k, "warmboot", 8) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ensure this is the "warmboot" key (not warmboot2/etc), allowing whitespace before '='.
|
||||||
|
char *p = k + 8;
|
||||||
|
while (*p == ' ' || *p == '\t')
|
||||||
|
p++;
|
||||||
|
if (*p != '=')
|
||||||
|
continue;
|
||||||
|
p++;
|
||||||
|
p = _ltrim(p);
|
||||||
|
_rstrip(p);
|
||||||
|
|
||||||
|
if (!strcmp(p, warmboot_value_rel))
|
||||||
|
{
|
||||||
|
is_set = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_close(&fp);
|
||||||
|
return is_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _ini_update_section_warmboot(const char *ini_path, const char *section_name, const char *warmboot_value_rel)
|
||||||
|
{
|
||||||
|
char tmp_path[128];
|
||||||
|
u32 len = strlen(ini_path);
|
||||||
|
if (len + 4 >= sizeof(tmp_path))
|
||||||
|
return false;
|
||||||
|
strcpy(tmp_path, ini_path);
|
||||||
|
strcat(tmp_path, ".tmp");
|
||||||
|
|
||||||
|
FIL in_fp;
|
||||||
|
FIL out_fp;
|
||||||
|
if (f_open(&in_fp, ini_path, FA_READ) != FR_OK)
|
||||||
|
return false;
|
||||||
|
if (f_open(&out_fp, tmp_path, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK)
|
||||||
|
{
|
||||||
|
f_close(&in_fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
bool in_section = false;
|
||||||
|
bool warmboot_seen = false;
|
||||||
|
bool wrote_any = false;
|
||||||
|
// Buffer trailing empty lines in the target section so the inserted/updated
|
||||||
|
// warmboot=... line stays directly after the last non-empty line.
|
||||||
|
#define MAX_PENDING_BLANKS 32
|
||||||
|
char pending_blanks[MAX_PENDING_BLANKS][256];
|
||||||
|
u32 pending_blank_count = 0;
|
||||||
|
|
||||||
|
while (_read_line_no_nl(&in_fp, line, sizeof(line)))
|
||||||
|
{
|
||||||
|
char *t = _ltrim(line);
|
||||||
|
|
||||||
|
// Section header?
|
||||||
|
if (*t == '[')
|
||||||
|
{
|
||||||
|
if (in_section && !warmboot_seen)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "warmboot=%s\n", warmboot_value_rel);
|
||||||
|
warmboot_seen = true;
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush buffered empty lines after warmboot, so they don't split it.
|
||||||
|
for (u32 i = 0; i < pending_blank_count; i++)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "%s\n", pending_blanks[i]);
|
||||||
|
}
|
||||||
|
pending_blank_count = 0;
|
||||||
|
|
||||||
|
char *end = strchr(t, ']');
|
||||||
|
if (end)
|
||||||
|
{
|
||||||
|
*end = 0;
|
||||||
|
in_section = !strcmp(t + 1, section_name);
|
||||||
|
*end = ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
f_printf(&out_fp, "%s\n", line);
|
||||||
|
wrote_any = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_section)
|
||||||
|
{
|
||||||
|
char *k = _ltrim(line);
|
||||||
|
if (strncmp(k, "warmboot", 8) == 0)
|
||||||
|
{
|
||||||
|
// Replace existing warmboot=... line inside the section.
|
||||||
|
// Allow whitespace between key and '='.
|
||||||
|
char *p = k + 8;
|
||||||
|
while (*p == ' ' || *p == '\t')
|
||||||
|
p++;
|
||||||
|
if (*p == '=')
|
||||||
|
{
|
||||||
|
// Keep the buffered empty lines where the original warmboot key appeared.
|
||||||
|
for (u32 i = 0; i < pending_blank_count; i++)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "%s\n", pending_blanks[i]);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
pending_blank_count = 0;
|
||||||
|
|
||||||
|
f_printf(&out_fp, "warmboot=%s\n", warmboot_value_rel);
|
||||||
|
warmboot_seen = true;
|
||||||
|
wrote_any = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in the target section and warmboot isn't present yet, buffer empty lines
|
||||||
|
// so warmboot will not be separated from the last non-empty entry.
|
||||||
|
if (in_section && !warmboot_seen)
|
||||||
|
{
|
||||||
|
if (*t == 0)
|
||||||
|
{
|
||||||
|
if (pending_blank_count < MAX_PENDING_BLANKS)
|
||||||
|
{
|
||||||
|
strncpy(pending_blanks[pending_blank_count], line, 255);
|
||||||
|
pending_blanks[pending_blank_count][255] = 0;
|
||||||
|
pending_blank_count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < pending_blank_count; i++)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "%s\n", pending_blanks[i]);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
pending_blank_count = 0;
|
||||||
|
|
||||||
|
f_printf(&out_fp, "%s\n", line);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-empty line: flush buffered blanks first.
|
||||||
|
for (u32 i = 0; i < pending_blank_count; i++)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "%s\n", pending_blanks[i]);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
pending_blank_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
f_printf(&out_fp, "%s\n", line);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_section && !warmboot_seen)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "warmboot=%s\n", warmboot_value_rel);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush trailing empty lines at EOF (after warmboot) if any.
|
||||||
|
for (u32 i = 0; i < pending_blank_count; i++)
|
||||||
|
{
|
||||||
|
f_printf(&out_fp, "%s\n", pending_blanks[i]);
|
||||||
|
wrote_any = true;
|
||||||
|
}
|
||||||
|
pending_blank_count = 0;
|
||||||
|
|
||||||
|
f_close(&in_fp);
|
||||||
|
f_close(&out_fp);
|
||||||
|
|
||||||
|
if (!wrote_any)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Replace original with temp.
|
||||||
|
f_unlink(ini_path);
|
||||||
|
f_rename(tmp_path, ini_path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_fixMarikoWarmbootSleep(void)
|
||||||
|
{
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Mariko Sleep Fix (Warmboot) --\n\n");
|
||||||
|
|
||||||
|
char warmboot_rel[64] = {0};
|
||||||
|
bool warmboot_generated = false;
|
||||||
|
|
||||||
|
// 1) Generate a fresh warmboot for the currently burnt fuses.
|
||||||
|
// Then we directly update the INI using that result.
|
||||||
|
gfx_printf("Erzeuge neuen Warmboot Cache...\n\n");
|
||||||
|
|
||||||
|
warmboot_info_t wb_info;
|
||||||
|
wb_extract_error_t wberr = extract_warmboot_from_pkg1_ex(&wb_info);
|
||||||
|
if (wberr == WB_SUCCESS)
|
||||||
|
{
|
||||||
|
char warmboot_sd_path[128];
|
||||||
|
get_warmboot_path(warmboot_sd_path, sizeof(warmboot_sd_path), wb_info.burnt_fuses);
|
||||||
|
|
||||||
|
if (save_warmboot_to_sd(&wb_info, warmboot_sd_path))
|
||||||
|
{
|
||||||
|
s_printf(warmboot_rel, "warmboot_mariko/wb_%02x.bin", wb_info.burnt_fuses);
|
||||||
|
warmboot_generated = true;
|
||||||
|
gfx_printf("Neues warmboot: %s\n\n", warmboot_rel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gfx_printf("Speichern auf SD fehlgeschlagen.\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gfx_printf("Warmboot Extraktion fehlgeschlagen: %s\n\n", wb_error_to_string(wberr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wb_info.data)
|
||||||
|
free(wb_info.data);
|
||||||
|
|
||||||
|
// 2) Fallback: pick the newest cached wb_xx.bin from sd:/warmboot_mariko.
|
||||||
|
if (!warmboot_generated)
|
||||||
|
{
|
||||||
|
const char *wb_dir = "sd:/warmboot_mariko";
|
||||||
|
|
||||||
|
DIR dir;
|
||||||
|
static FILINFO fno;
|
||||||
|
if (f_opendir(&dir, wb_dir) != FR_OK)
|
||||||
|
{
|
||||||
|
gfx_printf("Ordner nicht gefunden: %s\n\n", wb_dir);
|
||||||
|
gfx_printf("Bitte erst Warmboot-Extractor ausfuehren:\n");
|
||||||
|
gfx_printf("https://github.com/sthetix/Warmboot-Extractor\n\n");
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 best_fuse = 0;
|
||||||
|
u32 best_ts = 0;
|
||||||
|
bool found_any = false;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
FRESULT res = f_readdir(&dir, &fno);
|
||||||
|
if (res != FR_OK || fno.fname[0] == 0)
|
||||||
|
break;
|
||||||
|
if (fno.fattrib & AM_DIR)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
u32 fuse = 0;
|
||||||
|
if (!_parse_wb_filename_lower(fno.fname, &fuse))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
u32 ts = ((u32)fno.fdate << 16) | (u32)fno.ftime;
|
||||||
|
if (!found_any || fuse > best_fuse || (fuse == best_fuse && ts > best_ts))
|
||||||
|
{
|
||||||
|
best_fuse = fuse;
|
||||||
|
best_ts = ts;
|
||||||
|
found_any = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
if (!found_any)
|
||||||
|
{
|
||||||
|
gfx_printf("Keine passenden Dateien gefunden in %s:\n", wb_dir);
|
||||||
|
gfx_printf("wb_xx.bin (xx = lowercase hex fuse)\n\n");
|
||||||
|
gfx_printf("Bitte Warmboot-Extractor ausfuehren:\n");
|
||||||
|
gfx_printf("https://github.com/sthetix/Warmboot-Extractor\n\n");
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_printf(warmboot_rel, "warmboot_mariko/wb_%02x.bin", best_fuse);
|
||||||
|
gfx_printf("Neuestes warmboot: %s\n\n", warmboot_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ini_path = "sd:/bootloader/hekate_ipl.ini";
|
||||||
|
const char *preferred_section = "CFW-EmuMMC";
|
||||||
|
|
||||||
|
if (!_ini_section_exists(ini_path, preferred_section))
|
||||||
|
{
|
||||||
|
// Fallback: show a section chooser if CFW-EmuMMC doesn't exist.
|
||||||
|
gfx_printf("Section %s nicht gefunden.\n", preferred_section);
|
||||||
|
gfx_printf("Bitte waehle eine INI section.\n\n");
|
||||||
|
|
||||||
|
// Collect section names.
|
||||||
|
#define MAX_INI_SECTIONS 20
|
||||||
|
char section_names[MAX_INI_SECTIONS][64];
|
||||||
|
int section_count = 0;
|
||||||
|
|
||||||
|
FIL ini_fp;
|
||||||
|
if (f_open(&ini_fp, ini_path, FA_READ) != FR_OK)
|
||||||
|
{
|
||||||
|
gfx_printf("Datei nicht gefunden: %s\n", ini_path);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
while (_read_line_no_nl(&ini_fp, line, sizeof(line)))
|
||||||
|
{
|
||||||
|
char *t = _ltrim(line);
|
||||||
|
if (*t != '[')
|
||||||
|
continue;
|
||||||
|
char *end = strchr(t, ']');
|
||||||
|
if (!end)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*end = 0;
|
||||||
|
char *name = t + 1;
|
||||||
|
|
||||||
|
name = _ltrim(name);
|
||||||
|
_rstrip(name);
|
||||||
|
|
||||||
|
// Skip empty section headers like "[]"
|
||||||
|
if (!*name)
|
||||||
|
{
|
||||||
|
*end = ']';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "config") && section_count < MAX_INI_SECTIONS)
|
||||||
|
{
|
||||||
|
strncpy(section_names[section_count], name, 63);
|
||||||
|
section_names[section_count][63] = 0;
|
||||||
|
section_count++;
|
||||||
|
}
|
||||||
|
*end = ']';
|
||||||
|
}
|
||||||
|
f_close(&ini_fp);
|
||||||
|
|
||||||
|
if (section_count <= 0)
|
||||||
|
{
|
||||||
|
gfx_printf("Keine INI sections gefunden in %s.\n", ini_path);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuEntry_t entries[MAX_INI_SECTIONS + 1];
|
||||||
|
memset(entries, 0, sizeof(entries));
|
||||||
|
entries[0].optionUnion = COLORTORGB(COLOR_WHITE);
|
||||||
|
entries[0].name = "<- Zurueck";
|
||||||
|
for (int i = 0; i < section_count; i++)
|
||||||
|
{
|
||||||
|
entries[i + 1].optionUnion = COLORTORGB(COLOR_YELLOW);
|
||||||
|
entries[i + 1].name = section_names[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector_t ent = vecFromArray(entries, section_count + 1, sizeof(MenuEntry_t));
|
||||||
|
int sel = newMenu(&ent, 0, 79, 20, ENABLEB | ALWAYSREDRAW, section_count + 1);
|
||||||
|
// newMenu returns 0 for "<- Back" and also when the user presses B.
|
||||||
|
if (sel == 0)
|
||||||
|
return;
|
||||||
|
if (sel < 0 || sel > section_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const char *selected_section = section_names[sel - 1];
|
||||||
|
// Continue below with chosen section.
|
||||||
|
// Clear and re-print banner so the header stays visible (success/fail).
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Mariko Sleep Fix (Warmboot) --\n\n");
|
||||||
|
|
||||||
|
if (_ini_section_warmboot_is(ini_path, selected_section, warmboot_rel))
|
||||||
|
{
|
||||||
|
gfx_printf("\nwarmboot=%s existiert schon in [%s].\n", warmboot_rel, selected_section);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ini_update_section_warmboot(ini_path, selected_section, warmboot_rel);
|
||||||
|
gfx_printf("\nAktualisiert in [%s].\n", selected_section);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preferred path: always update CFW-EmuMMC directly.
|
||||||
|
if (_ini_section_warmboot_is(ini_path, preferred_section, warmboot_rel))
|
||||||
|
{
|
||||||
|
gfx_printf("\nwarmboot=%s existiert schon in [%s].\n", warmboot_rel, preferred_section);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ini_update_section_warmboot(ini_path, preferred_section, warmboot_rel);
|
||||||
|
gfx_printf("\nAktualisiert in [%s].\n", preferred_section);
|
||||||
|
gfx_printf("Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_stillNoBootInfo(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\n\n-- Meine Switch startet immer noch nicht.\n\n");
|
||||||
|
|
||||||
|
gfx_printf("%kSteckt eine Spiel-Cardrige im Slot?\n", COLOR_WHITE);
|
||||||
|
gfx_printf("Entferne sie und starte neu.\n\n");
|
||||||
|
|
||||||
|
gfx_printf("%kHast du vor kurzem Atmosphere/OmniNX aktualisiert?\n", COLOR_WHITE);
|
||||||
|
gfx_printf("Stecke die SD-Karte in deinen PC, hol das neue Paket vom NiklasCFW Discord-Server und entpacke die .zip Datei auf deine SD-Karte und ueberschreibe alle Dateien.\nDanach kannst du den OmniNX-Installer-Payload ausfuehren.\n\n");
|
||||||
|
|
||||||
|
gfx_printf("%kHast du eine neue SD-Karte gekauft?\n", COLOR_WHITE);
|
||||||
|
gfx_printf("Vergewissere dich das es keine fake/SanDisk Karte ist.\n\n");
|
||||||
|
|
||||||
|
gfx_printf("\n\n Druecke eine Taste um zurueckzukehren");
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_ViewCredits(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
gfx_printf("\nAllgemeinerProblemLoeser %d.%d.%d\nVon Team Neptune - (NiklasCFW Fork - Uebersetzt von Switch Bros.)\n\nBasierend auf TegraExplorer von SuchMemeManySkill,\nLockpick_RCM & Hekate, von shchmue & CTCaer\n\n\n", APL_VER_MJ, APL_VER_MN, APL_VER_BF);
|
||||||
|
hidWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void m_entry_fixAll(){
|
||||||
|
gfx_clearscreen();
|
||||||
|
m_entry_deleteBootFlags();
|
||||||
|
m_entry_deleteInstalledThemes();
|
||||||
|
|
||||||
|
m_entry_stillNoBootInfo();
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
#include <utils/util.h>
|
#include <utils/util.h>
|
||||||
|
|
||||||
void m_entry_fixArchiveBit(u32 type);
|
void m_entry_fixArchiveBit(u32 type);
|
||||||
void m_entry_fixAIOUpdate();
|
void m_entry_fixAMSError_4200(void);
|
||||||
void m_entry_fixClingWrap();
|
void m_entry_fixAMSError_6900(void);
|
||||||
|
void m_entry_fixAMSError_BD00(void);
|
||||||
void m_entry_deleteInstalledThemes();
|
void m_entry_deleteInstalledThemes();
|
||||||
void m_entry_deleteBootFlags();
|
void m_entry_deleteBootFlags();
|
||||||
void m_entry_ViewCredits();
|
void m_entry_ViewCredits();
|
||||||
void m_entry_fixAll();
|
void m_entry_fixAll();
|
||||||
void m_entry_stillNoBootInfo();
|
void m_entry_stillNoBootInfo();
|
||||||
void m_entry_fixMacSpecialFolders();
|
void m_entry_fixMacSpecialFolders();
|
||||||
|
void m_entry_fixMarikoWarmbootSleep(void);
|
||||||
452
source/cpr/cpr.c
452
source/cpr/cpr.c
@@ -1,452 +0,0 @@
|
|||||||
// #include <utils/util.h>
|
|
||||||
// #include "tools.h"
|
|
||||||
#include <storage/sd.h>
|
|
||||||
#include "../fs/readers/folderReader.h"
|
|
||||||
#include "../fs/fstypes.h"
|
|
||||||
#include "../fs/fscopy.h"
|
|
||||||
#include <utils/sprintf.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "../gfx/gfx.h"
|
|
||||||
#include "../gfx/gfxutils.h"
|
|
||||||
#include "../gfx/menu.h"
|
|
||||||
#include "../hid/hid.h"
|
|
||||||
// #include "utils.h"
|
|
||||||
#include "../utils/utils.h"
|
|
||||||
#include "../fs/fsutils.h"
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
// #include <dirent.h>
|
|
||||||
// #include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
void _DeleteFileSimple(char *thing){
|
|
||||||
//char *thing = CombinePaths(path, entry.name);
|
|
||||||
int res = f_unlink(thing);
|
|
||||||
if (res)
|
|
||||||
DrawError(newErrCode(res));
|
|
||||||
free(thing);
|
|
||||||
}
|
|
||||||
void _RenameFileSimple(char *sourcePath, char *destPath){
|
|
||||||
int res = f_rename(sourcePath, destPath);
|
|
||||||
if (res){
|
|
||||||
DrawError(newErrCode(res));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ErrCode_t _FolderDelete(const char *path){
|
|
||||||
int res = 0;
|
|
||||||
ErrCode_t ret = newErrCode(0);
|
|
||||||
u32 x, y;
|
|
||||||
gfx_con_getpos(&x, &y);
|
|
||||||
Vector_t fileVec = ReadFolder(path, &res);
|
|
||||||
if (res){
|
|
||||||
ret = newErrCode(res);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
vecDefArray(FSEntry_t *, fs, fileVec);
|
|
||||||
for (int i = 0; i < fileVec.count && !ret.err; i++){
|
|
||||||
char *temp = CombinePaths(path, fs[i].name);
|
|
||||||
if (fs[i].isDir){
|
|
||||||
ret = _FolderDelete(temp);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = f_unlink(temp);
|
|
||||||
if (res){
|
|
||||||
ret = newErrCode(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ret.err){
|
|
||||||
res = f_unlink(path);
|
|
||||||
if (res)
|
|
||||||
ret = newErrCode(res);
|
|
||||||
}
|
|
||||||
clearFileVector(&fileVec);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
int _StartsWith(const char *a, const char *b)
|
|
||||||
{
|
|
||||||
if(strncmp(a, b, strlen(b)) == 0) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int listdir(char *path, u32 hos_folder)
|
|
||||||
{
|
|
||||||
FRESULT res;
|
|
||||||
DIR dir;
|
|
||||||
u32 dirLength = 0;
|
|
||||||
static FILINFO fno;
|
|
||||||
|
|
||||||
// Open directory.
|
|
||||||
res = f_opendir(&dir, path);
|
|
||||||
if (res != FR_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
dirLength = strlen(path);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// Clear file or folder path.
|
|
||||||
path[dirLength] = 0;
|
|
||||||
// Read a directory item.
|
|
||||||
res = f_readdir(&dir, &fno);
|
|
||||||
// Break on error or end of dir.
|
|
||||||
if (res != FR_OK || fno.fname[0] == 0)
|
|
||||||
break;
|
|
||||||
// Skip official Nintendo dir if started from root.
|
|
||||||
if (!hos_folder && !strcmp(fno.fname, "Nintendo"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// // Set new directory or file.
|
|
||||||
memcpy(&path[dirLength], "/", 1);
|
|
||||||
memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1);
|
|
||||||
// gfx_printf("THING: %s\n", fno.fname);
|
|
||||||
// gfx_printf("Path: %s\n", dir);
|
|
||||||
// Is it a directory?
|
|
||||||
if (fno.fattrib & AM_DIR)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
strcmp(fno.fname, ".Trash") == 0 ||
|
|
||||||
strcmp(fno.fname, ".Trashes") == 0 ||
|
|
||||||
strcmp(fno.fname, ".DS_Store") == 0 ||
|
|
||||||
strcmp(fno.fname, ".Spotlight-V100") == 0 ||
|
|
||||||
strcmp(fno.fname, ".apDisk") == 0 ||
|
|
||||||
strcmp(fno.fname, ".VolumeIcon.icns") == 0 ||
|
|
||||||
strcmp(fno.fname, ".fseventsd") == 0 ||
|
|
||||||
strcmp(fno.fname, ".TemporaryItems") == 0
|
|
||||||
) {
|
|
||||||
_FolderDelete(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter the directory.
|
|
||||||
listdir(path, 0);
|
|
||||||
if (res != FR_OK)
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
strcmp(fno.fname, ".DS_Store") == 0 ||
|
|
||||||
strcmp(fno.fname, ".Spotlight-V100") == 0 ||
|
|
||||||
strcmp(fno.fname, ".apDisk") == 0 ||
|
|
||||||
strcmp(fno.fname, ".VolumeIcon.icns") == 0 ||
|
|
||||||
strcmp(fno.fname, ".fseventsd") == 0 ||
|
|
||||||
strcmp(fno.fname, ".TemporaryItems") == 0 ||
|
|
||||||
_StartsWith(fno.fname, "._")
|
|
||||||
) {
|
|
||||||
_DeleteFileSimple(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f_closedir(&dir);
|
|
||||||
free(path);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _fix_attributes(char *path, u32 *total, u32 hos_folder, u32 check_first_run){
|
|
||||||
FRESULT res;
|
|
||||||
DIR dir;
|
|
||||||
u32 dirLength = 0;
|
|
||||||
static FILINFO fno;
|
|
||||||
|
|
||||||
if (check_first_run)
|
|
||||||
{
|
|
||||||
// Read file attributes.
|
|
||||||
res = f_stat(path, &fno);
|
|
||||||
if (res != FR_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
// Check if archive bit is set.
|
|
||||||
if (fno.fattrib & AM_ARC)
|
|
||||||
{
|
|
||||||
*(u32 *)total = *(u32 *)total + 1;
|
|
||||||
f_chmod(path, 0, AM_ARC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open directory.
|
|
||||||
res = f_opendir(&dir, path);
|
|
||||||
if (res != FR_OK)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
dirLength = strlen(path);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// Clear file or folder path.
|
|
||||||
path[dirLength] = 0;
|
|
||||||
|
|
||||||
// Read a directory item.
|
|
||||||
res = f_readdir(&dir, &fno);
|
|
||||||
|
|
||||||
// Break on error or end of dir.
|
|
||||||
if (res != FR_OK || fno.fname[0] == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Skip official Nintendo dir if started from root.
|
|
||||||
if (!hos_folder && !strcmp(fno.fname, "Nintendo"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Set new directory or file.
|
|
||||||
memcpy(&path[dirLength], "/", 1);
|
|
||||||
memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1);
|
|
||||||
|
|
||||||
// Check if archive bit is set.
|
|
||||||
if (fno.fattrib & AM_ARC)
|
|
||||||
{
|
|
||||||
*total = *total + 1;
|
|
||||||
f_chmod(path, 0, AM_ARC);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is it a directory?
|
|
||||||
if (fno.fattrib & AM_DIR)
|
|
||||||
{
|
|
||||||
// Set archive bit to NCA folders.
|
|
||||||
if (hos_folder && !strcmp(fno.fname + strlen(fno.fname) - 4, ".nca"))
|
|
||||||
{
|
|
||||||
*total = *total + 1;
|
|
||||||
f_chmod(path, AM_ARC, AM_ARC);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter the directory.
|
|
||||||
res = _fix_attributes(path, total, hos_folder, 0);
|
|
||||||
if (res != FR_OK)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void m_entry_fixArchiveBit(u32 type){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Fix Archive Bits\n\n");
|
|
||||||
|
|
||||||
char path[256];
|
|
||||||
char label[16];
|
|
||||||
|
|
||||||
u32 total = 0;
|
|
||||||
if (sd_mount())
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
strcpy(path, "/");
|
|
||||||
strcpy(label, "SD Card");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
default:
|
|
||||||
strcpy(path, "/Nintendo");
|
|
||||||
strcpy(label, "Nintendo folder");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx_printf("Traversing all %s files!\nThis may take some time...\n\n", label);
|
|
||||||
_fix_attributes(path, &total, type, type);
|
|
||||||
gfx_printf("%kTotal archive bits cleared: %d!%k", 0xFF96FF00, total, 0xFFCCCCCC);
|
|
||||||
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void m_entry_fixAIOUpdate(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Fix broken Switch-AiO-Updater update.\n\n");
|
|
||||||
|
|
||||||
char *aio_fs_path = CpyStr("sd:/atmosphere/fusee-secondary.bin.aio");
|
|
||||||
char *aio_p_path = CpyStr("sd:/sept/payload.bin.aio");
|
|
||||||
char *aio_strt_path = CpyStr("sd:/atmosphere/stratosphere.romfs.aio");
|
|
||||||
|
|
||||||
char *o_fs_path = CpyStr("sd:/atmosphere/fusee-secondary.bin");
|
|
||||||
char *o_p_path = CpyStr("sd:/sept/payload.bin");
|
|
||||||
char *o_strt_path = CpyStr("sd:/atmosphere/stratosphere.romfs");
|
|
||||||
|
|
||||||
if (FileExists(aio_fs_path)) {
|
|
||||||
gfx_printf("Detected aio updated fusee-secondary file -> replacing original\n\n");
|
|
||||||
if (FileExists(o_fs_path)) {
|
|
||||||
_DeleteFileSimple(o_fs_path);
|
|
||||||
}
|
|
||||||
_RenameFileSimple(aio_fs_path, o_fs_path);
|
|
||||||
}
|
|
||||||
free(aio_fs_path);
|
|
||||||
free(o_fs_path);
|
|
||||||
|
|
||||||
if (FileExists(aio_p_path)) {
|
|
||||||
gfx_printf("Detected aio updated paload file -> replacing original\n\n");
|
|
||||||
if (FileExists(o_p_path)) {
|
|
||||||
_DeleteFileSimple(o_p_path);
|
|
||||||
}
|
|
||||||
_RenameFileSimple(aio_p_path, o_p_path);
|
|
||||||
}
|
|
||||||
free(aio_p_path);
|
|
||||||
free(o_p_path);
|
|
||||||
|
|
||||||
if (FileExists(aio_strt_path)) {
|
|
||||||
gfx_printf("Detected aio updated stratosphere file -> replacing original\n\n");
|
|
||||||
if (FileExists(o_strt_path)) {
|
|
||||||
_DeleteFileSimple(o_strt_path);
|
|
||||||
}
|
|
||||||
_RenameFileSimple(aio_strt_path, o_strt_path);
|
|
||||||
}
|
|
||||||
free(aio_strt_path);
|
|
||||||
free(o_strt_path);
|
|
||||||
|
|
||||||
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_fixClingWrap(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Fixing ClingWrap.\n\n");
|
|
||||||
char *bpath = CpyStr("sd:/_b0otloader");
|
|
||||||
char *bopath = CpyStr("sd:/bootloader");
|
|
||||||
char *kpath = CpyStr("sd:/atmosphere/_k1ps");
|
|
||||||
char *kopath = CpyStr("sd:/atmosphere/kips");
|
|
||||||
|
|
||||||
char *ppath = CpyStr("sd:/bootloader/_patchesCW.ini");
|
|
||||||
char *popath = CpyStr("sd:/atmosphere/patches.ini");
|
|
||||||
|
|
||||||
if (FileExists(bpath)) {
|
|
||||||
if (FileExists(bopath)) {
|
|
||||||
FolderDelete(bopath);
|
|
||||||
}
|
|
||||||
int res = f_rename(bpath, bopath);
|
|
||||||
if (res){
|
|
||||||
DrawError(newErrCode(res));
|
|
||||||
}
|
|
||||||
gfx_printf("-- Fixed Bootloader\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileExists(kpath)) {
|
|
||||||
if (FileExists(kopath)) {
|
|
||||||
FolderDelete(kopath);
|
|
||||||
}
|
|
||||||
int res = f_rename(kpath, kopath);
|
|
||||||
if (res){
|
|
||||||
DrawError(newErrCode(res));
|
|
||||||
}
|
|
||||||
gfx_printf("-- Fixed kips\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileExists(ppath)) {
|
|
||||||
if (FileExists(popath)) {
|
|
||||||
_DeleteFileSimple(popath);
|
|
||||||
}
|
|
||||||
_RenameFileSimple(ppath,popath);
|
|
||||||
gfx_printf("-- Fixed patches.ini\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(bpath);
|
|
||||||
free(bopath);
|
|
||||||
free(kpath);
|
|
||||||
free(kopath);
|
|
||||||
free(ppath);
|
|
||||||
free(popath);
|
|
||||||
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deleteTheme(char* basePath, char* folderId){
|
|
||||||
char *path = CombinePaths(basePath, folderId);
|
|
||||||
if (FileExists(path)) {
|
|
||||||
gfx_printf("-- Theme found: %s\n", path);
|
|
||||||
FolderDelete(path);
|
|
||||||
}
|
|
||||||
free(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_deleteInstalledThemes(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Deleting installed themes.\n\n");
|
|
||||||
_deleteTheme("sd:/atmosphere/contents", "0100000000001000");
|
|
||||||
_deleteTheme("sd:/atmosphere/contents", "0100000000001007");
|
|
||||||
_deleteTheme("sd:/atmosphere/contents", "0100000000001013");
|
|
||||||
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_deleteBootFlags(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Disabling automatic sysmodule startup.\n\n");
|
|
||||||
char *storedPath = CpyStr("sd:/atmosphere/contents");
|
|
||||||
int readRes = 0;
|
|
||||||
Vector_t fileVec = ReadFolder(storedPath, &readRes);
|
|
||||||
if (readRes){
|
|
||||||
clearFileVector(&fileVec);
|
|
||||||
DrawError(newErrCode(readRes));
|
|
||||||
} else {
|
|
||||||
vecDefArray(FSEntry_t*, fsEntries, fileVec);
|
|
||||||
for (int i = 0; i < fileVec.count; i++){
|
|
||||||
|
|
||||||
char *suf = "/flags/boot2.flag";
|
|
||||||
char *flagPath = CombinePaths(storedPath, fsEntries[i].name);
|
|
||||||
flagPath = CombinePaths(flagPath, suf);
|
|
||||||
|
|
||||||
if (FileExists(flagPath)) {
|
|
||||||
gfx_printf("Deleting: %s\n", flagPath);
|
|
||||||
_DeleteFileSimple(flagPath);
|
|
||||||
}
|
|
||||||
free(flagPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void m_entry_fixMacSpecialFolders(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- Fix mac folders (this can take some time, please wait.)\n\n");
|
|
||||||
listdir("/", 0);
|
|
||||||
gfx_printf("\n\rDone, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
|
|
||||||
// browse path
|
|
||||||
// list files & folders
|
|
||||||
// if file -> delete
|
|
||||||
// if folder !== nintendo
|
|
||||||
// if folder m_entry_fixMacSpecialFolders with new path
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_stillNoBootInfo(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\n\n-- My switch still does not boot.\n\n");
|
|
||||||
|
|
||||||
gfx_printf("%kDo you have a gamecard inserted?\n", COLOR_WHITE);
|
|
||||||
gfx_printf("Try taking it out and reboot.\n\n");
|
|
||||||
|
|
||||||
gfx_printf("%kDid you recently update Atmosphere/DeepSea?\n", COLOR_WHITE);
|
|
||||||
gfx_printf("Insert your sdcard into a computer, delete 'atmosphere', 'bootloader' & 'sept', download your preffered CFW and put the files back on your switch.\n\n");
|
|
||||||
|
|
||||||
gfx_printf("%kDid you just buy a new SD-card?\n", COLOR_WHITE);
|
|
||||||
gfx_printf("Make sure its not a fake card.\n\n");
|
|
||||||
|
|
||||||
gfx_printf("\n\n Done, press a key to proceed.");
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_ViewCredits(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
gfx_printf("\nCommon Problem Resolver v%d.%d.%d\nBy Team Neptune\n\nBased on TegraExplorer by SuchMemeManySkill,\nLockpick_RCM & Hekate, from shchmue & CTCaer\n\n\n", LP_VER_MJ, LP_VER_MN, LP_VER_BF);
|
|
||||||
hidWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void m_entry_fixAll(){
|
|
||||||
gfx_clearscreen();
|
|
||||||
m_entry_deleteBootFlags();
|
|
||||||
m_entry_deleteInstalledThemes();
|
|
||||||
m_entry_fixClingWrap();
|
|
||||||
m_entry_fixAIOUpdate();
|
|
||||||
|
|
||||||
|
|
||||||
m_entry_stillNoBootInfo();
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@ void gfx_printTopInfo() {
|
|||||||
bq24193_get_property(BQ24193_ChargeStatus, ¤t_charge_status);
|
bq24193_get_property(BQ24193_ChargeStatus, ¤t_charge_status);
|
||||||
SETCOLOR(COLOR_DEFAULT, COLOR_WHITE);
|
SETCOLOR(COLOR_DEFAULT, COLOR_WHITE);
|
||||||
gfx_con_setpos(0, 0);
|
gfx_con_setpos(0, 0);
|
||||||
gfx_printf("CPR %d.%d.%d | Battery: %d%% %c\n", LP_VER_MJ, LP_VER_MN, LP_VER_BF, battery >> 8, ((current_charge_status) ? 129 : 32));
|
gfx_printf("APL %d.%d.%d | Battery: %d%% %c\n", APL_VER_MJ, APL_VER_MN, APL_VER_BF, battery >> 8, ((current_charge_status) ? 129 : 32));
|
||||||
RESETCOLOR;
|
RESETCOLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
#define COLOR_GREY 0xFF888888
|
#define COLOR_GREY 0xFF888888
|
||||||
#define COLOR_DARKGREY 0xFF333333
|
#define COLOR_DARKGREY 0xFF333333
|
||||||
#define COLOR_ORANGE 0xFFFFA500
|
#define COLOR_ORANGE 0xFFFFA500
|
||||||
#define COLOR_GREEN 0xFF008000
|
#define COLOR_GREEN 0xFF00FF00
|
||||||
#define COLOR_VIOLET 0xFFEE82EE
|
#define COLOR_VIOLET 0xFFEE82EE
|
||||||
|
#define COLOR_PURPLE 0xFF7C49EF
|
||||||
#define COLOR_BLUE 0xFF0000FF
|
#define COLOR_BLUE 0xFF0000FF
|
||||||
|
#define COLOR_CYAN 0xFF00CCFF
|
||||||
#define COLOR_RED 0xFF0000FF
|
#define COLOR_RED 0xFF0000FF
|
||||||
#define COLOR_YELLOW 0xFFFF0000
|
#define COLOR_YELLOW 0xFFFFFF00
|
||||||
|
|
||||||
#define COLORTORGB(color) (color & 0x00FFFFFF)
|
#define COLORTORGB(color) (color & 0x00FFFFFF)
|
||||||
#define SETCOLOR(fg, bg) gfx_con_setcol(fg, 1, bg)
|
#define SETCOLOR(fg, bg) gfx_con_setcol(fg, 1, bg)
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ Input_t *hidRead(){
|
|||||||
LbaseY = controller->lstick_y;
|
LbaseY = controller->lstick_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.up = (controller->up || (controller->lstick_y > LbaseY + 500)) ? 1 : 0;
|
inputs.up = (controller->up || (controller->lstick_y > LbaseY + 500) || inputs.volp) ? 1 : 0;
|
||||||
inputs.down = (controller->down || (controller->lstick_y < LbaseY - 500)) ? 1 : 0;
|
inputs.down = (controller->down || (controller->lstick_y < LbaseY - 500) || inputs.volm) ? 1 : 0;
|
||||||
inputs.left = (controller->left || (controller->lstick_x < LbaseX - 500)) ? 1 : 0;
|
inputs.left = (controller->left || (controller->lstick_x < LbaseX - 500)) ? 1 : 0;
|
||||||
inputs.right = (controller->right || (controller->lstick_x > LbaseX + 500)) ? 1 : 0;
|
inputs.right = (controller->right || (controller->lstick_x > LbaseX + 500)) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,9 +242,9 @@ scriptFunction(funcWait){
|
|||||||
|
|
||||||
scriptFunction(funcGetVer){
|
scriptFunction(funcGetVer){
|
||||||
int *arr = malloc(3 * sizeof(int));
|
int *arr = malloc(3 * sizeof(int));
|
||||||
arr[0] = LP_VER_MJ;
|
arr[0] = APL_VER_MJ;
|
||||||
arr[1] = LP_VER_MN;
|
arr[1] = APL_VER_MN;
|
||||||
arr[2] = LP_VER_BF;
|
arr[2] = APL_VER_BF;
|
||||||
Vector_t res = vecFromArray(arr, 3, sizeof(int));
|
Vector_t res = vecFromArray(arr, 3, sizeof(int));
|
||||||
return newVar(IntArrayType, 1, .vectorType = res);
|
return newVar(IntArrayType, 1, .vectorType = res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,20 +25,23 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../cpr/cpr.h"
|
#include "../apl/apl.h"
|
||||||
|
|
||||||
extern hekate_config h_cfg;
|
extern hekate_config h_cfg;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MainExplore = 0,
|
MainExplore = 0,
|
||||||
DeleteBootFlags,
|
DeleteBootFlags,
|
||||||
DeleteThemes,
|
|
||||||
FixClingWrap,
|
|
||||||
FixAIOUpdaterBoot,
|
|
||||||
FixArchiveBitA,
|
FixArchiveBitA,
|
||||||
FixArchiveBitN,
|
FixArchiveBitN,
|
||||||
// FixAll,
|
// FixAll,
|
||||||
|
MainAMS,
|
||||||
|
DeleteThemes,
|
||||||
|
FixAMS_4200,
|
||||||
|
FixAMS_6900,
|
||||||
|
FixAMS_BD00,
|
||||||
MainConvenience,
|
MainConvenience,
|
||||||
|
FixMarikoWarmbootSleep,
|
||||||
FixMacSpecialFolders,
|
FixMacSpecialFolders,
|
||||||
MainOther,
|
MainOther,
|
||||||
MainViewStillNoBootInfo,
|
MainViewStillNoBootInfo,
|
||||||
@@ -48,32 +51,34 @@ enum {
|
|||||||
MainRebootRCM,
|
MainRebootRCM,
|
||||||
// MainRebootNormal,
|
// MainRebootNormal,
|
||||||
MainRebootHekate,
|
MainRebootHekate,
|
||||||
MainRebootAMS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MenuEntry_t mainMenuEntries[] = {
|
MenuEntry_t mainMenuEntries[] = {
|
||||||
[MainExplore] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Bootfixes --"},
|
[MainExplore] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Behebe Start Probleme --"},
|
||||||
[DeleteBootFlags] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Disable automatic sysmodule startup"},
|
[DeleteBootFlags] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Automatisches starten der sysmodule deaktivieren"},
|
||||||
[DeleteThemes] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Delete installed themes"},
|
[FixArchiveBitA] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Behebe Archive Bit (alle Ordner ausser Nintendo)"},
|
||||||
[FixClingWrap] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Fix ClingWrap"},
|
[FixArchiveBitN] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Behebe Archive Bit (Nintendo Ordner)"},
|
||||||
[FixAIOUpdaterBoot] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Fix Switch-AiO-Updater update"},
|
|
||||||
[FixArchiveBitA] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Fix archive bit (all folders except nintendo)"},
|
|
||||||
[FixArchiveBitN] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Fix archive bit (nintendo folder)"},
|
|
||||||
// [FixAll] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Try everything"},
|
// [FixAll] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Try everything"},
|
||||||
|
|
||||||
[MainConvenience] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Convenience --"},
|
[MainAMS] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Atmosphere Fehlercodes --"},
|
||||||
[FixMacSpecialFolders] = {.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Remove special folders created by Mac"},
|
[DeleteThemes] = {.optionUnion = COLORTORGB(COLOR_CYAN), .name = "Fix 0100000000001000 (Installiertes Theme)"},
|
||||||
|
[FixAMS_4200] = {.optionUnion = COLORTORGB(COLOR_CYAN), .name = "Fix 4200000000000010 (ldn_mitm)"},
|
||||||
|
[FixAMS_6900] = {.optionUnion = COLORTORGB(COLOR_CYAN), .name = "Fix 690000000000000D (sys-con)"},
|
||||||
|
[FixAMS_BD00] = {.optionUnion = COLORTORGB(COLOR_CYAN), .name = "Fix 010000000000BD00 (MissionControl)"},
|
||||||
|
|
||||||
[MainOther] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Other --"},
|
[MainConvenience] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Bequemlichkeit --"},
|
||||||
[MainViewStillNoBootInfo] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "My switch still does not boot"},
|
[FixMarikoWarmbootSleep] = {.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Mariko Sleep Fix (Warmboot)"},
|
||||||
[MainViewCredits] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Credits"},
|
[FixMacSpecialFolders] = {.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Entferne spezielle MacOS Dateien"},
|
||||||
|
|
||||||
[MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Exit --"},
|
[MainOther] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Sonstiges --"},
|
||||||
[MainPowerOff] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Power off"},
|
[MainViewStillNoBootInfo] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Meine Switch startet nicht mehr"},
|
||||||
[MainRebootRCM] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to RCM"},
|
[MainViewCredits] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Ueber"},
|
||||||
// [MainRebootNormal] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot normally"},
|
|
||||||
[MainRebootHekate] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to bootloader/update.bin"},
|
[MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Beenden --"},
|
||||||
[MainRebootAMS] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to atmosphere/reboot_payload.bin"}
|
[MainPowerOff] = {.optionUnion = COLORTORGB(COLOR_PURPLE), .name = "Ausschalten"},
|
||||||
|
[MainRebootRCM] = {.optionUnion = COLORTORGB(COLOR_PURPLE), .name = "Neustart in RCM-Modus"},
|
||||||
|
// [MainRebootNormal] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Normaler Neustart"},
|
||||||
|
[MainRebootHekate] = {.optionUnion = COLORTORGB(COLOR_PURPLE), .name = "Neustart in hekate"}
|
||||||
|
|
||||||
|
|
||||||
// [MainBrowseSd] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Browse SD"},
|
// [MainBrowseSd] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Browse SD"},
|
||||||
@@ -102,10 +107,6 @@ void HandleSD(){
|
|||||||
extern bool is_sd_inited;
|
extern bool is_sd_inited;
|
||||||
extern int launch_payload(char *path);
|
extern int launch_payload(char *path);
|
||||||
|
|
||||||
void RebootToAMS(){
|
|
||||||
launch_payload("sd:/atmosphere/reboot_payload.bin");
|
|
||||||
}
|
|
||||||
|
|
||||||
void RebootToHekate(){
|
void RebootToHekate(){
|
||||||
launch_payload("sd:/bootloader/update.bin");
|
launch_payload("sd:/bootloader/update.bin");
|
||||||
}
|
}
|
||||||
@@ -130,17 +131,18 @@ void archBitHelperN(){
|
|||||||
menuPaths mainMenuPaths[] = {
|
menuPaths mainMenuPaths[] = {
|
||||||
[DeleteBootFlags] = m_entry_deleteBootFlags,
|
[DeleteBootFlags] = m_entry_deleteBootFlags,
|
||||||
[DeleteThemes] = m_entry_deleteInstalledThemes,
|
[DeleteThemes] = m_entry_deleteInstalledThemes,
|
||||||
[FixClingWrap] = m_entry_fixClingWrap,
|
|
||||||
[FixAIOUpdaterBoot] = m_entry_fixAIOUpdate,
|
|
||||||
[FixArchiveBitA] = archBitHelperA,
|
[FixArchiveBitA] = archBitHelperA,
|
||||||
[FixArchiveBitN] = archBitHelperN,
|
[FixArchiveBitN] = archBitHelperN,
|
||||||
|
[FixAMS_4200] = m_entry_fixAMSError_4200,
|
||||||
|
[FixAMS_6900] = m_entry_fixAMSError_6900,
|
||||||
|
[FixAMS_BD00] = m_entry_fixAMSError_BD00,
|
||||||
[FixMacSpecialFolders] = m_entry_fixMacSpecialFolders,
|
[FixMacSpecialFolders] = m_entry_fixMacSpecialFolders,
|
||||||
|
[FixMarikoWarmbootSleep] = m_entry_fixMarikoWarmbootSleep,
|
||||||
// [FixAll] = m_entry_fixAll,
|
// [FixAll] = m_entry_fixAll,
|
||||||
[MainViewStillNoBootInfo] = m_entry_stillNoBootInfo,
|
[MainViewStillNoBootInfo] = m_entry_stillNoBootInfo,
|
||||||
[MainRebootHekate] = RebootToHekate,
|
[MainRebootHekate] = RebootToHekate,
|
||||||
[MainRebootRCM] = reboot_rcm,
|
[MainRebootRCM] = reboot_rcm,
|
||||||
[MainPowerOff] = power_off,
|
[MainPowerOff] = power_off,
|
||||||
[MainRebootAMS] = RebootToAMS,
|
|
||||||
[MainViewCredits] = m_entry_ViewCredits,
|
[MainViewCredits] = m_entry_ViewCredits,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,9 +153,9 @@ void EnterMainMenu(){
|
|||||||
sd_unmount();
|
sd_unmount();
|
||||||
|
|
||||||
// // -- Exit --
|
// // -- Exit --
|
||||||
mainMenuEntries[MainRebootAMS].hide = (!sd_get_card_mounted() || !FileExists("sd:/atmosphere/reboot_payload.bin"));
|
|
||||||
mainMenuEntries[MainRebootHekate].hide = (!sd_get_card_mounted() || !FileExists("sd:/bootloader/update.bin"));
|
mainMenuEntries[MainRebootHekate].hide = (!sd_get_card_mounted() || !FileExists("sd:/bootloader/update.bin"));
|
||||||
mainMenuEntries[MainRebootRCM].hide = h_cfg.t210b01;
|
mainMenuEntries[MainRebootRCM].hide = h_cfg.t210b01;
|
||||||
|
mainMenuEntries[FixMarikoWarmbootSleep].hide = !h_cfg.t210b01;
|
||||||
|
|
||||||
gfx_clearscreen();
|
gfx_clearscreen();
|
||||||
gfx_putc('\n');
|
gfx_putc('\n');
|
||||||
|
|||||||
@@ -325,11 +325,11 @@ void TakeScreenshot(){
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
char *name, *path;
|
char *name, *path;
|
||||||
const char basepath[] = "sd:/tegraexplorer/screenshots";
|
const char basepath[] = "sd:/APL/screenshots";
|
||||||
name = malloc(40);
|
name = malloc(40);
|
||||||
s_printf(name, "Screenshot_%08X.bmp", get_tmr_us());
|
s_printf(name, "Screenshot_%08X.bmp", get_tmr_us());
|
||||||
|
|
||||||
f_mkdir("sd:/tegraexplorer");
|
f_mkdir("sd:/APL");
|
||||||
f_mkdir(basepath);
|
f_mkdir(basepath);
|
||||||
path = CombinePaths(basepath, name);
|
path = CombinePaths(basepath, name);
|
||||||
free(name);
|
free(name);
|
||||||
|
|||||||
360
source/warmboot/warmboot_extractor.c
Normal file
360
source/warmboot/warmboot_extractor.c
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
* Warmboot Extractor
|
||||||
|
* Based on Atmosphere fusee_setup_horizon.cpp
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2025 Atmosphère-NX
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "warmboot_extractor.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <mem/heap.h>
|
||||||
|
|
||||||
|
#include <soc/fuse.h>
|
||||||
|
#include <soc/timer.h>
|
||||||
|
|
||||||
|
#include <sec/se.h>
|
||||||
|
|
||||||
|
#include "../storage/emummc.h"
|
||||||
|
#include "../storage/mountmanager.h"
|
||||||
|
#include <storage/emmc.h>
|
||||||
|
|
||||||
|
#include <libs/fatfs/ff.h>
|
||||||
|
#include <utils/sprintf.h>
|
||||||
|
|
||||||
|
// Mariko keyslot for BEK (Boot Encryption Key)
|
||||||
|
#define KS_MARIKO_BEK 13
|
||||||
|
|
||||||
|
// Helper function to check if this is Mariko hardware
|
||||||
|
bool is_mariko(void)
|
||||||
|
{
|
||||||
|
u32 odm4 = fuse_read_odm(4);
|
||||||
|
|
||||||
|
// Extract hardware type from ODM4 bits (same logic as Atmosphere fuse_api.cpp)
|
||||||
|
u32 hw_type = ((odm4 >> 2) & 1) | (((odm4 >> 8) & 1) << 1) | (((odm4 >> 16) & 0xF) << 2);
|
||||||
|
|
||||||
|
// Mariko hardware types: Iowa (0x04), Hoag (0x08), Aula (0x10)
|
||||||
|
if (hw_type == 0x04 || hw_type == 0x08 || hw_type == 0x10)
|
||||||
|
return true;
|
||||||
|
if (hw_type == 0x01) // Icosa
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// For 0x02 (Calcio/Copper), fall back to DRAM ID check
|
||||||
|
return (fuse_read_dramid(false) >= 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read burnt fuse count (ODM6 + ODM7)
|
||||||
|
u8 get_burnt_fuses(void)
|
||||||
|
{
|
||||||
|
u8 fuse_count = 0;
|
||||||
|
u32 fuse_odm6 = fuse_read_odm(6);
|
||||||
|
u32 fuse_odm7 = fuse_read_odm(7);
|
||||||
|
|
||||||
|
// Count bits in ODM6
|
||||||
|
for (u32 i = 0; i < 32; i++)
|
||||||
|
if ((fuse_odm6 >> i) & 1)
|
||||||
|
fuse_count++;
|
||||||
|
|
||||||
|
// Count bits in ODM7
|
||||||
|
for (u32 i = 0; i < 32; i++)
|
||||||
|
if ((fuse_odm7 >> i) & 1)
|
||||||
|
fuse_count++;
|
||||||
|
|
||||||
|
return fuse_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate warmboot cache path based on fuse count
|
||||||
|
void get_warmboot_path(char *path, size_t path_size, u8 fuse_count)
|
||||||
|
{
|
||||||
|
(void)path_size;
|
||||||
|
|
||||||
|
// Format matches Atmosphère's convention:
|
||||||
|
// Mariko: sd:/warmboot_mariko/wb_xx.bin (lowercase hex)
|
||||||
|
// Erista: not used by Atmosphère, but kept for completeness.
|
||||||
|
if (is_mariko())
|
||||||
|
s_printf(path, "sd:/warmboot_mariko/wb_%02x.bin", fuse_count);
|
||||||
|
else
|
||||||
|
s_printf(path, "sd:/warmboot_erista/wb_%02x.bin", fuse_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get target firmware version from Package1 header (display only)
|
||||||
|
static u32 get_target_firmware_from_pkg1(const u8 *package1)
|
||||||
|
{
|
||||||
|
switch (package1[0x1F])
|
||||||
|
{
|
||||||
|
case 0x01: return 0x100; // 1.0.0
|
||||||
|
case 0x02: return 0x200; // 2.0.0
|
||||||
|
case 0x04: return 0x300; // 3.0.0
|
||||||
|
case 0x07: return 0x400; // 4.0.0
|
||||||
|
case 0x0B: return 0x500; // 5.0.0
|
||||||
|
case 0x0E:
|
||||||
|
if (memcmp(package1 + 0x10, "20180802", 8) == 0) return 0x600; // 6.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20181107", 8) == 0) return 0x620; // 6.2.0
|
||||||
|
break;
|
||||||
|
case 0x0F: return 0x700; // 7.0.0
|
||||||
|
case 0x10:
|
||||||
|
if (memcmp(package1 + 0x10, "20190314", 8) == 0) return 0x800; // 8.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20190531", 8) == 0) return 0x810; // 8.1.0
|
||||||
|
if (memcmp(package1 + 0x10, "20190809", 8) == 0) return 0x900; // 9.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20191021", 8) == 0) return 0x910; // 9.1.0
|
||||||
|
if (memcmp(package1 + 0x10, "20200303", 8) == 0) return 0xA00; // 10.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20201030", 8) == 0) return 0xB00; // 11.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20210129", 8) == 0) return 0xC00; // 12.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20210422", 8) == 0) return 0xC02; // 12.0.2
|
||||||
|
if (memcmp(package1 + 0x10, "20210607", 8) == 0) return 0xC10; // 12.1.0
|
||||||
|
if (memcmp(package1 + 0x10, "20210805", 8) == 0) return 0xD00; // 13.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20220105", 8) == 0) return 0xD21; // 13.2.1
|
||||||
|
if (memcmp(package1 + 0x10, "20220209", 8) == 0) return 0xE00; // 14.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20220801", 8) == 0) return 0xF00; // 15.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20230111", 8) == 0) return 0x1000; // 16.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20230906", 8) == 0) return 0x1100; // 17.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20240207", 8) == 0) return 0x1200; // 18.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20240808", 8) == 0) return 0x1300; // 19.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20250206", 8) == 0) return 0x1400; // 20.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20251009", 8) == 0) return 0x1500; // 21.0.0
|
||||||
|
if (memcmp(package1 + 0x10, "20260123", 8) == 0) return 0x1600; // 22.0.0
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error code to string (display/debug)
|
||||||
|
const char *wb_error_to_string(wb_extract_error_t err)
|
||||||
|
{
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case WB_SUCCESS: return "Success";
|
||||||
|
case WB_ERR_NULL_INFO: return "NULL wb_info pointer";
|
||||||
|
case WB_ERR_ERISTA_NOT_SUPPORTED: return "Erista not supported (uses embedded warmboot)";
|
||||||
|
case WB_ERR_MALLOC_PKG1: return "Failed to allocate Package1 buffer (256KB)";
|
||||||
|
case WB_ERR_MMC_INIT: return "Failed to initialize eMMC";
|
||||||
|
case WB_ERR_MMC_PARTITION: return "Failed to set BOOT0 partition";
|
||||||
|
case WB_ERR_MMC_READ: return "Failed to read Package1 from BOOT0";
|
||||||
|
case WB_ERR_DECRYPT_VERIFY: return "Package1 decryption failed (BEK missing or wrong)";
|
||||||
|
case WB_ERR_PK11_MAGIC: return "PK11 magic not found (invalid Package1)";
|
||||||
|
case WB_ERR_WB_SIZE_INVALID: return "Warmboot size invalid (not 0x800-0x1000)";
|
||||||
|
case WB_ERR_MALLOC_WB: return "Failed to allocate warmboot buffer";
|
||||||
|
default: return "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended extraction with detailed error codes
|
||||||
|
wb_extract_error_t extract_warmboot_from_pkg1_ex(warmboot_info_t *wb_info)
|
||||||
|
{
|
||||||
|
if (!wb_info)
|
||||||
|
return WB_ERR_NULL_INFO;
|
||||||
|
|
||||||
|
// Initialize result
|
||||||
|
memset(wb_info, 0, sizeof(warmboot_info_t));
|
||||||
|
wb_info->is_erista = !is_mariko();
|
||||||
|
wb_info->fuse_count = get_burnt_fuses();
|
||||||
|
|
||||||
|
// Erista doesn't need warmboot extraction from Package1
|
||||||
|
if (wb_info->is_erista)
|
||||||
|
return WB_ERR_ERISTA_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
// From here on, we're dealing with Mariko only
|
||||||
|
|
||||||
|
// Allocate buffer for Package1
|
||||||
|
u8 *pkg1_buffer = (u8 *)malloc(PKG1_SIZE);
|
||||||
|
if (!pkg1_buffer)
|
||||||
|
return WB_ERR_MALLOC_PKG1;
|
||||||
|
u8 *pkg1_buffer_orig = pkg1_buffer;
|
||||||
|
|
||||||
|
// Connect eMMC (BOOT0 access)
|
||||||
|
if (connectMMC(MMC_CONN_EMMC))
|
||||||
|
{
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_MMC_INIT;
|
||||||
|
}
|
||||||
|
msleep(1);
|
||||||
|
|
||||||
|
// Set to BOOT0 and read package1
|
||||||
|
if (!emummc_storage_set_mmc_partition(&emmc_storage, EMMC_BOOT0))
|
||||||
|
{
|
||||||
|
disconnectMMC();
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_MMC_PARTITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emummc_storage_read(&emmc_storage, PKG1_OFFSET / EMMC_BLOCKSIZE, PKG1_SIZE / EMMC_BLOCKSIZE, pkg1_buffer))
|
||||||
|
{
|
||||||
|
disconnectMMC();
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_MMC_READ;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectMMC();
|
||||||
|
|
||||||
|
// On Mariko, Package1 is encrypted and needs decryption.
|
||||||
|
// Skip 0x170 byte header to get to the encrypted payload.
|
||||||
|
u8 *pkg1_mariko = pkg1_buffer + 0x170;
|
||||||
|
|
||||||
|
// Set IV from package1 + 0x10 (16 bytes)
|
||||||
|
se_aes_iv_set(KS_MARIKO_BEK, pkg1_mariko + 0x10);
|
||||||
|
|
||||||
|
// Decrypt using BEK at keyslot 13
|
||||||
|
se_aes_crypt_cbc(KS_MARIKO_BEK, 0, /* DECRYPT */
|
||||||
|
pkg1_mariko + 0x20,
|
||||||
|
0x40000 - (0x20 + 0x170),
|
||||||
|
pkg1_mariko + 0x20,
|
||||||
|
0x40000 - (0x20 + 0x170));
|
||||||
|
|
||||||
|
// Verify decryption (first 0x20 bytes should match decrypted header)
|
||||||
|
if (memcmp(pkg1_mariko, pkg1_mariko + 0x20, 0x20) != 0)
|
||||||
|
{
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_DECRYPT_VERIFY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use pkg1_mariko as the working pointer
|
||||||
|
pkg1_buffer = pkg1_mariko;
|
||||||
|
|
||||||
|
// Detect target firmware (display only)
|
||||||
|
u32 target_fw = get_target_firmware_from_pkg1(pkg1_buffer);
|
||||||
|
|
||||||
|
// Store debug info: Package1 version byte and date string
|
||||||
|
wb_info->pkg1_version = pkg1_buffer[0x1F];
|
||||||
|
memcpy(wb_info->pkg1_date, pkg1_buffer + 0x10, 8);
|
||||||
|
wb_info->pkg1_date[8] = '\0';
|
||||||
|
|
||||||
|
// Get burnt fuse count from device
|
||||||
|
u8 burnt_fuses = get_burnt_fuses();
|
||||||
|
|
||||||
|
// For warmboot naming, ALWAYS use burnt_fuses directly.
|
||||||
|
wb_info->fuse_count = burnt_fuses;
|
||||||
|
wb_info->burnt_fuses = burnt_fuses;
|
||||||
|
wb_info->target_firmware = target_fw;
|
||||||
|
|
||||||
|
// Determine PK11 offset using firmware hint, then fall back.
|
||||||
|
u32 pk11_offset = 0x4000;
|
||||||
|
if (target_fw != 0 && target_fw >= 0x620)
|
||||||
|
pk11_offset = 0x7000;
|
||||||
|
|
||||||
|
u8 *pk11_ptr_u8 = pkg1_buffer + pk11_offset;
|
||||||
|
bool pk11_ok = memcmp(pk11_ptr_u8, "PK11", 4) == 0;
|
||||||
|
|
||||||
|
if (!pk11_ok)
|
||||||
|
{
|
||||||
|
pk11_offset = (pk11_offset == 0x4000) ? 0x7000 : 0x4000;
|
||||||
|
pk11_ptr_u8 = pkg1_buffer + pk11_offset;
|
||||||
|
pk11_ok = memcmp(pk11_ptr_u8, "PK11", 4) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pk11_ok)
|
||||||
|
{
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_PK11_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
wb_info->pk11_offset = pk11_offset;
|
||||||
|
|
||||||
|
u32 *pk11_ptr = (u32 *)pk11_ptr_u8;
|
||||||
|
|
||||||
|
// Store debug header
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
wb_info->pk11_header[i] = pk11_ptr[i];
|
||||||
|
|
||||||
|
// Navigate through PK11 container to find warmboot (Atmosphere logic)
|
||||||
|
u32 *pk11_data = pk11_ptr + (0x20 / sizeof(u32));
|
||||||
|
|
||||||
|
wb_info->sig_found[0] = 0;
|
||||||
|
wb_info->sig_found[1] = 0;
|
||||||
|
wb_info->sig_found[2] = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
u32 signature = *pk11_data;
|
||||||
|
wb_info->sig_found[i] = signature;
|
||||||
|
|
||||||
|
switch (signature)
|
||||||
|
{
|
||||||
|
case SIG_NX_BOOTLOADER:
|
||||||
|
pk11_data += pk11_ptr[6] / sizeof(u32);
|
||||||
|
break;
|
||||||
|
case SIG_SECURE_MONITOR_1:
|
||||||
|
case SIG_SECURE_MONITOR_2:
|
||||||
|
pk11_data += pk11_ptr[4] / sizeof(u32);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
i = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wb_info->debug_ptr_offset = (u32)((u8 *)pk11_data - pk11_ptr_u8);
|
||||||
|
|
||||||
|
u32 wb_size = *pk11_data;
|
||||||
|
|
||||||
|
// Debug layout type
|
||||||
|
u32 warmboot_size_header = pk11_ptr[1];
|
||||||
|
if (warmboot_size_header == wb_size &&
|
||||||
|
warmboot_size_header >= WARMBOOT_MIN_SIZE &&
|
||||||
|
warmboot_size_header < WARMBOOT_MAX_SIZE)
|
||||||
|
wb_info->debug_layout_type = 1;
|
||||||
|
else
|
||||||
|
wb_info->debug_layout_type = 2;
|
||||||
|
|
||||||
|
if (wb_size < WARMBOOT_MIN_SIZE || wb_size >= WARMBOOT_MAX_SIZE)
|
||||||
|
{
|
||||||
|
wb_info->size = wb_size;
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_WB_SIZE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
wb_info->size = wb_size;
|
||||||
|
|
||||||
|
memcpy(wb_info->debug_warmboot_preview, (u8 *)pk11_data, 16);
|
||||||
|
|
||||||
|
// Allocate warmboot buffer ([size_u32][warmboot_binary...])
|
||||||
|
u8 *wb_data = (u8 *)malloc(wb_size);
|
||||||
|
if (!wb_data)
|
||||||
|
{
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_ERR_MALLOC_WB;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(wb_data, (u8 *)pk11_data, wb_size);
|
||||||
|
|
||||||
|
wb_info->data = wb_data;
|
||||||
|
wb_info->size = wb_size;
|
||||||
|
|
||||||
|
free(pkg1_buffer_orig);
|
||||||
|
return WB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original wrapper for backward compatibility
|
||||||
|
bool extract_warmboot_from_pkg1(warmboot_info_t *wb_info)
|
||||||
|
{
|
||||||
|
return extract_warmboot_from_pkg1_ex(wb_info) == WB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save warmboot to SD card
|
||||||
|
bool save_warmboot_to_sd(const warmboot_info_t *wb_info, const char *path)
|
||||||
|
{
|
||||||
|
if (!wb_info || !wb_info->data || !path)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FIL fp;
|
||||||
|
UINT bytes_written = 0;
|
||||||
|
|
||||||
|
const char *dir_path = wb_info->is_erista ? "sd:/warmboot_erista" : "sd:/warmboot_mariko";
|
||||||
|
f_mkdir(dir_path);
|
||||||
|
|
||||||
|
if (f_open(&fp, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (f_write(&fp, wb_info->data, wb_info->size, &bytes_written) != FR_OK)
|
||||||
|
{
|
||||||
|
f_close(&fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
f_close(&fp);
|
||||||
|
|
||||||
|
return (bytes_written == wb_info->size);
|
||||||
|
}
|
||||||
|
|
||||||
86
source/warmboot/warmboot_extractor.h
Normal file
86
source/warmboot/warmboot_extractor.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Warmboot Extractor
|
||||||
|
* Based on Atmosphere fusee_setup_horizon.cpp
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2025 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License, version 2, as published
|
||||||
|
* by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WARMBOOT_EXTRACTOR_H_
|
||||||
|
#define _WARMBOOT_EXTRACTOR_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <utils/types.h>
|
||||||
|
|
||||||
|
// Warmboot binary size constraints
|
||||||
|
#define WARMBOOT_MIN_SIZE 0x800 // 2048 bytes
|
||||||
|
#define WARMBOOT_MAX_SIZE 0x1000 // 4096 bytes
|
||||||
|
|
||||||
|
// Package1 locations
|
||||||
|
#define PKG1_OFFSET 0x100000 // 1MB into BOOT0
|
||||||
|
#define PKG1_SIZE 0x40000 // 256KB
|
||||||
|
|
||||||
|
// PK11 magic
|
||||||
|
#define PK11_MAGIC 0x31314B50 // "PK11" in little endian
|
||||||
|
|
||||||
|
// Known payload signatures to skip
|
||||||
|
#define SIG_NX_BOOTLOADER 0xD5034FDF
|
||||||
|
#define SIG_SECURE_MONITOR_1 0xE328F0C0
|
||||||
|
#define SIG_SECURE_MONITOR_2 0xF0C0A7F0
|
||||||
|
|
||||||
|
// Warmboot metadata structure
|
||||||
|
typedef struct {
|
||||||
|
u32 magic; // "WBT0" (0x30544257)
|
||||||
|
u32 target_firmware; // Target firmware version
|
||||||
|
u32 reserved[2]; // Reserved fields
|
||||||
|
} warmboot_metadata_t;
|
||||||
|
|
||||||
|
// Warmboot extraction result
|
||||||
|
typedef struct {
|
||||||
|
u8 *data; // Warmboot binary data
|
||||||
|
u32 size; // Size of warmboot binary
|
||||||
|
u8 fuse_count; // Burnt fuse count (used for naming: wb_XX.bin)
|
||||||
|
u8 burnt_fuses; // Actual burnt fuses on device (same as fuse_count)
|
||||||
|
u32 target_firmware; // Detected target firmware (0 if unknown, for display only)
|
||||||
|
bool is_erista; // True if Erista, false if Mariko
|
||||||
|
|
||||||
|
// Debug fields for troubleshooting
|
||||||
|
u32 pk11_offset;
|
||||||
|
u32 pk11_header[8];
|
||||||
|
u32 sig_found[3];
|
||||||
|
u32 debug_ptr_offset;
|
||||||
|
u32 debug_layout_type;
|
||||||
|
u8 debug_warmboot_preview[16];
|
||||||
|
u8 pkg1_version; // Package1 version byte at offset 0x1F
|
||||||
|
u8 pkg1_date[12]; // Package1 date string (8 chars + null)
|
||||||
|
} warmboot_info_t;
|
||||||
|
|
||||||
|
// Extraction error codes
|
||||||
|
typedef enum {
|
||||||
|
WB_SUCCESS = 0,
|
||||||
|
WB_ERR_NULL_INFO,
|
||||||
|
WB_ERR_ERISTA_NOT_SUPPORTED,
|
||||||
|
WB_ERR_MALLOC_PKG1,
|
||||||
|
WB_ERR_MMC_INIT,
|
||||||
|
WB_ERR_MMC_PARTITION,
|
||||||
|
WB_ERR_MMC_READ,
|
||||||
|
WB_ERR_DECRYPT_VERIFY,
|
||||||
|
WB_ERR_PK11_MAGIC,
|
||||||
|
WB_ERR_WB_SIZE_INVALID,
|
||||||
|
WB_ERR_MALLOC_WB,
|
||||||
|
} wb_extract_error_t;
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
wb_extract_error_t extract_warmboot_from_pkg1_ex(warmboot_info_t *wb_info);
|
||||||
|
bool extract_warmboot_from_pkg1(warmboot_info_t *wb_info);
|
||||||
|
bool save_warmboot_to_sd(const warmboot_info_t *wb_info, const char *path);
|
||||||
|
u8 get_burnt_fuses(void);
|
||||||
|
bool is_mariko(void);
|
||||||
|
void get_warmboot_path(char *path, size_t path_size, u8 fuse_count);
|
||||||
|
const char *wb_error_to_string(wb_extract_error_t err);
|
||||||
|
|
||||||
|
#endif /* _WARMBOOT_EXTRACTOR_H_ */
|
||||||
|
|
||||||
Reference in New Issue
Block a user