Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5ddc38477 | ||
|
|
8d102256a2 | ||
|
|
2e561f7ea9 | ||
|
|
9656fe1b6f | ||
|
|
48c23f2a43 | ||
|
|
6d82649c3c | ||
|
|
81dea35754 | ||
|
|
fdbe43421b | ||
|
|
0d71560785 | ||
|
|
108e8a0cd4 | ||
|
|
653e81c48e | ||
|
|
0dc0783094 | ||
|
|
13ef1bf6be | ||
|
|
7ea80353f6 | ||
|
|
07bbff7d11 | ||
|
|
2ff4fc3cdd | ||
|
|
c9456055ea | ||
|
|
2900f2d67b | ||
|
|
93c8c90804 | ||
|
|
36e54642d2 | ||
|
|
e04cb28711 | ||
|
|
ad1dc51e06 | ||
|
|
5b6bab7a58 | ||
|
|
9bd2fc659d | ||
|
|
f18633f65f | ||
|
|
1026bc7b4f | ||
|
|
f1dee68142 | ||
|
|
f7cc2d295c | ||
|
|
a6f254b5cf | ||
|
|
00dbbe1b28 | ||
|
|
7f33309903 | ||
|
|
c95808fa2d | ||
|
|
5b6bd048a9 | ||
|
|
28e6ad3372 | ||
|
|
9b128ebba5 | ||
|
|
71f49180c3 | ||
|
|
4a042241a8 | ||
|
|
0eab9127e3 | ||
|
|
9f11991410 | ||
|
|
fec0b95098 | ||
|
|
5081439f53 | ||
|
|
c44aebee4d | ||
|
|
706cd50f25 | ||
|
|
b1b81c87f2 | ||
|
|
bf7e30539e | ||
|
|
5f8a61201b | ||
|
|
ed8aee8b8c | ||
|
|
92ec2af587 | ||
|
|
7960c87579 | ||
|
|
700d572732 | ||
|
|
014a0d86f1 | ||
|
|
88db59405f | ||
|
|
ef2e008700 | ||
|
|
07101c053a | ||
|
|
6c5f6ac475 | ||
|
|
61ecd9a617 | ||
|
|
6f56a9bfe9 | ||
|
|
416875ee46 | ||
|
|
c875b506ea | ||
|
|
ff4517e583 | ||
|
|
92cc989dc9 | ||
|
|
6e5987e3ca | ||
|
|
f03e232b90 | ||
|
|
1eb18c1790 | ||
|
|
3076d56973 | ||
|
|
b35707edf9 | ||
|
|
dfbd0dc9e7 | ||
|
|
620ad7ba71 | ||
|
|
5e4fd53243 | ||
|
|
0ba7630354 | ||
|
|
028d0ec0d5 | ||
|
|
751fa05fcd | ||
|
|
f89845257e | ||
|
|
2f274e2f47 | ||
|
|
38088e80e1 | ||
|
|
14162828ea | ||
|
|
ab8507e09d | ||
|
|
0d25c07333 | ||
|
|
bfc8ba8447 | ||
|
|
a45f8293d9 | ||
|
|
f29b9d14d7 | ||
|
|
9c9fd2deef | ||
|
|
3bb01ffd68 | ||
|
|
bc6d9994dc | ||
|
|
e177f9e0fe | ||
|
|
c5d75d2de9 | ||
|
|
cc0cade4d2 | ||
|
|
1e3362250f | ||
|
|
db16e8d602 | ||
|
|
2a563eddd0 | ||
|
|
6b8474b7b9 | ||
|
|
b5336c81cc | ||
|
|
1fcab825bf | ||
|
|
0306556032 | ||
|
|
b093578046 | ||
|
|
9332b9eb33 | ||
|
|
141c7817a0 | ||
|
|
f155026d8f | ||
|
|
37e467ba60 | ||
|
|
3572b835b5 | ||
|
|
da4f3a72af | ||
|
|
2938bbd11f | ||
|
|
abf3017eb2 | ||
|
|
5c855ea52f | ||
|
|
c83edea7ad | ||
|
|
6d3113c8c3 | ||
|
|
48c48c7bbc | ||
|
|
e4093ed988 | ||
|
|
c79af52720 | ||
|
|
61eeaca6d5 | ||
|
|
320a79ba72 | ||
|
|
2e1b943805 | ||
|
|
bc167dde2d | ||
|
|
7d9a8b4211 |
19
.github/ISSUE_TEMPLATE.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
#
|
||||
# THIS IS NOT A SUPPORT FORUM! For support please go to:
|
||||
# Luma3DS GBATemp thread: https://gbatemp.net/threads/luma3ds-noob-proof-3ds-custom-firmware.411110/
|
||||
# /r/3dshacks: http://reddit.com/r/3dshacks/
|
||||
# Nintendo Hacking: https://discordapp.com/invite/C29hYvh
|
||||
# IRC: #3dshacks@rizon
|
||||
#
|
||||
# Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
|
||||
#
|
||||
# For those with GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: https://3ds.guide/troubleshooting
|
||||
#
|
||||
# Please make sure to read "Enable region/language emulation and external .code" https://github.com/AuroraWright/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable region/language emulation and external .code" option(s).
|
||||
# Keep in mind that Wiki page only applies to nightly builds. It will NOT WORK with 6.6 Stable Luma3DS.
|
||||
# As of 0.02 (https://github.com/Possum/LumaLocaleSwitcher/releases) LumaLocaleSwitcher's path(s) are for 6.6 Stable and won't work with Luma3DS nightly builds newer or equal to https://github.com/AuroraWright/Luma3DS/commit/b5336c81cc82b6c5e8115249342beb5b065cdce9.
|
||||
# Use this version for Luma3DS nightlies newer or equal to https://github.com/AuroraWright/Luma3DS/commit/b5336c81cc82b6c5e8115249342beb5b065cdce9 :
|
||||
# https://puu.sh/uC5zW/5470adc347.7z (from https://github.com/Possum/LumaLocaleSwitcher/issues/9#issuecomment-285564014)
|
||||
#
|
||||
-->
|
||||
27
Makefile
27
Makefile
@@ -4,12 +4,7 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := Luma3DS
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||
@@ -27,7 +22,7 @@ dir_build := build
|
||||
dir_out := out
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostartfiles
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
@@ -39,8 +34,6 @@ bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/svcG
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $(dir_build)/bundled.h
|
||||
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $(dir_build)/bundled.h
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
@@ -67,6 +60,11 @@ clean:
|
||||
|
||||
.PRECIOUS: $(dir_build)/%.bin
|
||||
|
||||
.PHONY: $(dir_loader)
|
||||
.PHONY: $(dir_arm9_exceptions)
|
||||
.PHONY: $(dir_arm11_exceptions)
|
||||
.PHONY: $(dir_injector)
|
||||
|
||||
$(dir_out) $(dir_build):
|
||||
@mkdir -p "$@"
|
||||
|
||||
@@ -77,7 +75,7 @@ $(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||
@cp -a $(dir_build)/main.bin $@
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/main.elf: $(bundled) $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
@@ -104,11 +102,16 @@ $(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\""
|
||||
$(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)"
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
$(dir_build)/bundled.h: $(bundled)
|
||||
@$(foreach f, $(bundled),\
|
||||
echo "extern const u8" `(echo $(basename $(notdir $(f))) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $@;\
|
||||
echo "extern const u32" `(echo $(basename $(notdir $(f)))| sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $@;\
|
||||
)
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bundled.h
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
**Luma3DS** is a program to patch the system software of (New) Nintendo 3DS handheld consoles "on the fly", adding features (such as per-game language settings and debugging capabilities for developers) and removing restrictions enforced by Nintendo (such as the region lock).
|
||||
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://github.com/Plailect/Guide/wiki) for details on how to get your system ready.
|
||||
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://3ds.guide/) for details on how to get your system ready.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,12 +4,7 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := arm11_exceptions
|
||||
|
||||
@@ -18,7 +13,7 @@ dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfpu=vfp
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
@@ -33,7 +28,7 @@ clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
@@ -45,4 +40,3 @@ $(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
||||
//Dump registers
|
||||
//Current order of saved regs: dfsr, ifsr, far, fpexc, fpinst, fpinst2, cpsr, pc, r8-r12, sp, lr, r0-r7
|
||||
u32 cpsr = regs[6];
|
||||
u32 pc = regs[7] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
u32 pc = regs[7] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 0);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
|
||||
@@ -4,12 +4,7 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := arm9_exceptions
|
||||
|
||||
@@ -18,7 +13,7 @@ dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
@@ -33,7 +28,7 @@ clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
@@ -45,4 +40,3 @@ $(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x01FF7FE0;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
Submodule haxloader/CakeBrah updated: 9f7cea77d4...1efda4e894
Submodule haxloader/CakeHax updated: 5245c7b9dc...329212a8e0
@@ -4,12 +4,7 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
dir_source := source
|
||||
dir_diffs := diffs
|
||||
@@ -20,7 +15,7 @@ dir_build := build
|
||||
dir_out := ../out
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostartfiles
|
||||
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Aurora Wright/TuxSH" --no-print-directory
|
||||
ROPFLAGS := DATNAME=$(name).dat DISPNAME=$(name) GRAPHICS=../graphics/
|
||||
@@ -63,7 +58,7 @@ $(dir_out)/mset/$(name).nds:
|
||||
@mv $(dir_cakesrop)/CakesROP.nds $@
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/main.elf: $(objects) ../$(dir_build)/loader.bin.o
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
@@ -77,4 +72,3 @@ $(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
@@ -50,7 +50,7 @@ diff -uNr a/source/brahma.c b/source/brahma.c
|
||||
+ fseek(p , 0, SEEK_END);
|
||||
+ psize = ftell(p);
|
||||
+ if (psize > 5 && psize < 58) {
|
||||
+ char path[psize + 1];
|
||||
+ char path[57];
|
||||
+
|
||||
+ fseek(p, 0, SEEK_SET);
|
||||
+ u32 bytes_read = fread(path, 1, psize, p);
|
||||
@@ -58,7 +58,7 @@ diff -uNr a/source/brahma.c b/source/brahma.c
|
||||
+ if (bytes_read == psize) {
|
||||
+ if (path[psize - 1] == 0xA) psize--;
|
||||
+ if (path[psize - 1] == 0xD) psize--;
|
||||
+ if (psize > 5 && psize < 56 && path[0] == '/' && memcmp(&path[psize - 4], ".bin", 4)) {
|
||||
+ if (psize > 5 && psize < 56 && path[0] == '/' && memcmp(path + psize - 4, ".bin", 4)) {
|
||||
+ path[psize] = 0;
|
||||
+ f = fopen(path, "rb");
|
||||
+ if (f) use_default = false;
|
||||
@@ -103,7 +103,7 @@ diff -uNr a/source/brahma.c b/source/brahma.c
|
||||
diff -uNr a/source/main.c b/source/main.c
|
||||
--- a/source/main.c 2016-09-26 16:05:36.363067000 +0200
|
||||
+++ b/source/main.c 2016-09-26 21:40:35.202513018 +0200
|
||||
@@ -10,7 +10,7 @@
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
int main (void) {
|
||||
if (brahma_init()) {
|
||||
@@ -112,7 +112,7 @@ diff -uNr a/source/main.c b/source/main.c
|
||||
goto error;
|
||||
firm_reboot();
|
||||
brahma_exit();
|
||||
@@ -22,7 +22,7 @@
|
||||
@@ -50,7 +50,7 @@
|
||||
error:
|
||||
gfxInitDefault();
|
||||
consoleInit(GFX_BOTTOM, NULL);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff -uNr a/source/main.c b/source/main.c
|
||||
--- a/source/main.c 2016-09-11 01:04:25.665231884 +0200
|
||||
+++ b/source/main.c 2016-09-14 12:36:28.601439550 +0200
|
||||
@@ -9,6 +9,7 @@
|
||||
#endif
|
||||
@@ -37,6 +37,7 @@
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
+ svcSleepThread(2500 * 1000000ULL);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x23F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
13
haxloader/source/fatfs/00history.txt
Executable file → Normal file
13
haxloader/source/fatfs/00history.txt
Executable file → Normal file
@@ -212,7 +212,7 @@ R0.10a (January 15, 2014)
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ R0.12a (July 10, 2016)
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Improved f_rename() to be able to rename objects with the same name but case.
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
@@ -277,3 +277,12 @@ R0.12b (September 04, 2016)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
||||
16
haxloader/source/fatfs/00readme.txt
Executable file → Normal file
16
haxloader/source/fatfs/00readme.txt
Executable file → Normal file
@@ -1,21 +1,21 @@
|
||||
FatFs Module Source Files R0.12a
|
||||
FatFs Module Source Files R0.12c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
option Optional external modules.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
||||
|
||||
657
haxloader/source/fatfs/ff.c
Executable file → Normal file
657
haxloader/source/fatfs/ff.c
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
25
haxloader/source/fatfs/ff.h
Executable file → Normal file
25
haxloader/source/fatfs/ff.h
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12b /
|
||||
/ FatFs - Generic FAT file system module R0.12c /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
/ Copyright (C) 2017, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 68020 /* Revision ID */
|
||||
#define _FATFS 68300 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -42,13 +42,6 @@ typedef struct {
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -140,14 +133,15 @@ typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if _FS_EXFAT
|
||||
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_cont; /* Size of first fragment, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0 and non-directory object) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
@@ -163,7 +157,7 @@ typedef struct {
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||
@@ -185,7 +179,7 @@ typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if _USE_LFN != 0
|
||||
@@ -285,6 +279,7 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
|
||||
21
haxloader/source/fatfs/ffconf.h
Executable file → Normal file
21
haxloader/source/fatfs/ffconf.h
Executable file → Normal file
@@ -2,7 +2,7 @@
|
||||
/ FatFs - FAT file system module configuration file
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 68020 /* Revision ID */
|
||||
#define _FFCONF 68300 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -73,7 +73,7 @@
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 1 - ASCII (No support of extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
@@ -148,7 +148,7 @@
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
@@ -172,11 +172,11 @@
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
/ to variable sector size and GET_SECTOR_SIZE command needs to be implemented to
|
||||
/ the disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
@@ -204,7 +204,7 @@
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes.
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the file system object (FATFS) is used for the file data transfer. */
|
||||
|
||||
@@ -212,13 +212,13 @@
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
|
||||
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards C89 compatibility. */
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 1
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2016
|
||||
#define _NORTC_YEAR 2017
|
||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
@@ -258,10 +258,11 @@
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _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. */
|
||||
|
||||
/* #include <windows.h> // O/S definitions */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
2
haxloader/source/fatfs/integer.h
Executable file → Normal file
2
haxloader/source/fatfs/integer.h
Executable file → Normal file
@@ -30,7 +30,7 @@ typedef unsigned short WCHAR;
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
|
||||
/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#endif
|
||||
|
||||
0
haxloader/source/fatfs/option/ccsbcs.c
Executable file → Normal file
0
haxloader/source/fatfs/option/ccsbcs.c
Executable file → Normal file
@@ -43,13 +43,13 @@ void main(void)
|
||||
|
||||
if(pathSize > 5 && pathSize < 58)
|
||||
{
|
||||
char path[pathSize + 1];
|
||||
char path[57];
|
||||
unsigned int read;
|
||||
f_read(&pathFile, path, pathSize, &read);
|
||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||
|
||||
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
||||
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(path + pathSize - 4, ".bin", 4) == 0)
|
||||
{
|
||||
path[pathSize] = 0;
|
||||
foundPayload = f_open(&payload, path, FA_READ) == FR_OK;
|
||||
@@ -63,7 +63,7 @@ void main(void)
|
||||
|
||||
if(foundPayload)
|
||||
{
|
||||
u32 *loaderAddress = (u32 *)0x24FFFF00;
|
||||
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
||||
void *payloadAddress = (void *)0x24F00000;
|
||||
u32 payloadSize = f_size(&payload);
|
||||
|
||||
|
||||
@@ -6,14 +6,10 @@ endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_patches := patches
|
||||
dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
@@ -23,14 +19,19 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
|
||||
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS)
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -specs=3dsx.specs $(ASFLAGS) -Wl,--section-start,.text=0x14000000
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c))
|
||||
|
||||
bundled = $(dir_build)/romfsredir.bin.o
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
@@ -38,19 +39,35 @@ all: $(dir_out)/$(name).bin
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
.PRECIOUS: $(dir_build)/%.bin
|
||||
|
||||
$(dir_build):
|
||||
@mkdir -p "$@"
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
@makerom -f ncch -rsf loader.rsf -nocodepadding -o $@ -elf $<
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(dir_build)/$(name).elf: $(bundled) $(objects)
|
||||
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
|
||||
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
$(dir_build)/%.bin: $(dir_patches)/%.s $(dir_build)
|
||||
@armips $<
|
||||
|
||||
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/bundled.h: $(bundled)
|
||||
@$(foreach f, $(bundled),\
|
||||
echo "extern const u8" `(echo $(basename $(notdir $(f))) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $@;\
|
||||
echo "extern const u32" `(echo $(basename $(notdir $(f)))| sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $@;\
|
||||
)
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bundled.h
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
99
injector/patches/romfsredir.s
Normal file
99
injector/patches/romfsredir.s
Normal file
@@ -0,0 +1,99 @@
|
||||
.arm.little
|
||||
.create "build/romfsredir.bin", 0
|
||||
|
||||
.macro addr, reg, func
|
||||
add reg, pc, #func-.-8
|
||||
.endmacro
|
||||
.macro load, reg, func
|
||||
ldr reg, [pc, #func-.-8]
|
||||
.endmacro
|
||||
|
||||
; Patch by delebile
|
||||
|
||||
.arm
|
||||
_start:
|
||||
|
||||
; Jumps here before the fsOpenFileDirectly call
|
||||
_mountArchive:
|
||||
b mountArchive
|
||||
.word 0xdead0000 ; Substituted opcode
|
||||
.word 0xdead0001 ; Branch to hooked function
|
||||
|
||||
; Jumps here before every iFileOpen call
|
||||
_fsRedir:
|
||||
b fsRedir
|
||||
.word 0xdead0002 ; Substituted opcode
|
||||
.word 0xdead0003 ; Branch to hooked function
|
||||
|
||||
; Mounts the archive and registers it as 'lf:'
|
||||
mountArchive:
|
||||
cmp r3, #3
|
||||
bne _mountArchive + 4
|
||||
stmfd sp!, {r0-r4, lr}
|
||||
sub sp, sp, #4
|
||||
load r1, archiveId
|
||||
mov r0, sp
|
||||
load r4, fsMountArchive
|
||||
blx r4
|
||||
mov r3, #0
|
||||
mov r2, #0
|
||||
ldr r1, [sp]
|
||||
addr r0, archiveName
|
||||
load r4, fsRegisterArchive
|
||||
blx r4
|
||||
add sp, sp, #4
|
||||
ldmfd sp!, {r0-r4, lr}
|
||||
b _mountArchive + 4
|
||||
|
||||
; Check the path passed to iFileOpen.
|
||||
; If it is trying to access a RomFS file, we try to
|
||||
; open it from the LayeredFS folder.
|
||||
; If the file cannot be opened, we just open
|
||||
; it from its original archive like nothing happened
|
||||
fsRedir:
|
||||
stmfd sp!, {r0-r12, lr}
|
||||
ldrb r12, [r1]
|
||||
cmp r12, #0x72 ; 'r', should include "rom:" and "rom2:"
|
||||
cmpne r12, #0x70 ; 'p', should include "patch:"
|
||||
bne endRedir
|
||||
sub sp, sp, #0x400
|
||||
pathRedir:
|
||||
stmfd sp!, {r0-r3}
|
||||
add r0, sp, #0x10
|
||||
addr r3, customPath
|
||||
pathRedir_1:
|
||||
ldrb r2, [r3], #1
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_1
|
||||
sub r0, r0, #2
|
||||
pathRedir_2:
|
||||
ldrh r2, [r1], #2
|
||||
cmp r2, #0x3A ; ':'
|
||||
bne pathRedir_2
|
||||
pathRedir_3:
|
||||
ldrh r2, [r1], #2
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_3
|
||||
ldmfd sp!, {r0-r3}
|
||||
mov r1, sp
|
||||
bl _fsRedir + 4
|
||||
add sp, sp, #0x400
|
||||
cmp r0, #0
|
||||
|
||||
endRedir:
|
||||
ldmfd sp!, {r0-r12, lr}
|
||||
moveq r0, #0
|
||||
bxeq lr
|
||||
b _fsRedir + 4
|
||||
|
||||
.pool
|
||||
.align 4
|
||||
archiveName : .dcb "lf:", 0
|
||||
fsMountArchive : .word 0xdead0005
|
||||
fsRegisterArchive : .word 0xdead0006
|
||||
archiveId : .word 0xdead0007
|
||||
customPath : .word 0xdead0004
|
||||
|
||||
.close
|
||||
@@ -107,3 +107,75 @@ Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archi
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80C,3,2); // 0x80C00C2
|
||||
cmdbuf[1] = id;
|
||||
cmdbuf[2] = path.type;
|
||||
cmdbuf[3] = path.size;
|
||||
cmdbuf[4] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[5] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(archive) *archive = cmdbuf[2] | ((u64) cmdbuf[3] << 32);
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_CloseArchive(FS_Archive archive)
|
||||
{
|
||||
if(!archive) return -2;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80E,2,0); // 0x80E0080
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x80B,4,2); // 0x80B0102
|
||||
cmdbuf[1] = (u32) archive;
|
||||
cmdbuf[2] = (u32) (archive >> 32);
|
||||
cmdbuf[3] = path.type;
|
||||
cmdbuf[4] = path.size;
|
||||
cmdbuf[5] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[6] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSDIRLDR_Close(Handle handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x802,0,0); // 0x8020000
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(handle))) return ret;
|
||||
|
||||
ret = cmdbuf[1];
|
||||
if(R_SUCCEEDED(ret)) ret = svcCloseHandle(handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -7,3 +7,7 @@ void fsldrExit(void);
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version);
|
||||
Result FSLDR_SetPriority(u32 priority);
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_ArchiveID archiveId, FS_Path archivePath, FS_Path filePath, u32 openFlags, u32 attributes);
|
||||
Result FSLDR_OpenArchive(FS_Archive* archive, FS_ArchiveID id, FS_Path path);
|
||||
Result FSLDR_CloseArchive(FS_Archive archive);
|
||||
Result FSLDR_OpenDirectory(Handle* out, FS_Archive archive, FS_Path path);
|
||||
Result FSDIRLDR_Close(Handle handle);
|
||||
@@ -158,7 +158,7 @@ static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int i
|
||||
u16 progver = g_exheader.codesetinfo.flags.remasterversion[0] | (g_exheader.codesetinfo.flags.remasterversion[1] << 8);
|
||||
|
||||
// patch
|
||||
patchCode(progid, progver, (u8 *)shared->text_addr, shared->total_size << 12);
|
||||
patchCode(progid, progver, (u8 *)shared->text_addr, shared->total_size << 12, g_exheader.codesetinfo.text.codesize, g_exheader.codesetinfo.ro.codesize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#include "patcher.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "fsldr.h"
|
||||
#include "ifile.h"
|
||||
#include "CFWInfo.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
static CFWInfo info;
|
||||
|
||||
@@ -38,21 +40,50 @@ static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, in
|
||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
||||
}
|
||||
|
||||
static Result openLumaFile(IFile *file, const char *path)
|
||||
static u32 dirCheck(FS_ArchiveID archiveId, const char *path)
|
||||
{
|
||||
u32 ret;
|
||||
Handle handle;
|
||||
FS_Archive archive;
|
||||
FS_Path dirPath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
|
||||
if(R_FAILED(FSLDR_OpenArchive(&archive, archiveId, archivePath))) ret = 1;
|
||||
else
|
||||
{
|
||||
ret = R_SUCCEEDED(FSLDR_OpenDirectory(&handle, archive, dirPath)) ? 0 : 2;
|
||||
if(ret) FSDIRLDR_Close(handle);
|
||||
FSLDR_CloseArchive(archive);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool openLumaFile(IFile *file, const char *path)
|
||||
{
|
||||
Result res = fileOpen(file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
|
||||
if((u32)res == 0xC88044AB) res = fileOpen(file, ARCHIVE_NAND_RW, path, FS_OPEN_READ); //Returned if SD is not mounted
|
||||
if(R_SUCCEEDED(res)) return true;
|
||||
|
||||
return res;
|
||||
//Returned if SD is not mounted
|
||||
return (u32)res == 0xC88044AB && R_SUCCEEDED(fileOpen(file, ARCHIVE_NAND_RW, path, FS_OPEN_READ));
|
||||
}
|
||||
|
||||
static u32 checkLumaDir(const char *path)
|
||||
{
|
||||
u32 res = dirCheck(ARCHIVE_SDMC, path);
|
||||
|
||||
if(!res) return ARCHIVE_SDMC;
|
||||
|
||||
return res == 1 && !dirCheck(ARCHIVE_NAND_RW, path) ? ARCHIVE_NAND_RW : 0;
|
||||
}
|
||||
|
||||
static inline void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
|
||||
if(!infoLoaded)
|
||||
{
|
||||
if(infoLoaded) return;
|
||||
|
||||
svcGetCFWInfo(&info);
|
||||
|
||||
IFile file;
|
||||
@@ -61,21 +92,19 @@ static inline void loadCFWInfo(void)
|
||||
|
||||
infoLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool secureInfoExists(void)
|
||||
{
|
||||
static bool exists = false;
|
||||
|
||||
if(!exists)
|
||||
{
|
||||
if(exists) return true;
|
||||
|
||||
IFile file;
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
||||
{
|
||||
exists = true;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
@@ -90,17 +119,17 @@ static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 current
|
||||
|
||||
IFile file;
|
||||
|
||||
if(R_SUCCEEDED(openLumaFile(&file, paths[currentNand])))
|
||||
{
|
||||
if(!openLumaFile(&file, paths[currentNand])) return;
|
||||
|
||||
u64 fileSize;
|
||||
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= 62)
|
||||
{
|
||||
u8 buf[fileSize];
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > 62) goto exit;
|
||||
|
||||
u8 buf[62];
|
||||
u64 total;
|
||||
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize)))
|
||||
{
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
|
||||
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
||||
u32 finalSize = 0;
|
||||
|
||||
@@ -131,66 +160,323 @@ static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 current
|
||||
if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0;
|
||||
*verStringSize = finalSize * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
{
|
||||
/* HANS:
|
||||
Look for error code which is known to be stored near cfg:u handle
|
||||
this way we can find the right candidate
|
||||
(handle should also be stored right after end of candidate function) */
|
||||
|
||||
u32 n = 0,
|
||||
possible[24];
|
||||
|
||||
for(u8 *pos = code + 16; n < 24 && pos <= code + size - 16; pos += 4)
|
||||
{
|
||||
if(*(u32 *)pos != 0xD8A103F9) continue;
|
||||
|
||||
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
||||
if(*l <= 0x10000000) possible[n++] = *l;
|
||||
}
|
||||
|
||||
static inline u32 loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin"
|
||||
If it exists it should be a decompressed binary code file */
|
||||
if(!n) return NULL;
|
||||
|
||||
char path[] = "/luma/code_sections/0000000000000000.bin";
|
||||
progIdToStr(path + 35, progId);
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos <= code + size - 12; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
{
|
||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
if(cmp[0] != CFGU_GetConfigInfoBlk2_endPattern[0] || cmp[1] != CFGU_GetConfigInfoBlk2_endPattern[1]) continue;
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == cmp[2])
|
||||
{
|
||||
*CFGUHandleOffset = cmp[2];
|
||||
return CFGU_GetConfigInfoBlk2_endPos;
|
||||
}
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
|
||||
|
||||
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||
*((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D; CFGU_GetConfigInfoBlk2_startPos -= 4)
|
||||
if(CFGU_GetConfigInfoBlk2_startPos < code + 4) return false;
|
||||
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos <= code + size - 4; languageBlkIdPos += 4)
|
||||
{
|
||||
if(*(u32 *)languageBlkIdPos != 0xA0002) continue;
|
||||
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
{
|
||||
if(instr[3] != 0xEB) continue; //We're looking for BL
|
||||
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
|
||||
calledFunction += offset;
|
||||
|
||||
if(calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||
|
||||
//We're done
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && calledFunction[3] == 0xEA);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
{
|
||||
for(u8 *cmdPos = code; cmdPos <= code + size - 28; cmdPos += 4)
|
||||
{
|
||||
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D0F70, 0xE3A00802};
|
||||
|
||||
u32 *cmp = (u32 *)cmdPos;
|
||||
|
||||
if(*cmp != cfgSecureInfoGetRegionCmdPattern[1]) continue;
|
||||
|
||||
for(u32 i = 1; i < 3; i++)
|
||||
if((*(cmp - i) & 0xFFFF0FFF) == cfgSecureInfoGetRegionCmdPattern[0] && *((u16 *)cmdPos + 5) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
||||
{
|
||||
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
|
||||
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
|
||||
cmp[6] = 0xE5840004; //str r0, [r4, #4]
|
||||
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u32 findFunctionStart(u8* code, u32 pos)
|
||||
{
|
||||
while(pos >= 4)
|
||||
{
|
||||
pos -= 4;
|
||||
if(*(u16 *)(code + pos + 2) == 0xE92D) return pos;
|
||||
}
|
||||
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsSymbols(u8* code, u32 size, u32 *fsMountArchive, u32 *fsRegisterArchive, u32 *fsTryOpenFile, u32 *fsOpenFileDirectly)
|
||||
{
|
||||
for(u32 addr = 0; addr <= size - 4; addr += 4)
|
||||
{
|
||||
if(*fsMountArchive == 0xFFFFFFFF)
|
||||
{
|
||||
if(addr <= size - 12 && *(u32 *)(code + addr) == 0xE5970010)
|
||||
{
|
||||
if((*(u32 *)(code + addr + 4) == 0xE1CD20D8) && ((*(u32 *)(code + addr + 8) & 0xFFFFFF) == 0x008D0000))
|
||||
*fsMountArchive = findFunctionStart(code, addr);
|
||||
}
|
||||
else if(addr <= size - 16 && *(u32 *)(code + addr) == 0xE24DD028)
|
||||
{
|
||||
if((*(u32 *)(code + addr + 4) == 0xE1A04000) && (*(u32 *)(code + addr + 8) == 0xE59F60A8) && (*(u32 *)(code + addr + 0xC) == 0xE3A0C001))
|
||||
*fsMountArchive = findFunctionStart(code, addr);
|
||||
}
|
||||
}
|
||||
|
||||
if(addr <= size - 12 && *fsRegisterArchive == 0xFFFFFFFF && *(u32 *)(code + addr) == 0xE3500008 &&
|
||||
(*(u32 *)(code + addr + 4) & 0xFFF00FF0) == 0xE1800400 && (*(u32 *)(code + addr + 8) & 0xFFF00FF0) == 0xE1800FC0)
|
||||
*fsRegisterArchive = findFunctionStart(code, addr);
|
||||
|
||||
if(addr <= size - 0x40 && *fsTryOpenFile == 0xFFFFFFFF && *(u32 *)(code + addr + 4) == 0x1AFFFFFC && *(u32 *)(code + addr) == 0xE351003A &&
|
||||
*(u32 *)(code + addr + 0x34) == 0xE590C000 && *(u32 *)(code + addr + 0x3C) == 0xE12FFF3C)
|
||||
*fsTryOpenFile = findFunctionStart(code, addr);
|
||||
|
||||
if(*fsOpenFileDirectly == 0xFFFFFFFF && *(u32 *)(code + addr) == 0x08030204)
|
||||
*fsOpenFileDirectly = findFunctionStart(code, addr);
|
||||
|
||||
if(*fsMountArchive != 0xFFFFFFFF && *fsRegisterArchive != 0xFFFFFFFF && *fsTryOpenFile != 0xFFFFFFFF && *fsOpenFileDirectly != 0xFFFFFFFF) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool findLayeredFsPayloadOffset(u8* code, u32 size, u32 *payloadOffset)
|
||||
{
|
||||
//First check for sufficient padding at the end of the .text segment
|
||||
if(((size + 4095) & 0xFFFFF000) - size >= romfsredir_bin_size)
|
||||
{
|
||||
*payloadOffset = size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//If there isn't enough padding look for the "throwFatalError" function to replace
|
||||
u32 svcConnectToPort = 0xFFFFFFFF;
|
||||
|
||||
for(u32 addr = 4; svcConnectToPort == 0xFFFFFFFF && addr <= size - 4; addr += 4)
|
||||
{
|
||||
if(*(u32 *)(code + addr) == 0xEF00002D)
|
||||
svcConnectToPort = addr - 4;
|
||||
}
|
||||
|
||||
if(svcConnectToPort != 0xFFFFFFFF)
|
||||
{
|
||||
u32 func = 0xFFFFFFFF;
|
||||
|
||||
for(u32 i = 4; func == 0xFFFFFFFF && i <= size - 4; i += 4)
|
||||
{
|
||||
if(*(u32 *)(code + i) != MAKE_BRANCH_LINK(i, svcConnectToPort)) continue;
|
||||
|
||||
func = findFunctionStart(code, i);
|
||||
|
||||
for(u32 pos = func + 4; func != 0xFFFFFFFF && pos <= size - 4 && *(u16 *)(code + pos + 2) != 0xE92D; pos += 4)
|
||||
if(*(u32 *)(code + pos) == 0xE200167E) func = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if(func != 0xFFFFFFFF)
|
||||
{
|
||||
*payloadOffset = func;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool applyCodeIpsPatch(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.ips"
|
||||
If it exists it should be an IPS format patch */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/code.ips";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
u32 ret = 0;
|
||||
|
||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||
{
|
||||
u64 fileSize;
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = 1;
|
||||
else
|
||||
{
|
||||
bool ret = false;
|
||||
u8 buffer[5];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) ret = 1;
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 5)) || total != 5 || memcmp(buffer, "PATCH", 5) != 0) goto exit;
|
||||
|
||||
while(R_SUCCEEDED(IFile_Read(&file, &total, buffer, 3)) && total == 3)
|
||||
{
|
||||
if(memcmp(buffer, "EOF", 3) == 0)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
u32 offset = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
||||
|
||||
u32 patchSize = (buffer[0] << 8) | buffer[1];
|
||||
|
||||
if(!patchSize)
|
||||
{
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 2)) || total != 2) break;
|
||||
|
||||
u32 rleSize = (buffer[0] << 8) | buffer[1];
|
||||
|
||||
if(offset + rleSize > size) break;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buffer, 1)) || total != 1) break;
|
||||
|
||||
for(u32 i = 0; i < rleSize; i++)
|
||||
code[offset + i] = buffer[0];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset + patchSize > size) break;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, code + offset, patchSize)) || total != patchSize) break;
|
||||
}
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u32 loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/code.bin"
|
||||
If it exists it should be a decrypted and decompressed binary code file */
|
||||
|
||||
char path[] = "/luma/locales/0000000000000000.txt";
|
||||
progIdToStr(path + 29, progId);
|
||||
char path[] = "/luma/titles/0000000000000000/code.bin";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
u32 ret = 0;
|
||||
|
||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||
{
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
bool ret;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) ret = 1;
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
|
||||
else
|
||||
{
|
||||
char buf[fileSize];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) ret = 1;
|
||||
else
|
||||
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/locale.txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/locale.txt";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
|
||||
bool ret = false;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) goto exit;
|
||||
|
||||
char buf[8];
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||
|
||||
u32 i,
|
||||
j;
|
||||
|
||||
@@ -216,142 +502,85 @@ static inline u32 loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId
|
||||
}
|
||||
}
|
||||
|
||||
if(i == 7 || j == 12) ret = 1;
|
||||
}
|
||||
}
|
||||
ret = i != 7 && j != 12;
|
||||
|
||||
exit:
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
static inline bool patchLayeredFs(u64 progId, u8* code, u32 size)
|
||||
{
|
||||
/* HANS:
|
||||
Look for error code which is known to be stored near cfg:u handle
|
||||
this way we can find the right candidate
|
||||
(handle should also be stored right after end of candidate function) */
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/romfs"
|
||||
If it exists it should be a folder containing ROMFS files */
|
||||
|
||||
u32 n = 0,
|
||||
possible[24];
|
||||
char path[] = "/luma/titles/0000000000000000/romfs";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
|
||||
u32 archiveId = checkLumaDir(path);
|
||||
|
||||
if(!archiveId) return true;
|
||||
|
||||
static const char *archiveName = "lf:";
|
||||
|
||||
u32 fsMountArchive = 0xFFFFFFFF,
|
||||
fsRegisterArchive = 0xFFFFFFFF,
|
||||
fsTryOpenFile = 0xFFFFFFFF,
|
||||
fsOpenFileDirectly = 0xFFFFFFFF,
|
||||
payloadOffset;
|
||||
|
||||
if(!findLayeredFsSymbols(code, size, &fsMountArchive, &fsRegisterArchive, &fsTryOpenFile, &fsOpenFileDirectly) ||
|
||||
!findLayeredFsPayloadOffset(code, size, &payloadOffset)) return false;
|
||||
|
||||
//Setup the payload
|
||||
u8 *payload = code + payloadOffset;
|
||||
memcpy(payload, romfsredir_bin, romfsredir_bin_size);
|
||||
|
||||
//Insert symbols in the payload
|
||||
u32 *payload32 = (u32 *)payload;
|
||||
for(u32 i = 0; i < romfsredir_bin_size / 4; i++)
|
||||
{
|
||||
if(*(u32 *)pos == 0xD8A103F9)
|
||||
switch (payload32[i])
|
||||
{
|
||||
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
||||
if(*l <= 0x10000000) possible[n++] = *l;
|
||||
case 0xdead0000:
|
||||
payload32[i] = *(u32 *)(code + fsOpenFileDirectly);
|
||||
break;
|
||||
case 0xdead0001:
|
||||
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsOpenFileDirectly + 4);
|
||||
break;
|
||||
case 0xdead0002:
|
||||
payload32[i] = *(u32 *)(code + fsTryOpenFile);
|
||||
break;
|
||||
case 0xdead0003:
|
||||
payload32[i] = MAKE_BRANCH(payloadOffset + i * 4, fsTryOpenFile + 4);
|
||||
break;
|
||||
case 0xdead0004:
|
||||
memcpy(payload32 + i, archiveName, 3);
|
||||
memcpy((u8 *)(payload32 + i) + 3, path, sizeof(path));
|
||||
break;
|
||||
case 0xdead0005:
|
||||
payload32[i] = 0x100000 + fsMountArchive;
|
||||
break;
|
||||
case 0xdead0006:
|
||||
payload32[i] = 0x100000 + fsRegisterArchive;
|
||||
break;
|
||||
case 0xdead0007:
|
||||
payload32[i] = archiveId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(n > 0)
|
||||
{
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
{
|
||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||
//Place the hooks
|
||||
*(u32 *)(code + fsOpenFileDirectly) = MAKE_BRANCH(fsOpenFileDirectly, payloadOffset);
|
||||
*(u32 *)(code + fsTryOpenFile) = MAKE_BRANCH(fsTryOpenFile, payloadOffset + 12);
|
||||
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
|
||||
{
|
||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline u32 patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
|
||||
|
||||
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
||||
CFGU_GetConfigInfoBlk2_startPos -= 4);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_startPos >= code)
|
||||
{
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
||||
{
|
||||
if(*(u32 *)languageBlkIdPos == 0xA0002)
|
||||
{
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
{
|
||||
if(instr[3] == 0xEB) //We're looking for BL
|
||||
{
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0;
|
||||
bool found;
|
||||
|
||||
do
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
|
||||
calledFunction += offset;
|
||||
|
||||
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||
|
||||
if(found)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||
|
||||
//We're done
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
{
|
||||
for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
|
||||
{
|
||||
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D0F70, 0xE3A00802};
|
||||
|
||||
u32 *cmp = (u32 *)cmdPos;
|
||||
|
||||
if(*cmp == cfgSecureInfoGetRegionCmdPattern[1])
|
||||
{
|
||||
for(u32 i = 1; i < 3; i++)
|
||||
if((*(cmp - i) & 0xFFFF0FFF) == cfgSecureInfoGetRegionCmdPattern[0] && *((u16 *)cmdPos + 5) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
||||
{
|
||||
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
|
||||
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
|
||||
cmp[6] = 0xE5840004; //str r0, [r4, #4]
|
||||
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize)
|
||||
{
|
||||
loadCFWInfo();
|
||||
u32 res = 0;
|
||||
|
||||
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
||||
progId == 0x0004003000008202LL || //JPN Home Menu
|
||||
@@ -370,12 +599,12 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
};
|
||||
|
||||
//Patch SMDH region checks
|
||||
if(!patchMemory(code, size,
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -31,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||
@@ -384,14 +613,14 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
0x42, 0xE0, 0x1E, 0xFF
|
||||
};
|
||||
|
||||
u8 mostRecentFpdVer = 8;
|
||||
u8 mostRecentFpdVer = 10;
|
||||
|
||||
u8 *off = memsearch(code, pattern, size, sizeof(pattern));
|
||||
u8 *off = memsearch(code, pattern, textSize, sizeof(pattern));
|
||||
|
||||
if(off == NULL) res++;
|
||||
if(off == NULL) goto error;
|
||||
|
||||
//Allow online access to work with old friends modules
|
||||
else if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
}
|
||||
|
||||
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||
@@ -437,12 +666,12 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
}
|
||||
|
||||
//Patch Ver. string
|
||||
if(!patchMemory(code, size,
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 0,
|
||||
patch,
|
||||
patchSize, 1
|
||||
)) res++;
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000008002LL) //NS
|
||||
@@ -457,14 +686,14 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
};
|
||||
|
||||
//Disable updates from foreign carts (makes carts region-free)
|
||||
u32 ret = patchMemory(code, size,
|
||||
u32 ret = patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 2
|
||||
);
|
||||
|
||||
if(ret == 0 || (ret == 1 && progVer > 0xB)) res++;
|
||||
if(ret == 0 || (ret == 1 && progVer > 0xB)) goto error;
|
||||
}
|
||||
|
||||
if(LOADERFLAG(ISN3DS))
|
||||
@@ -477,11 +706,10 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
0x0C, 0x00, 0x94, 0x15
|
||||
};
|
||||
|
||||
u32 *off = (u32 *)memsearch(code, pattern, size, sizeof(pattern));
|
||||
u32 *off = (u32 *)memsearch(code, pattern, textSize, sizeof(pattern));
|
||||
|
||||
if(off == NULL) goto error;
|
||||
|
||||
if(off == NULL) res++;
|
||||
else
|
||||
{
|
||||
//Patch N3DS CPU Clock and L2 cache setting
|
||||
*(off - 4) = *(off - 3);
|
||||
*(off - 3) = *(off - 1);
|
||||
@@ -490,7 +718,6 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000001702LL) //CFG
|
||||
{
|
||||
@@ -502,12 +729,12 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
};
|
||||
|
||||
//Disable SecureInfo signature check
|
||||
if(!patchMemory(code, size,
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
)) goto error;
|
||||
|
||||
if(secureInfoExists())
|
||||
{
|
||||
@@ -515,12 +742,12 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
patch[] = u"C";
|
||||
|
||||
//Use SecureInfo_C
|
||||
if(patchMemory(code, size,
|
||||
if(patchMemory(code + textSize, roSize,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 22,
|
||||
patch,
|
||||
sizeof(patch) - 2, 2
|
||||
) != 2) res++;
|
||||
) != 2) goto error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,31 +766,28 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
||||
};
|
||||
|
||||
//Disable CRR0 signature (RSA2048 with SHA256) check
|
||||
if(!patchMemory(code, size,
|
||||
//Disable CRR0 signature (RSA2048 with SHA256) check and CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -9,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||
if(!patchMemory(code, size,
|
||||
) ||
|
||||
!patchMemory(code, textSize,
|
||||
pattern2,
|
||||
sizeof(pattern2), 1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
if(!patchMemory(code, size,
|
||||
) ||
|
||||
!patchMemory(code, textSize,
|
||||
pattern3,
|
||||
sizeof(pattern3), -2,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
||||
else if(progId == 0x0004003000008A02LL && CONFIG(ENABLEEXCEPTIONHANDLERS) && !CONFIG(PATCHUNITINFO)) //ErrDisp
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x00, 0xD0, 0xE5, 0xDB
|
||||
@@ -576,44 +800,63 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
};
|
||||
|
||||
//Patch UNITINFO checks to make ErrDisp more verbose
|
||||
if(!patchMemory(code, size,
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), -1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
if(patchMemory(code, size,
|
||||
) ||
|
||||
patchMemory(code, textSize,
|
||||
pattern2,
|
||||
sizeof(pattern2), 0,
|
||||
patch,
|
||||
sizeof(patch), 3
|
||||
) != 3) res++;
|
||||
) != 3) goto error;
|
||||
}
|
||||
|
||||
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||
else if(progId == 0x0004013000002802LL) //DLP
|
||||
{
|
||||
//External .code section loading
|
||||
res += loadTitleCodeSection(progId, code, size);
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0xAC, 0xC0, 0xD8
|
||||
},
|
||||
patch[] = {
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
//Language emulation
|
||||
//Patch DLP region checks
|
||||
if(!patchMemory(code, textSize,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) goto error;
|
||||
}
|
||||
|
||||
if(CONFIG(PATCHGAMES) && (u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
||||
{
|
||||
u8 regionId = 0xFF,
|
||||
languageId;
|
||||
res += loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||
|
||||
if(!res && regionId != 0xFF)
|
||||
if(!loadTitleCodeSection(progId, code, size) ||
|
||||
!applyCodeIpsPatch(progId, code, size) ||
|
||||
!loadTitleLocaleConfig(progId, ®ionId, &languageId) ||
|
||||
!patchLayeredFs(progId, code, textSize)) goto error;
|
||||
|
||||
if(regionId != 0xFF)
|
||||
{
|
||||
u32 CFGUHandleOffset;
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, textSize, &CFGUHandleOffset);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_endPos == NULL) res++;
|
||||
else
|
||||
{
|
||||
res += patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||
}
|
||||
if(CFGU_GetConfigInfoBlk2_endPos == NULL ||
|
||||
!patchCfgGetLanguage(code, textSize, languageId, CFGU_GetConfigInfoBlk2_endPos)) goto error;
|
||||
|
||||
patchCfgGetRegion(code, textSize, regionId, CFGUHandleOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if(res != 0) svcBreak(USERBREAK_ASSERT);
|
||||
return;
|
||||
|
||||
error:
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
while(true);
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
|
||||
#define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((info.config >> (a * 2 + 8)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
|
||||
@@ -18,8 +21,7 @@ enum multiOptions
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
PIN,
|
||||
NEWCPU,
|
||||
DEVOPTIONS
|
||||
NEWCPU
|
||||
};
|
||||
|
||||
enum singleOptions
|
||||
@@ -28,10 +30,12 @@ enum singleOptions
|
||||
USESYSFIRM,
|
||||
LOADEXTFIRMSANDMODULES,
|
||||
USECUSTOMPATH,
|
||||
USELANGEMUANDCODE,
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
ENABLEEXCEPTIONHANDLERS
|
||||
};
|
||||
|
||||
enum flags
|
||||
@@ -40,4 +44,4 @@ enum flags
|
||||
ISSAFEMODE
|
||||
};
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size);
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize);
|
||||
|
||||
14
linker.ld
14
linker.ld
@@ -1,11 +1,15 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x23F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(4); }
|
||||
.bss : ALIGN(8) { __bss_start = .; *(.bss* COMMON); . = ALIGN(8); __bss_end = .; }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,7 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
@@ -18,7 +13,7 @@ dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
@@ -33,7 +28,7 @@ clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
@@ -47,4 +42,3 @@ $(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x24FFFE00;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
|
||||
.arm
|
||||
; Interesting registers and locations to keep in mind, set just before this code is ran:
|
||||
; - r1: FIRM path in exefs.
|
||||
; - r7: pointer to file object
|
||||
; - r7 (or r8): pointer to file object
|
||||
; - *r7: vtable
|
||||
; - *(vtable + 0x28): fread function
|
||||
; - *(r7 + 8): file handle
|
||||
|
||||
sub r7, r0, #8
|
||||
mov r8, r1
|
||||
|
||||
pxi_wait_recv:
|
||||
@@ -105,6 +106,8 @@ nand_mount: .dcw "nand"
|
||||
.align 4
|
||||
kernelcode_start:
|
||||
|
||||
ldr sp, =0x080FF000
|
||||
|
||||
; Disable MPU
|
||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
@@ -137,3 +140,4 @@ nand_mount: .dcw "nand"
|
||||
|
||||
.pool
|
||||
.close
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
|
||||
#define DPAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN)
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_B | BUTTON_X | BUTTON_Y)
|
||||
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT)
|
||||
#define EMUNAND_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN)
|
||||
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
||||
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (DPAD_BUTTONS | BUTTON_B | BUTTON_X | BUTTON_Y)
|
||||
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_START | BUTTON_SELECT)
|
||||
#define MENU_BUTTONS (DPAD_BUTTONS | BUTTON_A | BUTTON_START)
|
||||
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | DPAD_BUTTONS | BUTTON_START | BUTTON_SELECT)
|
||||
144
source/config.c
144
source/config.c
@@ -30,6 +30,8 @@
|
||||
#include "pin.h"
|
||||
|
||||
CfgData configData;
|
||||
ConfigurationStatus needConfig;
|
||||
static u32 oldConfig;
|
||||
|
||||
bool readConfig(void)
|
||||
{
|
||||
@@ -41,19 +43,24 @@ bool readConfig(void)
|
||||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
||||
{
|
||||
configData.config = 0;
|
||||
|
||||
ret = false;
|
||||
}
|
||||
else ret = true;
|
||||
|
||||
oldConfig = configData.config;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
||||
void writeConfig(bool isPayloadLaunch)
|
||||
{
|
||||
if(isPayloadLaunch) configData.config = (configData.config & 0xFFFFFF00) | (oldConfig & 0xFF);
|
||||
|
||||
/* If the configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFF7F) != configData.config)
|
||||
{
|
||||
if(needConfig != CREATE_CONFIGURATION && (configData.config & 0xFFFFFF7F) == oldConfig) return;
|
||||
|
||||
if(needConfig == CREATE_CONFIGURATION)
|
||||
{
|
||||
memcpy(configData.magic, "CONF", 4);
|
||||
@@ -61,13 +68,9 @@ void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
||||
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
||||
}
|
||||
|
||||
//Merge the new options and new boot configuration
|
||||
configData.config = (configData.config & 0xFFFFFF00) | (configTemp & 0xFF);
|
||||
|
||||
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
}
|
||||
|
||||
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
{
|
||||
@@ -76,17 +79,18 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
"Splash: Off( ) Before( ) After( ) payloads",
|
||||
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
|
||||
"Dev. features: Off( ) ErrDisp( ) UNITINFO( )"
|
||||
};
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
|
||||
"( ) Use SysNAND FIRM if booting with R",
|
||||
"( ) Enable loading external FIRMs and modules",
|
||||
"( ) Use custom path",
|
||||
"( ) Enable region/language emu. and ext. .code",
|
||||
"( ) Enable game patching",
|
||||
"( ) Show NAND or user string in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Patch SVC/service/archive/ARM9 access"
|
||||
"( ) Patch SVC/service/archive/ARM9 access",
|
||||
"( ) Set developer UNITINFO",
|
||||
"( ) Enable exception handlers"
|
||||
};
|
||||
|
||||
const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
||||
@@ -118,19 +122,6 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
"'Clock+L2' can cause issues with some\n"
|
||||
"games.",
|
||||
|
||||
"Select the developer features.\n\n"
|
||||
"\t* 'Off' disables exception handlers\n"
|
||||
"in FIRM.\n"
|
||||
"\t* 'ErrDisp' displays debug info\n"
|
||||
"on the 'An error has occurred' screen.\n"
|
||||
"\t* 'UNITINFO' makes the console be\n"
|
||||
"always detected as a development unit\n"
|
||||
"(which breaks online features and\n"
|
||||
"allows booting some developer\n"
|
||||
"software and installing dev CIAs).\n\n"
|
||||
"Only change this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
"If enabled, SysNAND will be launched\n"
|
||||
"on boot.\n\n"
|
||||
"Otherwise, an EmuNAND will.\n\n"
|
||||
@@ -162,10 +153,13 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
|
||||
"Enable overriding the region and\n"
|
||||
"language configuration and the usage\n"
|
||||
"of patched code binaries for specific\n"
|
||||
"games.\n\n"
|
||||
"Also makes certain DLCs for\n"
|
||||
"out-of-region games work.\n\n"
|
||||
"of patched code binaries,\n"
|
||||
"IPS code patches and LayeredFS\n"
|
||||
"for specific games.\n\n"
|
||||
"Also makes certain DLCs\n"
|
||||
"for out-of-region games work.\n\n"
|
||||
"Enabling this requires the\n"
|
||||
"archive patch to be applied.\n\n"
|
||||
"Refer to the wiki for instructions.",
|
||||
|
||||
"Enable showing the current NAND/FIRM:\n\n"
|
||||
@@ -188,8 +182,22 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
"The service and archive patches\n"
|
||||
"don't work on New 3DS FIRMs between\n"
|
||||
"9.3 and 10.4.\n\n"
|
||||
"Only change this if you know what you\n"
|
||||
"Only select this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
"Make the console be always detected\n"
|
||||
"as a development unit, and conversely.\n"
|
||||
"(which breaks online features, amiibo\n"
|
||||
"and retail CIAs, but allows installing\n"
|
||||
"and booting some developer software).\n\n"
|
||||
"Only select this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
"Enable Luma3DS's ARM9/ARM11 exception\n"
|
||||
"handlers.\n"
|
||||
"A9LH is required, and Luma3DS should\n"
|
||||
"be ran as arm9loaderhax.bin.\n"
|
||||
"Useful for debugging."
|
||||
};
|
||||
|
||||
struct multiOption {
|
||||
@@ -203,7 +211,6 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
{ .posXs = {12, 22, 31, 0}, .visible = true },
|
||||
{ .posXs = {14, 19, 24, 29}, .visible = true },
|
||||
{ .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
|
||||
{ .posXs = {19, 30, 42, 0}, .visible = true }
|
||||
};
|
||||
|
||||
struct singleOption {
|
||||
@@ -218,6 +225,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true}
|
||||
};
|
||||
|
||||
@@ -225,7 +234,9 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
||||
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
|
||||
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
||||
selectedOption;
|
||||
selectedOption,
|
||||
singleSelected;
|
||||
bool isMultiOption = false;
|
||||
|
||||
//Parse the existing options
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
@@ -236,56 +247,56 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
initScreens();
|
||||
|
||||
drawString(CONFIG_TITLE, true, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to save", true, 10, 30, COLOR_WHITE);
|
||||
drawString("Press A to select, START to save", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
||||
|
||||
//Character to display a selected option
|
||||
char selected = 'x';
|
||||
|
||||
u32 endPos = 42;
|
||||
u32 endPos = 10 + 2 * SPACING_Y;
|
||||
|
||||
//Display all the multiple choice options in white
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
{
|
||||
if(multiOptions[i].visible)
|
||||
{
|
||||
if(!multiOptions[i].visible) continue;
|
||||
|
||||
multiOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
endPos += SPACING_Y / 2;
|
||||
|
||||
//Display all the normal options in white except for the first one
|
||||
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
|
||||
{
|
||||
if(singleOptions[i].visible)
|
||||
{
|
||||
if(!singleOptions[i].visible) continue;
|
||||
|
||||
singleOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
||||
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||
|
||||
if(color == COLOR_RED)
|
||||
{
|
||||
singleSelected = i;
|
||||
selectedOption = i + multiOptionsAmount;
|
||||
color = COLOR_WHITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||
|
||||
u32 pressed = 0;
|
||||
|
||||
//Boring configuration menu
|
||||
while(pressed != BUTTON_START)
|
||||
while(true)
|
||||
{
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
pressed = waitInput(true);
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
|
||||
if(pressed == BUTTON_START) break;
|
||||
|
||||
if(pressed != BUTTON_A)
|
||||
{
|
||||
//Remember the previously selected option
|
||||
@@ -309,13 +320,26 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
pressed = BUTTON_UP;
|
||||
selectedOption = totalIndexes;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
{
|
||||
if(multiOptions[selectedOption].visible) break;
|
||||
if(!multiOptions[selectedOption].visible) continue;
|
||||
|
||||
isMultiOption = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
singleSelected = selectedOption - multiOptionsAmount;
|
||||
|
||||
if(!singleOptions[singleSelected].visible) continue;
|
||||
|
||||
isMultiOption = false;
|
||||
break;
|
||||
}
|
||||
else if(singleOptions[selectedOption - multiOptionsAmount].visible) break;
|
||||
}
|
||||
|
||||
if(selectedOption == oldSelectedOption) continue;
|
||||
@@ -333,21 +357,16 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
}
|
||||
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
if(isMultiOption) drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
|
||||
clearScreens(false, true, false);
|
||||
drawString(optionsDescription[oldSelectedOption], false, 10, 10, COLOR_BLACK);
|
||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
//The selected option's status changed, print the 'x's accordingly
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
if(isMultiOption)
|
||||
{
|
||||
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
||||
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
|
||||
@@ -357,20 +376,15 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
|
||||
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
|
||||
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
|
||||
bool oldEnabled = singleOptions[singleSelected].enabled;
|
||||
singleOptions[singleSelected].enabled = !oldEnabled;
|
||||
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
if(isMultiOption) drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (first 9 bits)
|
||||
@@ -387,8 +401,6 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
|
||||
else if(oldPinStatus) fileDelete(PIN_FILE);
|
||||
|
||||
//Wait for the pressed buttons to change
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
|
||||
chrono(2);
|
||||
wait(2000ULL);
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#define CONFIG_FILE "config.bin"
|
||||
#define CONFIG_VERSIONMAJOR 1
|
||||
#define CONFIG_VERSIONMINOR 7
|
||||
#define CONFIG_VERSIONMINOR 9
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
@@ -44,7 +44,6 @@ enum multiOptions
|
||||
SPLASH,
|
||||
PIN,
|
||||
NEWCPU,
|
||||
DEVOPTIONS
|
||||
};
|
||||
|
||||
enum singleOptions
|
||||
@@ -53,10 +52,12 @@ enum singleOptions
|
||||
USESYSFIRM,
|
||||
LOADEXTFIRMSANDMODULES,
|
||||
USECUSTOMPATH,
|
||||
USELANGEMUANDCODE,
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
ENABLEEXCEPTIONHANDLERS
|
||||
};
|
||||
|
||||
typedef enum ConfigurationStatus
|
||||
@@ -67,5 +68,5 @@ typedef enum ConfigurationStatus
|
||||
} ConfigurationStatus;
|
||||
|
||||
bool readConfig(void);
|
||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
|
||||
void writeConfig(bool isPayloadLaunch);
|
||||
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode);
|
||||
130
source/crypto.c
130
source/crypto.c
@@ -85,9 +85,29 @@ __asm__\
|
||||
|
||||
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
||||
{
|
||||
if(keyslot <= 0x03) return; //Ignore TWL keys for now
|
||||
u32 *key32 = (u32 *)key;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
|
||||
if(keyslot <= 3)
|
||||
{
|
||||
if((mode & AES_CNT_INPUT_ORDER) == AES_INPUT_REVERSED)
|
||||
{
|
||||
REGs_AESTWLKEYS[keyslot][keyType][0] = key32[3];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][1] = key32[2];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][2] = key32[1];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][3] = key32[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
REGs_AESTWLKEYS[keyslot][keyType][0] = key32[0];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][1] = key32[1];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][2] = key32[2];
|
||||
REGs_AESTWLKEYS[keyslot][keyType][3] = key32[3];
|
||||
}
|
||||
}
|
||||
|
||||
else if(keyslot < 0x40)
|
||||
{
|
||||
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
||||
|
||||
REG_AESKEYFIFO[keyType] = key32[0];
|
||||
@@ -95,6 +115,7 @@ static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
||||
REG_AESKEYFIFO[keyType] = key32[2];
|
||||
REG_AESKEYFIFO[keyType] = key32[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void aes_use_keyslot(u8 keyslot)
|
||||
{
|
||||
@@ -299,11 +320,9 @@ static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
__attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE],
|
||||
shaHashBackup[SHA_256_HASH_SIZE];
|
||||
__attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart;
|
||||
static bool didShaHashBackup = false;
|
||||
|
||||
FirmwareSource firmSource;
|
||||
|
||||
@@ -403,11 +422,7 @@ void set6x7xKeys(void)
|
||||
|
||||
bool decryptExeFs(Cxi *cxi)
|
||||
{
|
||||
bool isCxi;
|
||||
|
||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
|
||||
{
|
||||
isCxi = true;
|
||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) != 0) return false;
|
||||
|
||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||
@@ -421,19 +436,13 @@ bool decryptExeFs(Cxi *cxi)
|
||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
else isCxi = false;
|
||||
|
||||
return isCxi && memcmp(cxi, "FIRM", 4) == 0;
|
||||
return memcmp(cxi, "FIRM", 4) == 0;
|
||||
}
|
||||
|
||||
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||
{
|
||||
bool isTicket;
|
||||
|
||||
if(memcmp(ticket->sigIssuer, "Root", 4) == 0)
|
||||
{
|
||||
isTicket = true;
|
||||
if(memcmp(ticket->sigIssuer, "Root", 4) != 0) return false;
|
||||
|
||||
__attribute__((aligned(4))) const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
||||
__attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE],
|
||||
@@ -450,10 +459,25 @@ bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x16);
|
||||
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
else isTicket = false;
|
||||
|
||||
return isTicket && decryptExeFs(cxi);
|
||||
return decryptExeFs(cxi);
|
||||
}
|
||||
|
||||
static inline void twlConsoleInfoInit(void)
|
||||
{
|
||||
u64 twlConsoleId = CFG_UNITINFO != 0 ? OTP_DEVCONSOLEID : (0x80000000ULL | (*(vu64 *)0x01FFB808 ^ 0x8C267B7B358A6AFULL));
|
||||
CFG_TWLUNITINFO = CFG_UNITINFO;
|
||||
OTP_TWLCONSOLEID = twlConsoleId;
|
||||
|
||||
*REG_AESCNT = 0;
|
||||
|
||||
vu32 *k3X = REGs_AESTWLKEYS[3][1], *k1X = REGs_AESTWLKEYS[1][1];
|
||||
|
||||
k3X[0] = (u32)twlConsoleId;
|
||||
k3X[3] = (u32)(twlConsoleId >> 32);
|
||||
|
||||
k1X[2] = (u32)(twlConsoleId >> 32);
|
||||
k1X[3] = (u32)twlConsoleId;
|
||||
}
|
||||
|
||||
void kernel9Loader(Arm9Bin *arm9Section)
|
||||
@@ -476,8 +500,6 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
||||
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
|
||||
|
||||
if(k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt))
|
||||
{
|
||||
//Set 0x11 keyslot
|
||||
__attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = {
|
||||
{0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
|
||||
@@ -489,23 +511,16 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
};
|
||||
|
||||
aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
if(needToDecrypt)
|
||||
{
|
||||
u8 arm9BinSlot;
|
||||
|
||||
if(!k9lVersion) arm9BinSlot = 0x15;
|
||||
else
|
||||
{
|
||||
arm9BinSlot = 0x16;
|
||||
u8 arm9BinSlot = k9lVersion == 0 ? 0x15 : 0x16;
|
||||
|
||||
//Set keyX
|
||||
__attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE];
|
||||
aes_use_keyslot(0x11);
|
||||
aes(keyX, arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
aes(keyX, k9lVersion == 0 ? arm9Section->keyX : arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(arm9BinSlot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Set keyY
|
||||
__attribute__((aligned(4))) u8 keyY[AES_BLOCK_SIZE];
|
||||
@@ -523,19 +538,42 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
if(*startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000) error("Failed to decrypt the ARM9 binary.");
|
||||
}
|
||||
|
||||
//Set >=9.6 KeyXs
|
||||
__attribute__((aligned(4))) u8 keyBlocks[2][AES_BLOCK_SIZE] = {
|
||||
{0xA4, 0x8D, 0xE4, 0xF1, 0x0B, 0x36, 0x44, 0xAA, 0x90, 0x31, 0x28, 0xFF, 0x4D, 0xCA, 0x76, 0xDF},
|
||||
{0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98}
|
||||
}, decKey[AES_BLOCK_SIZE];
|
||||
|
||||
u8 firstKey;
|
||||
u32 keyBlocksIndex;
|
||||
|
||||
if(k9lVersion == 2)
|
||||
{
|
||||
__attribute__((aligned(4))) u8 keyData[AES_BLOCK_SIZE] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98},
|
||||
decKey[sizeof(keyData)];
|
||||
|
||||
//Set keys 0x19..0x1F keyXs
|
||||
aes_use_keyslot(0x11);
|
||||
for(u8 slot = 0x19; slot < 0x20; slot++, keyData[0xF]++)
|
||||
firstKey = 0x19;
|
||||
keyBlocksIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
aes(decKey, keyData, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
firstKey = 0x18;
|
||||
keyBlocksIndex = 0;
|
||||
}
|
||||
|
||||
aes_use_keyslot(0x11);
|
||||
for(u8 slot = firstKey; slot < 0x20; slot++, keyBlocks[keyBlocksIndex][0xF]++)
|
||||
{
|
||||
aes(decKey, keyBlocks[keyBlocksIndex], 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
if(!ISSIGHAX) return;
|
||||
|
||||
twlConsoleInfoInit();
|
||||
|
||||
if(k9lVersion == 2)
|
||||
{
|
||||
aes_setkey(0x11, key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x11);
|
||||
aes(decKey, keyBlocks[0], 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(0x18, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,12 +590,18 @@ void computePinHash(u8 *outbuf, const u8 *inbuf)
|
||||
|
||||
void backupAndRestoreShaHash(bool isRestore)
|
||||
{
|
||||
if(ISA9LH)
|
||||
{
|
||||
if(!ISA9LH) return;
|
||||
|
||||
static bool didShaHashBackup = false;
|
||||
__attribute__((aligned(4))) static u8 shaHashBackup[SHA_256_HASH_SIZE];
|
||||
|
||||
if(isRestore)
|
||||
{
|
||||
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
||||
}
|
||||
else if(!didShaHashBackup) memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
||||
else if(!didShaHashBackup)
|
||||
{
|
||||
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
||||
didShaHashBackup = true;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,8 @@
|
||||
#define REG_AESKEYXFIFO ((vu32 *)0x10009104)
|
||||
#define REG_AESKEYYFIFO ((vu32 *)0x10009108)
|
||||
|
||||
#define REGs_AESTWLKEYS (*((vu32 (*)[4][3][4])0x10009040))
|
||||
|
||||
#define AES_CCM_DECRYPT_MODE (0u << 27)
|
||||
#define AES_CCM_ENCRYPT_MODE (1u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
|
||||
@@ -38,31 +38,23 @@ bool loadSplash(void)
|
||||
*bottomSplashFile = "splashbottom.bin";
|
||||
|
||||
bool isTopSplashValid = getFileSize(topSplashFile) == SCREEN_TOP_FBSIZE,
|
||||
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE,
|
||||
ret;
|
||||
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE;
|
||||
|
||||
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
|
||||
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
|
||||
else
|
||||
{
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
initScreens();
|
||||
clearScreens(true, true, true);
|
||||
clearScreens(true);
|
||||
|
||||
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
|
||||
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
||||
|
||||
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
|
||||
else
|
||||
{
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
swapFramebuffers(true);
|
||||
wait(3000ULL);
|
||||
|
||||
chrono(3);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
||||
|
||||
@@ -36,7 +36,6 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
static u8 __attribute__((aligned(4))) temp[0x200];
|
||||
static u32 nandSize = 0,
|
||||
fatStart;
|
||||
bool found = false;
|
||||
|
||||
if(!nandSize)
|
||||
{
|
||||
@@ -45,7 +44,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
fatStart = *(u32 *)(temp + 0x1C6); //First sector of the FAT partition
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 3 && !found; i++)
|
||||
for(u32 i = 0; i < 3; i++)
|
||||
{
|
||||
static const u32 roundedMinsizes[] = {0x1D8000, 0x26E000};
|
||||
|
||||
@@ -58,7 +57,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
case 2:
|
||||
nandOffset = roundedMinsizes[ISN3DS ? 1 : 0]; //"Minsize" layout
|
||||
break;
|
||||
default:
|
||||
case 0:
|
||||
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
|
||||
break;
|
||||
}
|
||||
@@ -72,7 +71,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
{
|
||||
emuOffset = nandOffset + 1;
|
||||
*emuHeader = nandOffset + 1;
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//Check for Gateway EmuNAND
|
||||
@@ -80,7 +79,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
{
|
||||
emuOffset = nandOffset;
|
||||
*emuHeader = nandOffset + nandSize;
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,8 +87,6 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
}
|
||||
|
||||
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
||||
if(!found)
|
||||
{
|
||||
if(*nandType != FIRMWARE_EMUNAND)
|
||||
{
|
||||
*nandType = FIRMWARE_EMUNAND;
|
||||
@@ -97,105 +94,81 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
}
|
||||
else *nandType = FIRMWARE_SYSNAND;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||
static inline bool getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
u32 ret;
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
*freeK9Space = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(*freeK9Space == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(*freeK9Space == NULL) return false;
|
||||
|
||||
*freeK9Space += 0x455;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
||||
{
|
||||
//Look for struct code
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
u32 ret;
|
||||
|
||||
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
||||
{
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
u32 ret;
|
||||
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(readOffset == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(readOffset == NULL) return 1;
|
||||
|
||||
readOffset -= 3;
|
||||
|
||||
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
||||
|
||||
if(writeOffset == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(writeOffset == NULL) return 1;
|
||||
|
||||
writeOffset -= 3;
|
||||
*readOffset = *writeOffset = 0x4C00;
|
||||
readOffset[1] = writeOffset[1] = 0x47A0;
|
||||
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 patchMpu(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[1] = 0x0036;
|
||||
off[0xC] = off[0x12] = 0x0603;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address)
|
||||
{
|
||||
u8 *freeK9Space;
|
||||
|
||||
if(!getFreeK9Space(arm9Section, kernel9Size, &freeK9Space)) return 1;
|
||||
|
||||
u32 ret = 0;
|
||||
|
||||
u8 *freeK9Space;
|
||||
ret += getFreeK9Space(arm9Section, kernel9Size, &freeK9Space);
|
||||
|
||||
if(!ret)
|
||||
{
|
||||
//Copy EmuNAND code
|
||||
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
||||
|
||||
@@ -217,7 +190,6 @@ u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 proce
|
||||
|
||||
//Set MPU
|
||||
ret += patchMpu(arm9Section, kernel9Size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -45,9 +45,8 @@ void installArm9Handlers(void)
|
||||
}
|
||||
}
|
||||
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset, u32 *dAbtHandler, u32 dAbtHandlerMemAddress)
|
||||
{
|
||||
u32 ret;
|
||||
u32 *endPos = exceptionsPage + 0x400;
|
||||
|
||||
u32 *initFPU;
|
||||
@@ -59,9 +58,8 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
||||
u32 *mcuReboot;
|
||||
for(mcuReboot = exceptionsPage; mcuReboot < endPos && *mcuReboot != 0xE3A0A0C2; mcuReboot++);
|
||||
|
||||
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
|
||||
else
|
||||
{
|
||||
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) return 1;
|
||||
|
||||
initFPU += 3;
|
||||
mcuReboot -= 2;
|
||||
|
||||
@@ -69,14 +67,33 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
||||
|
||||
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
||||
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
||||
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
|
||||
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
||||
|
||||
for(u32 *pos = dAbtHandler; *pos != stackAddress; pos++)
|
||||
{
|
||||
u32 va_dst = 0xFFFF0000 + (((u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4)) - (u8 *)exceptionsPage);
|
||||
u32 va_src;
|
||||
switch(*pos)
|
||||
{
|
||||
case 0xF96D0513: //srsdb sp!, 0x13
|
||||
va_src = dAbtHandlerMemAddress + ((u8 *)pos - (u8 *)dAbtHandler);
|
||||
*pos = MAKE_BRANCH((u8 *)va_src, (u8 *)va_dst);
|
||||
break;
|
||||
case 0xE29EF004: //subs pc, lr, 4
|
||||
pos++;
|
||||
*pos++ = 0xE8BD000F;// pop {r0-r3}
|
||||
va_src = dAbtHandlerMemAddress + ((u8 *)pos - (u8 *)dAbtHandler);
|
||||
*pos = MAKE_BRANCH((u8 *)va_src, (u8 *)va_dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
||||
{
|
||||
switch(*pos) //Perform relocations
|
||||
{
|
||||
case 0xFFFF3000: *pos = stackAddress; break;
|
||||
case 0xFFFF3000: *pos = stackAddress - 0x10; break;
|
||||
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
||||
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
||||
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
||||
@@ -84,18 +101,15 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void detectAndProcessExceptionDumps(void)
|
||||
{
|
||||
volatile ExceptionDumpHeader *dumpHeader = (volatile ExceptionDumpHeader *)0x25000000;
|
||||
|
||||
if(dumpHeader->magic[0] == 0xDEADC0DE && dumpHeader->magic[1] == 0xDEADCAFE && (dumpHeader->processor == 9 || dumpHeader->processor == 11))
|
||||
{
|
||||
if(dumpHeader->magic[0] != 0xDEADC0DE || dumpHeader->magic[1] != 0xDEADCAFE || (dumpHeader->processor != 9 && dumpHeader->processor != 11)) return;
|
||||
|
||||
const vu32 *regs = (vu32 *)((vu8 *)dumpHeader + sizeof(ExceptionDumpHeader));
|
||||
const vu8 *stackDump = (vu8 *)regs + dumpHeader->registerDumpSize + dumpHeader->codeDumpSize;
|
||||
const vu8 *additionalData = stackDump + dumpHeader->stackDumpSize;
|
||||
@@ -132,7 +146,7 @@ void detectAndProcessExceptionDumps(void)
|
||||
if(instr == 0xE12FFF7E) drawString(specialExceptions[0], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
else if(instr == 0xEF00003C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
}
|
||||
else if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 2)
|
||||
else if((regs[16] & 0x20) != 0 && dumpHeader->codeDumpSize >= 2)
|
||||
{
|
||||
u16 instr = *(vu16 *)(stackDump - 2);
|
||||
if(instr == 0xDF3C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
@@ -184,12 +198,11 @@ void detectAndProcessExceptionDumps(void)
|
||||
}
|
||||
}
|
||||
|
||||
char path[36];
|
||||
char fileName[] = "crash_dump_00000000.dmp";
|
||||
const char *pathFolder = dumpHeader->processor == 9 ? "dumps/arm9" : "dumps/arm11";
|
||||
char path[36] = "dumps/",
|
||||
fileName[] = "crash_dump_00000000.dmp";
|
||||
|
||||
findDumpFile(pathFolder, fileName);
|
||||
memcpy(path, pathFolder, strlen(pathFolder) + 1);
|
||||
concatenateStrings(path, dumpHeader->processor == 9 ? "arm9" : "arm11");
|
||||
findDumpFile(path, fileName);
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, fileName);
|
||||
|
||||
@@ -204,7 +217,6 @@ void detectAndProcessExceptionDumps(void)
|
||||
|
||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||
|
||||
waitInput();
|
||||
waitInput(false);
|
||||
mcuPowerOff();
|
||||
}
|
||||
}
|
||||
@@ -28,5 +28,5 @@
|
||||
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
|
||||
void installArm9Handlers(void);
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
|
||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset, u32 *dAbtHandler, u32 dAbtHandlerMemAddress);
|
||||
void detectAndProcessExceptionDumps(void);
|
||||
13
source/fatfs/00history.txt
Executable file → Normal file
13
source/fatfs/00history.txt
Executable file → Normal file
@@ -212,7 +212,7 @@ R0.10a (January 15, 2014)
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ R0.12a (July 10, 2016)
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Improved f_rename() to be able to rename objects with the same name but case.
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
@@ -277,3 +277,12 @@ R0.12b (September 04, 2016)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
||||
16
source/fatfs/00readme.txt
Executable file → Normal file
16
source/fatfs/00readme.txt
Executable file → Normal file
@@ -1,21 +1,21 @@
|
||||
FatFs Module Source Files R0.12a
|
||||
FatFs Module Source Files R0.12c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
option Optional external modules.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
||||
|
||||
657
source/fatfs/ff.c
Executable file → Normal file
657
source/fatfs/ff.c
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
25
source/fatfs/ff.h
Executable file → Normal file
25
source/fatfs/ff.h
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12b /
|
||||
/ FatFs - Generic FAT file system module R0.12c /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
/ Copyright (C) 2017, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 68020 /* Revision ID */
|
||||
#define _FATFS 68300 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -42,13 +42,6 @@ typedef struct {
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -140,14 +133,15 @@ typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if _FS_EXFAT
|
||||
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_cont; /* Size of first fragment, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0 and non-directory object) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
@@ -163,7 +157,7 @@ typedef struct {
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||
@@ -185,7 +179,7 @@ typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if _USE_LFN != 0
|
||||
@@ -285,6 +279,7 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
|
||||
21
source/fatfs/ffconf.h
Executable file → Normal file
21
source/fatfs/ffconf.h
Executable file → Normal file
@@ -2,7 +2,7 @@
|
||||
/ FatFs - FAT file system module configuration file
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 68020 /* Revision ID */
|
||||
#define _FFCONF 68300 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -73,7 +73,7 @@
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 1 - ASCII (No support of extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
@@ -148,7 +148,7 @@
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
@@ -172,11 +172,11 @@
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
/ to variable sector size and GET_SECTOR_SIZE command needs to be implemented to
|
||||
/ the disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
@@ -204,7 +204,7 @@
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes.
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the file system object (FATFS) is used for the file data transfer. */
|
||||
|
||||
@@ -212,13 +212,13 @@
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
|
||||
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards C89 compatibility. */
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 1
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2016
|
||||
#define _NORTC_YEAR 2017
|
||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
@@ -258,10 +258,11 @@
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _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. */
|
||||
|
||||
/* #include <windows.h> // O/S definitions */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
2
source/fatfs/integer.h
Executable file → Normal file
2
source/fatfs/integer.h
Executable file → Normal file
@@ -30,7 +30,7 @@ typedef unsigned short WCHAR;
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
|
||||
/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#endif
|
||||
|
||||
0
source/fatfs/option/ccsbcs.c
Executable file → Normal file
0
source/fatfs/option/ccsbcs.c
Executable file → Normal file
116
source/firm.c
116
source/firm.c
@@ -36,21 +36,47 @@
|
||||
|
||||
static Firm *firm = (Firm *)0x24000000;
|
||||
|
||||
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
|
||||
static inline bool loadFirmFromStorage(FirmwareType firmType)
|
||||
{
|
||||
const char *firmwareFiles[] = {
|
||||
"firmware.bin",
|
||||
"firmware_twl.bin",
|
||||
"firmware_agb.bin",
|
||||
"firmware_safe.bin"
|
||||
"firmware_safe.bin",
|
||||
"firmware_sysupdater.bin"
|
||||
},
|
||||
*cetkFiles[] = {
|
||||
"cetk",
|
||||
"cetk_twl",
|
||||
"cetk_agb",
|
||||
"cetk_safe"
|
||||
"cetk_safe",
|
||||
"cetk_sysupdater"
|
||||
};
|
||||
|
||||
u32 firmSize = fileRead(firm, firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
||||
|
||||
if(!firmSize) return false;
|
||||
|
||||
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
|
||||
|
||||
if(memcmp(firm, "FIRM", 4) != 0)
|
||||
{
|
||||
u8 cetk[0xA50];
|
||||
|
||||
if(fileRead(cetk, firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk) ||
|
||||
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
|
||||
error("The FIRM in /luma is encrypted or corrupted.");
|
||||
}
|
||||
|
||||
//Check that the FIRM is right for the console from the ARM9 section address
|
||||
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||
error("The FIRM in /luma is not for this console.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
|
||||
{
|
||||
//Load FIRM from CTRNAND
|
||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||
|
||||
@@ -75,32 +101,8 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
||||
else if(firmVersion < 0x25) mustLoadFromStorage = true;
|
||||
}
|
||||
|
||||
if(loadFromStorage || mustLoadFromStorage)
|
||||
{
|
||||
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
||||
|
||||
if(firmSize > 0)
|
||||
{
|
||||
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
|
||||
|
||||
if(memcmp(firm, "FIRM", 4) != 0)
|
||||
{
|
||||
u8 cetk[0xA50];
|
||||
|
||||
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) != sizeof(cetk) ||
|
||||
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
|
||||
error("The FIRM in /luma is encrypted or corrupted.");
|
||||
}
|
||||
|
||||
//Check that the FIRM is right for the console from the ARM9 section address
|
||||
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||
error("The FIRM in /luma is not for this console.");
|
||||
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if(firmVersion != 0xFFFFFFFF)
|
||||
if((loadFromStorage || mustLoadFromStorage) && loadFirmFromStorage(*firmType)) firmVersion = 0xFFFFFFFF;
|
||||
else
|
||||
{
|
||||
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
||||
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
||||
@@ -110,7 +112,7 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
||||
return firmVersion;
|
||||
}
|
||||
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode)
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, bool doUnitinfoPatch, bool enableExceptionHandlers)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
|
||||
*arm11Section1 = (u8 *)firm + firm->section[1].offset;
|
||||
@@ -134,8 +136,9 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
u32 baseK11VA;
|
||||
u8 *freeK11Space;
|
||||
u32 *arm11SvcHandler,
|
||||
*arm11DAbtHandler,
|
||||
*arm11ExceptionsPage,
|
||||
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
|
||||
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11DAbtHandler, &arm11ExceptionsPage);
|
||||
|
||||
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
|
||||
ret = 0;
|
||||
@@ -169,22 +172,25 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
ret += reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||
}
|
||||
|
||||
//Stub svc 0x59 on 11.3+ FIRMs
|
||||
if(firmVersion >= (ISN3DS ? 0x2D : 0x5C)) ret += stubSvcRestrictGpuDma(arm11Section1, arm11SvcTable, baseK11VA);
|
||||
|
||||
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
|
||||
|
||||
//Apply UNITINFO patches
|
||||
if(devMode == 2)
|
||||
if(doUnitinfoPatch)
|
||||
{
|
||||
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
|
||||
}
|
||||
|
||||
if(devMode != 0 && isA9lhInstalled)
|
||||
if(enableExceptionHandlers && isA9lhInstalled)
|
||||
{
|
||||
//ARM11 exception handlers
|
||||
u32 codeSetOffset,
|
||||
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, firm->section[1].size, &codeSetOffset);
|
||||
ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
|
||||
patchSvcBreak11(arm11Section1, arm11SvcTable);
|
||||
ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset, arm11DAbtHandler, baseK11VA + ((u8 *)arm11DAbtHandler - arm11Section1));
|
||||
patchSvcBreak11(arm11Section1, arm11SvcTable, baseK11VA);
|
||||
ret += patchKernel11Panic(arm11Section1, firm->section[1].size);
|
||||
|
||||
//ARM9 exception handlers
|
||||
@@ -193,17 +199,24 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
ret += patchKernel9Panic(arm9Section, kernel9Size);
|
||||
}
|
||||
|
||||
if(CONFIG(PATCHACCESS))
|
||||
bool patchAccess = CONFIG(PATCHACCESS),
|
||||
patchGames = CONFIG(PATCHGAMES);
|
||||
|
||||
if(patchAccess || patchGames)
|
||||
{
|
||||
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space, patchGames);
|
||||
|
||||
if(patchAccess)
|
||||
{
|
||||
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
|
||||
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space);
|
||||
ret += patchP9AccessChecks(process9Offset, process9Size);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchTwlFirm(u32 firmVersion, u32 devMode)
|
||||
u32 patchTwlFirm(u32 firmVersion, bool doUnitinfoPatch)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
|
||||
|
||||
@@ -231,12 +244,12 @@ u32 patchTwlFirm(u32 firmVersion, u32 devMode)
|
||||
ret += patchTwlShaHashChecks(process9Offset, process9Size);
|
||||
|
||||
//Apply UNITINFO patch
|
||||
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchAgbFirm(u32 devMode)
|
||||
u32 patchAgbFirm(bool doUnitinfoPatch)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
|
||||
|
||||
@@ -259,12 +272,12 @@ u32 patchAgbFirm(u32 devMode)
|
||||
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size);
|
||||
|
||||
//Apply UNITINFO patch
|
||||
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patch1x2xNativeAndSafeFirm(u32 devMode)
|
||||
u32 patch1x2xNativeAndSafeFirm(bool enableExceptionHandlers)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
|
||||
|
||||
@@ -285,7 +298,9 @@ u32 patch1x2xNativeAndSafeFirm(u32 devMode)
|
||||
|
||||
ret += ISN3DS ? patchFirmWrites(process9Offset, process9Size) : patchOldFirmWrites(process9Offset, process9Size);
|
||||
|
||||
if(devMode != 0)
|
||||
ret += ISN3DS ? patchSignatureChecks(process9Offset, process9Size) : patchOldSignatureChecks(process9Offset, process9Size);
|
||||
|
||||
if(enableExceptionHandlers)
|
||||
{
|
||||
//ARM9 exception handlers
|
||||
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
|
||||
@@ -308,10 +323,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
||||
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
|
||||
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
|
||||
|
||||
bool loadedModule;
|
||||
|
||||
if(!loadFromStorage) loadedModule = false;
|
||||
else
|
||||
if(loadFromStorage)
|
||||
{
|
||||
char fileName[24] = "sysmodules/";
|
||||
|
||||
@@ -321,8 +333,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
||||
|
||||
dstModuleSize = getFileSize(fileName);
|
||||
|
||||
if(dstModuleSize == 0) loadedModule = false;
|
||||
else
|
||||
if(dstModuleSize != 0)
|
||||
{
|
||||
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
||||
|
||||
@@ -332,12 +343,10 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
||||
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
|
||||
error("An external FIRM module is invalid or corrupted.");
|
||||
|
||||
loadedModule = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!loadedModule)
|
||||
{
|
||||
const u8 *module;
|
||||
|
||||
if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0)
|
||||
@@ -356,13 +365,12 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
||||
memcpy(dst, module, dstModuleSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void launchFirm(FirmwareType firmType, bool loadFromStorage)
|
||||
{
|
||||
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
|
||||
u32 sectionNum;
|
||||
if(firmType == NATIVE_FIRM || (loadFromStorage && firmType != SAFE_FIRM && firmType != NATIVE_FIRM1X2X))
|
||||
if(firmType == NATIVE_FIRM || (loadFromStorage && (firmType == TWL_FIRM || firmType == AGB_FIRM)))
|
||||
{
|
||||
copySection0AndInjectSystemModules(firmType, loadFromStorage);
|
||||
sectionNum = 1;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
#include "types.h"
|
||||
|
||||
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode);
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode);
|
||||
u32 patchTwlFirm(u32 firmVersion, u32 devMode);
|
||||
u32 patchAgbFirm(u32 devMode);
|
||||
u32 patch1x2xNativeAndSafeFirm(u32 devMode);
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, bool doUnitinfoPatch, bool enableExceptionHandlers);
|
||||
u32 patchTwlFirm(u32 firmVersion, bool doUnitinfoPatch);
|
||||
u32 patchAgbFirm(bool doUnitinfoPatch);
|
||||
u32 patch1x2xNativeAndSafeFirm(bool enableExceptionHandlers);
|
||||
void launchFirm(FirmwareType firmType, bool loadFromStorage);
|
||||
169
source/fs.c
169
source/fs.c
@@ -26,6 +26,9 @@
|
||||
#include "crypto.h"
|
||||
#include "cache.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "fatfs/ff.h"
|
||||
#include "buttons.h"
|
||||
#include "../build/bundled.h"
|
||||
@@ -36,23 +39,17 @@ static FATFS sdFs,
|
||||
static bool switchToMainDir(bool isSd)
|
||||
{
|
||||
const char *mainDir = isSd ? "/luma" : "/rw/luma";
|
||||
bool ret;
|
||||
|
||||
switch(f_chdir(mainDir))
|
||||
{
|
||||
case FR_OK:
|
||||
ret = true;
|
||||
break;
|
||||
return true;
|
||||
case FR_NO_PATH:
|
||||
f_mkdir(mainDir);
|
||||
ret = switchToMainDir(isSd);
|
||||
break;
|
||||
return switchToMainDir(isSd);
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mountFs(bool isSd, bool switchToCtrNand)
|
||||
@@ -64,17 +61,15 @@ bool mountFs(bool isSd, bool switchToCtrNand)
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
{
|
||||
FIL file;
|
||||
u32 ret;
|
||||
u32 ret = 0;
|
||||
|
||||
if(f_open(&file, path, FA_READ) != FR_OK) return ret;
|
||||
|
||||
if(f_open(&file, path, FA_READ) != FR_OK) ret = 0;
|
||||
else
|
||||
{
|
||||
u32 size = f_size(&file);
|
||||
if(dest == NULL) ret = size;
|
||||
else if(size <= maxSize)
|
||||
f_read(&file, dest, size, (unsigned int *)&ret);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -87,7 +82,6 @@ u32 getFileSize(const char *path)
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
FIL file;
|
||||
bool ret;
|
||||
|
||||
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||
{
|
||||
@@ -98,8 +92,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
f_truncate(&file);
|
||||
f_close(&file);
|
||||
|
||||
ret = (u32)written == size;
|
||||
break;
|
||||
return (u32)written == size;
|
||||
}
|
||||
case FR_NO_PATH:
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
@@ -111,14 +104,10 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
f_mkdir(folder);
|
||||
}
|
||||
|
||||
ret = fileWrite(buffer, path, size);
|
||||
break;
|
||||
return fileWrite(buffer, path, size);
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fileDelete(const char *path)
|
||||
@@ -126,7 +115,14 @@ void fileDelete(const char *path)
|
||||
f_unlink(path);
|
||||
}
|
||||
|
||||
void loadPayload(u32 pressed)
|
||||
void loadPayload(u32 pressed, const char *payloadPath)
|
||||
{
|
||||
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
||||
u8 *payloadAddress = (u8 *)0x24F00000;
|
||||
u32 payloadSize = 0,
|
||||
maxPayloadSize = (u32)((u8 *)loaderAddress - payloadAddress);
|
||||
|
||||
if(payloadPath == NULL)
|
||||
{
|
||||
const char *pattern;
|
||||
|
||||
@@ -144,28 +140,28 @@ void loadPayload(u32 pressed)
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
FRESULT result;
|
||||
char path[22] = "payloads";
|
||||
|
||||
FRESULT result = f_findfirst(&dir, &info, path, pattern);
|
||||
result = f_findfirst(&dir, &info, path, pattern);
|
||||
|
||||
if(result != FR_OK) return;
|
||||
|
||||
if(result == FR_OK)
|
||||
{
|
||||
f_closedir(&dir);
|
||||
|
||||
if(info.fname[0] != 0)
|
||||
{
|
||||
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
||||
u8 *payloadAddress = (u8 *)0x24F00000;
|
||||
|
||||
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
||||
if(!info.fname[0]) return;
|
||||
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, info.altname);
|
||||
payloadSize = fileRead(payloadAddress, path, maxPayloadSize);
|
||||
}
|
||||
else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize);
|
||||
|
||||
u32 payloadSize = fileRead(payloadAddress, path, (u32)((u8 *)loaderAddress - payloadAddress));
|
||||
if(!payloadSize) return;
|
||||
|
||||
if(payloadSize > 0)
|
||||
{
|
||||
writeConfig(true);
|
||||
|
||||
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
||||
loaderAddress[1] = payloadSize;
|
||||
|
||||
backupAndRestoreShaHash(true);
|
||||
@@ -176,8 +172,98 @@ void loadPayload(u32 pressed)
|
||||
|
||||
((void (*)())loaderAddress)();
|
||||
}
|
||||
|
||||
void payloadMenu(void)
|
||||
{
|
||||
DIR dir;
|
||||
char path[62] = "payloads";
|
||||
|
||||
if(f_opendir(&dir, path) != FR_OK) return;
|
||||
|
||||
FILINFO info;
|
||||
u32 payloadNum = 0;
|
||||
char payloadList[20][49];
|
||||
|
||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0 && payloadNum < 20)
|
||||
{
|
||||
if(info.fname[0] == '.') continue;
|
||||
|
||||
u32 nameLength = strlen(info.fname);
|
||||
|
||||
if(nameLength < 5 || nameLength > 52) continue;
|
||||
|
||||
nameLength -= 4;
|
||||
|
||||
if(memcmp(info.fname + nameLength, ".bin", 4) != 0) continue;
|
||||
|
||||
memcpy(payloadList[payloadNum], info.fname, nameLength);
|
||||
payloadList[payloadNum][nameLength] = 0;
|
||||
payloadNum++;
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(!payloadNum) return;
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString("Luma3DS chainloader", true, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to quit", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
||||
|
||||
for(u32 i = 0, posY = 10 + 3 * SPACING_Y, color = COLOR_RED; i < payloadNum; i++, posY += SPACING_Y)
|
||||
{
|
||||
drawString(payloadList[i], true, 10, posY, color);
|
||||
if(color == COLOR_RED) color = COLOR_WHITE;
|
||||
}
|
||||
|
||||
u32 pressed = 0,
|
||||
selectedPayload = 0;
|
||||
|
||||
while(pressed != BUTTON_A && pressed != BUTTON_START)
|
||||
{
|
||||
do
|
||||
{
|
||||
pressed = waitInput(true);
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
|
||||
u32 oldSelectedPayload = selectedPayload;
|
||||
|
||||
switch(pressed)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
selectedPayload = !selectedPayload ? payloadNum - 1 : selectedPayload - 1;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
selectedPayload = selectedPayload == payloadNum - 1 ? 0 : selectedPayload + 1;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
selectedPayload = 0;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
selectedPayload = payloadNum - 1;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if(oldSelectedPayload == selectedPayload) continue;
|
||||
|
||||
drawString(payloadList[oldSelectedPayload], true, 10, 10 + (3 + oldSelectedPayload) * SPACING_Y, COLOR_WHITE);
|
||||
drawString(payloadList[selectedPayload], true, 10, 10 + (3 + selectedPayload) * SPACING_Y, COLOR_RED);
|
||||
}
|
||||
|
||||
if(pressed == BUTTON_A)
|
||||
{
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, payloadList[selectedPayload]);
|
||||
concatenateStrings(path, ".bin");
|
||||
loadPayload(0, path);
|
||||
error("The payload is too large or corrupted.");
|
||||
}
|
||||
|
||||
while(HID_PAD & MENU_BUTTONS);
|
||||
wait(2000ULL);
|
||||
}
|
||||
|
||||
u32 firmRead(void *dest, u32 firmType)
|
||||
@@ -195,8 +281,8 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
DIR dir;
|
||||
u32 firmVersion = 0xFFFFFFFF;
|
||||
|
||||
if(f_opendir(&dir, path) == FR_OK)
|
||||
{
|
||||
if(f_opendir(&dir, path) != FR_OK) goto exit;
|
||||
|
||||
FILINFO info;
|
||||
|
||||
//Parse the target directory
|
||||
@@ -213,8 +299,8 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(firmVersion != 0xFFFFFFFF)
|
||||
{
|
||||
if(firmVersion == 0xFFFFFFFF) goto exit;
|
||||
|
||||
//Complete the string with the .app name
|
||||
concatenateStrings(path, "/00000000.app");
|
||||
|
||||
@@ -222,9 +308,8 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
hexItoa(firmVersion, path + 35, 8, false);
|
||||
|
||||
if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return firmVersion;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||
u32 getFileSize(const char *path);
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||
void fileDelete(const char *path);
|
||||
void loadPayload(u32 pressed);
|
||||
void loadPayload(u32 pressed, const char *payloadPath);
|
||||
void payloadMenu(void);
|
||||
u32 firmRead(void *dest, u32 firmType);
|
||||
void findDumpFile(const char *path, char *fileName);
|
||||
25
source/i2c.c
25
source/i2c.c
@@ -115,6 +115,31 @@ static bool i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
u8 i2cReadRegister(u8 dev_id, u8 reg)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id),
|
||||
dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||
{
|
||||
if(i2cSelectDevice(bus_id, dev_addr | 1))
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
i2cStop(bus_id, 1);
|
||||
i2cWaitBusy(bus_id);
|
||||
|
||||
return *i2cGetDataReg(bus_id);
|
||||
}
|
||||
}
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
i2cWaitBusy(bus_id);
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id),
|
||||
|
||||
@@ -41,4 +41,5 @@
|
||||
#define I2C_DEV_GYRO 10
|
||||
#define I2C_DEV_IR 13
|
||||
|
||||
u8 i2cReadRegister(u8 dev_id, u8 reg);
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
@@ -30,19 +30,20 @@
|
||||
#include "strings.h"
|
||||
#include "buttons.h"
|
||||
#include "pin.h"
|
||||
#include "crypto.h"
|
||||
|
||||
extern CfgData configData;
|
||||
extern ConfigurationStatus needConfig;
|
||||
extern FirmwareSource firmSource;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bool isA9lhInstalled,
|
||||
isSafeMode = false;
|
||||
u32 configTemp,
|
||||
emuHeader;
|
||||
isSafeMode = false,
|
||||
isNoForceFlagSet = false;
|
||||
u32 emuHeader;
|
||||
FirmwareType firmType;
|
||||
FirmwareSource nandType;
|
||||
ConfigurationStatus needConfig;
|
||||
|
||||
//Mount SD or CTRNAND
|
||||
bool isSdMode;
|
||||
@@ -78,9 +79,10 @@ void main(void)
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
isA9lhInstalled = BOOTCFG_A9LH != 0;
|
||||
|
||||
goto boot;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(ISA9LH)
|
||||
{
|
||||
detectAndProcessExceptionDumps();
|
||||
@@ -93,36 +95,33 @@ void main(void)
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
//Save old options and begin saving the new boot configuration
|
||||
configTemp = (configData.config & 0xFFFFFF00) | ((u32)ISA9LH << 6);
|
||||
|
||||
//If it's a MCU reboot, try to force boot options
|
||||
if(ISA9LH && CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
|
||||
{
|
||||
|
||||
//Always force a SysNAND boot when quitting AGB_FIRM
|
||||
if(CFG_BOOTENV == 7)
|
||||
{
|
||||
nandType = FIRMWARE_SYSNAND;
|
||||
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
|
||||
//Flag to prevent multiple boot options-forcing
|
||||
configTemp |= 1 << 7;
|
||||
//Prevent multiple boot options-forcing
|
||||
isNoForceFlagSet = true;
|
||||
|
||||
goto boot;
|
||||
}
|
||||
|
||||
/* Else, force the last used boot options unless a button is pressed
|
||||
or the no-forcing flag is set */
|
||||
else if(!pressed && !BOOTCFG_NOFORCEFLAG)
|
||||
if(!pressed && !BOOTCFG_NOFORCEFLAG)
|
||||
{
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
|
||||
goto boot;
|
||||
}
|
||||
}
|
||||
|
||||
//Boot options aren't being forced
|
||||
if(needConfig != DONT_CONFIGURE)
|
||||
{
|
||||
u32 pinMode = MULTICONFIG(PIN);
|
||||
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
||||
|
||||
@@ -148,21 +147,23 @@ void main(void)
|
||||
if(pinExists && !shouldLoadConfigMenu)
|
||||
{
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
chrono(2);
|
||||
wait(2000ULL);
|
||||
}
|
||||
|
||||
goto boot;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
u32 splashMode = MULTICONFIG(SPLASH);
|
||||
|
||||
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
||||
|
||||
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
||||
chainload an external payload */
|
||||
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
||||
|
||||
if(shouldLoadPayload) loadPayload(pressed);
|
||||
if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START)
|
||||
{
|
||||
payloadMenu();
|
||||
pressed = HID_PAD;
|
||||
}
|
||||
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadPayload(pressed, NULL);
|
||||
|
||||
if(splashMode == 2) loadSplash();
|
||||
|
||||
@@ -192,7 +193,7 @@ void main(void)
|
||||
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||
{
|
||||
FirmwareSource tempNand;
|
||||
switch(pressed & EMUNAND_BUTTONS)
|
||||
switch(pressed & DPAD_BUTTONS)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
tempNand = FIRMWARE_EMUNAND;
|
||||
@@ -214,9 +215,8 @@ void main(void)
|
||||
if(nandType == FIRMWARE_EMUNAND) nandType = tempNand;
|
||||
else firmSource = tempNand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boot:
|
||||
|
||||
//If we need to boot EmuNAND, make sure it exists
|
||||
if(nandType != FIRMWARE_SYSNAND)
|
||||
@@ -231,8 +231,8 @@ void main(void)
|
||||
|
||||
if(!ISFIRMLAUNCH)
|
||||
{
|
||||
configTemp |= (u32)nandType | ((u32)firmSource << 3);
|
||||
writeConfig(needConfig, configTemp);
|
||||
configData.config = (configData.config & 0xFFFFFF00) | ((u32)isNoForceFlagSet << 7) | ((u32)ISA9LH << 6) | ((u32)firmSource << 3) | (u32)nandType;
|
||||
writeConfig(false);
|
||||
}
|
||||
|
||||
if(isSdMode && !mountFs(false, false)) error("Failed to mount CTRNAND.");
|
||||
@@ -240,24 +240,23 @@ void main(void)
|
||||
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
|
||||
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromStorage, isSafeMode);
|
||||
|
||||
u32 devMode = MULTICONFIG(DEVOPTIONS);
|
||||
|
||||
bool doUnitinfoPatch = CONFIG(PATCHUNITINFO), enableExceptionHandlers = CONFIG(ENABLEEXCEPTIONHANDLERS);
|
||||
u32 res;
|
||||
switch(firmType)
|
||||
{
|
||||
case NATIVE_FIRM:
|
||||
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
|
||||
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, doUnitinfoPatch, enableExceptionHandlers);
|
||||
break;
|
||||
case TWL_FIRM:
|
||||
res = patchTwlFirm(firmVersion, devMode);
|
||||
res = patchTwlFirm(firmVersion, doUnitinfoPatch);
|
||||
break;
|
||||
case AGB_FIRM:
|
||||
res = patchAgbFirm(devMode);
|
||||
res = patchAgbFirm(doUnitinfoPatch);
|
||||
break;
|
||||
case SAFE_FIRM:
|
||||
case SYSUPDATER_FIRM:
|
||||
case NATIVE_FIRM1X2X:
|
||||
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(enableExceptionHandlers) : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
448
source/patches.c
448
source/patches.c
@@ -22,10 +22,12 @@
|
||||
|
||||
/*
|
||||
* Signature patches by an unknown author
|
||||
* Signature patches for old FIRMs by SciresM
|
||||
* firmlaunches patching code originally by delebile
|
||||
* FIRM partition writes patches by delebile
|
||||
* ARM11 modules patching code originally by Subv
|
||||
* Idea for svcBreak patches from yellows8 and others on #3dsdev
|
||||
* TWL_FIRM patches by Steveice10 and others
|
||||
*/
|
||||
|
||||
#include "patches.h"
|
||||
@@ -35,6 +37,30 @@
|
||||
#include "utils.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
static inline void pathChanger(u8 *pos)
|
||||
{
|
||||
const char *pathFile = "path.txt";
|
||||
u8 path[57];
|
||||
|
||||
u32 pathSize = fileRead(path, pathFile, sizeof(path));
|
||||
|
||||
if(pathSize < 6) return;
|
||||
|
||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||
|
||||
if(pathSize < 6 || pathSize > 55 || path[0] != '/' || memcmp(path + pathSize - 4, ".bin", 4) != 0) return;
|
||||
|
||||
u16 finalPath[56];
|
||||
for(u32 i = 0; i < pathSize; i++)
|
||||
finalPath[i] = (u16)path[i];
|
||||
|
||||
finalPath[pathSize] = 0;
|
||||
|
||||
u8 *posPath = memsearch(pos, u"sd", reboot_bin_size, 4) + 0xA;
|
||||
memcpy(posPath, finalPath, (pathSize + 1) * 2);
|
||||
}
|
||||
|
||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
{
|
||||
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
||||
@@ -49,34 +75,29 @@ u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200;
|
||||
}
|
||||
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11DAbtHandler, u32 **arm11ExceptionsPage)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
||||
bool res = true;
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5},
|
||||
pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||
|
||||
if(*arm11ExceptionsPage == NULL || *freeK11Space == NULL) error("Failed to get Kernel11 data.");
|
||||
|
||||
u32 *arm11SvcTable;
|
||||
|
||||
if(*arm11ExceptionsPage == NULL) res = false;
|
||||
else
|
||||
{
|
||||
*arm11ExceptionsPage -= 0xB;
|
||||
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 dabtOffset = (-(((*arm11ExceptionsPage)[4] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||
}
|
||||
|
||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||
|
||||
if(*freeK11Space == NULL) res = false;
|
||||
else (*freeK11Space)++;
|
||||
|
||||
if(!res) error("Failed to get Kernel11 data.");
|
||||
pointedInstructionVA = 0xFFFF0010 - dabtOffset;
|
||||
*arm11DAbtHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA);
|
||||
(*freeK11Space)++;
|
||||
|
||||
return arm11SvcTable;
|
||||
}
|
||||
@@ -86,36 +107,46 @@ u32 patchSignatureChecks(u8 *pos, u32 size)
|
||||
//Look for signature checks
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||
|
||||
if(off == NULL || temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
u16 *off2 = (u16 *)(temp - 1);
|
||||
if(off == NULL || temp == NULL) return 1;
|
||||
|
||||
u16 *off2 = (u16 *)(temp - 1);
|
||||
*off = off2[0] = 0x2000;
|
||||
off2[1] = 0x4770;
|
||||
|
||||
ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
u32 patchOldSignatureChecks(u8 *pos, u32 size)
|
||||
{
|
||||
// Look for signature checks
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0xBD, 0xE7},
|
||||
pattern2[] = {0xB5, 0x23, 0x4E, 0x0C};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||
|
||||
if(off == NULL || temp == NULL) return 1;
|
||||
|
||||
u16 *off2 = (u16 *)(temp - 1);
|
||||
*off = off2[0] = 0x2000;
|
||||
off2[1] = 0x4770;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
{
|
||||
//Look for firmlaunch code
|
||||
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off -= 0x13;
|
||||
|
||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||
@@ -128,165 +159,105 @@ u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
||||
*pos_fopen = fOpenOffset;
|
||||
|
||||
ret = 0;
|
||||
if(CONFIG(USECUSTOMPATH)) pathChanger(off);
|
||||
|
||||
if(CONFIG(USECUSTOMPATH))
|
||||
{
|
||||
const char *pathFile = "path.txt";
|
||||
|
||||
u32 pathSize = getFileSize(pathFile);
|
||||
|
||||
if(pathSize > 5 && pathSize < 58)
|
||||
{
|
||||
u8 path[pathSize];
|
||||
fileRead(path, pathFile, pathSize);
|
||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||
|
||||
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
||||
{
|
||||
u16 finalPath[pathSize];
|
||||
for(u32 i = 0; i < pathSize; i++)
|
||||
finalPath[i] = (u16)path[i];
|
||||
|
||||
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
||||
memcpy(pos_path, finalPath, pathSize * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
//Look for FIRM writing code
|
||||
u8 *off = memsearch(pos, "exe:", size, 4);
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||
|
||||
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
|
||||
|
||||
if(off2 == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off2 == NULL) return 1;
|
||||
|
||||
off2[0] = 0x2000;
|
||||
off2[1] = 0x46C0;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchOldFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for FIRM writing code
|
||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[0] = 0x2400;
|
||||
off[1] = 0xE01D;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||
{
|
||||
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||
|
||||
off++;
|
||||
|
||||
memset32(off, 0, 8); //Zero out the first TitleID in the list
|
||||
//Zero out the first TitleID in the list
|
||||
memset32(off, 0, 8);
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x28, 0x2A, 0xD0, 0x08};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
u16 *off = (u16 *)(temp - 1);
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u16 *off = (u16 *)(temp - 1);
|
||||
*off = 0x2001; //mov r0, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x07, 0xD1, 0x28, 0x7A};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
off--;
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off--;
|
||||
*off = 0x2001; //mov r0, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchCheckForDevCommonKey(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x03, 0x7C, 0x28, 0x00};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
*off = 0x2301; //mov r3, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||
{
|
||||
u32 ret = 0;
|
||||
if(arm11SvcTable[0x7B] != 0) return 0;
|
||||
|
||||
//Official implementation of svcBackdoor
|
||||
const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||
@@ -300,28 +271,31 @@ u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **free
|
||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||
|
||||
if(!arm11SvcTable[0x7B])
|
||||
{
|
||||
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) ret = 1;
|
||||
else
|
||||
{
|
||||
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) return 1;
|
||||
|
||||
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
|
||||
|
||||
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
||||
*freeK11Space += sizeof(svcBackdoor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
u32 stubSvcRestrictGpuDma(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA)
|
||||
{
|
||||
if(arm11SvcTable[0x59] != 0)
|
||||
{
|
||||
u32 *off = (u32 *)(pos + arm11SvcTable[0x59] - baseK11VA);
|
||||
off[1] = 0xE1A00000; //replace call to inner function by a NOP
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode)
|
||||
{
|
||||
u32 ret;
|
||||
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) return 1;
|
||||
|
||||
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) ret = 1;
|
||||
else
|
||||
{
|
||||
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
|
||||
|
||||
struct CfwInfo
|
||||
@@ -355,37 +329,30 @@ u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **free
|
||||
else isRelease = rev[4] == 0;
|
||||
|
||||
if(isRelease) info->flags = 1;
|
||||
|
||||
if(ISN3DS) info->flags |= 1 << 4;
|
||||
|
||||
if(isSafeMode) info->flags |= 1 << 5;
|
||||
|
||||
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
||||
*freeK11Space += svcGetCFWInfo_bin_size;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u32 *off = (u32 *)(temp - 0xA);
|
||||
|
||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||
{
|
||||
//Discard everything that's not str rX, [r0, #imm](!)
|
||||
if((*off & 0xFE5F0000) == 0xE4000000)
|
||||
{
|
||||
if((*off & 0xFE5F0000) != 0xE4000000) continue;
|
||||
|
||||
u32 rD = (*off >> 12) & 0xF,
|
||||
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
||||
bool writeback = ((*off >> 21) & 1) != 0,
|
||||
@@ -398,39 +365,24 @@ u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||
if(!pre) addr += offset;
|
||||
if(writeback) r0 = addr;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
||||
{
|
||||
const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
|
||||
pattern2[] = {0xE8, 0x13, 0x00, 0x02}; //Call exception dispatcher
|
||||
bool ret = true;
|
||||
|
||||
u32 *loadCodeSet = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(loadCodeSet == NULL) ret = false;
|
||||
else
|
||||
{
|
||||
loadCodeSet -= 2;
|
||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||
}
|
||||
|
||||
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||
|
||||
u32 stackAddress;
|
||||
if(loadCodeSet == NULL || temp == NULL) error("Failed to get ARM11 exception handlers data.");
|
||||
|
||||
if(temp == NULL) ret = false;
|
||||
else stackAddress = *(u32 *)(temp + 9);
|
||||
loadCodeSet -= 2;
|
||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||
|
||||
if(!ret) error("Failed to get ARM11 exception handlers data.");
|
||||
|
||||
return stackAddress;
|
||||
return *(u32 *)(temp + 9);
|
||||
}
|
||||
|
||||
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||
@@ -439,125 +391,94 @@ u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||
|
||||
//Look for the svc handler
|
||||
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
||||
u32 ret;
|
||||
|
||||
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(arm9SvcTable == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(arm9SvcTable == NULL) return 1;
|
||||
|
||||
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||
*addr = 0xE12FFF7F;
|
||||
|
||||
ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA)
|
||||
{
|
||||
//Same as above, for NATIVE_FIRM ARM11
|
||||
u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000);
|
||||
u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - baseK11VA);
|
||||
*addr = 0xE12FFF7F;
|
||||
}
|
||||
|
||||
u32 patchKernel9Panic(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u32 *off = (u32 *)(temp - 0x12);
|
||||
*off = 0xE12FFF7E;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchKernel11Panic(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
||||
u32 ret;
|
||||
|
||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
*off = 0xE12FFF7E;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchP9AccessChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
u16 *off = (u16 *)(temp - 3);
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u16 *off = (u16 *)(temp - 3);
|
||||
off[0] = 0x2001; //mov r0, #1
|
||||
off[1] = 0x4770; //bx lr
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
|
||||
|
||||
if(arm11SvcHandler == endPos) ret = 1;
|
||||
else
|
||||
{
|
||||
if(arm11SvcHandler == endPos) return 1;
|
||||
|
||||
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
||||
|
||||
ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space, bool patchGames)
|
||||
{
|
||||
/* We have to detour a function in the ARM11 kernel because builtin modules
|
||||
are compressed in memory and are only decompressed at runtime */
|
||||
|
||||
u32 ret;
|
||||
|
||||
//Check that we have enough free space
|
||||
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) ret = 0;
|
||||
else
|
||||
{
|
||||
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) return patchGames ? 1 : 0;
|
||||
|
||||
//Look for the code that decompresses the .code section of the builtin modules
|
||||
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
//Inject our code into the free space
|
||||
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
||||
|
||||
@@ -568,190 +489,139 @@ u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||
|
||||
*freeK11Space += k11modules_bin_size;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for UNITINFO value being set during kernel sync
|
||||
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[0] = ISDEVUNIT ? 0 : 1;
|
||||
off[3] = 0xE3;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchLgySignatureChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
u16 *off = (u16 *)(temp + 1);
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u16 *off = (u16 *)(temp + 1);
|
||||
off[0] = 0x2000;
|
||||
off[1] = 0xB04E;
|
||||
off[2] = 0xBD70;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
u16 *off = (u16 *)(temp - 1);
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u16 *off = (u16 *)(temp - 1);
|
||||
*off = 0x2001; //mov r0, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[1] = 0x2000;
|
||||
off[2] = 0;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[2] = 0x2000;
|
||||
off[3] = 0;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||
{
|
||||
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
|
||||
u32 ret;
|
||||
|
||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(temp == NULL)
|
||||
{
|
||||
if(firmVersion == 0xFFFFFFFF) ret = patchOldTwlFlashcartChecks(pos, size);
|
||||
else ret = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 *off = (u16 *)(temp + 3);
|
||||
if(firmVersion == 0xFFFFFFFF) return patchOldTwlFlashcartChecks(pos, size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
u16 *off = (u16 *)(temp + 3);
|
||||
off[0] = off[6] = off[0xC] = 0x2001; //mov r0, #1
|
||||
off[1] = off[7] = off[0xD] = 0; //nop
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[0] = off[6] = 0x2001; //mov r0, #1
|
||||
off[1] = off[7] = 0; //nop
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[0] = 0x2001; //mov r0, #1
|
||||
off[1] = 0x4770;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 patchAgbBootSplash(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
if(off == NULL) return 1;
|
||||
|
||||
off[2] = 0x26;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
@@ -22,10 +22,12 @@
|
||||
|
||||
/*
|
||||
* Signature patches by an unknown author
|
||||
* Signature patches for old FIRMs by SciresM
|
||||
* firmlaunches patching code originally by delebile
|
||||
* FIRM partition writes patches by delebile
|
||||
* ARM11 modules patching code originally by Subv
|
||||
* Idea for svcBreak patches from yellows8 and others on #3dsdev
|
||||
* TWL_FIRM patches by Steveice10 and others
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -35,8 +37,9 @@
|
||||
extern CfgData configData;
|
||||
|
||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11DAbtHandler, u32 **arm11ExceptionsPage);
|
||||
u32 patchSignatureChecks(u8 *pos, u32 size);
|
||||
u32 patchOldSignatureChecks(u8 *pos, u32 size);
|
||||
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||
u32 patchFirmWrites(u8 *pos, u32 size);
|
||||
u32 patchOldFirmWrites(u8 *pos, u32 size);
|
||||
@@ -45,16 +48,17 @@ u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size);
|
||||
u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size);
|
||||
u32 patchCheckForDevCommonKey(u8 *pos, u32 size);
|
||||
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||
u32 stubSvcRestrictGpuDma(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA);
|
||||
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
|
||||
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset);
|
||||
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA);
|
||||
u32 patchKernel9Panic(u8 *pos, u32 size);
|
||||
u32 patchKernel11Panic(u8 *pos, u32 size);
|
||||
u32 patchP9AccessChecks(u8 *pos, u32 size);
|
||||
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos);
|
||||
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
|
||||
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space, bool patchGames);
|
||||
u32 patchUnitInfoValueSet(u8 *pos, u32 size);
|
||||
u32 patchLgySignatureChecks(u8 *pos, u32 size);
|
||||
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size);
|
||||
|
||||
104
source/pin.c
104
source/pin.c
@@ -46,27 +46,37 @@ static char pinKeyToLetter(u32 pressed)
|
||||
|
||||
void newPin(bool allowSkipping, u32 pinMode)
|
||||
{
|
||||
clearScreens(true, true, false);
|
||||
clearScreens(false);
|
||||
|
||||
u8 length = 4 + 2 * (pinMode - 1);
|
||||
|
||||
const char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
|
||||
drawString(title, true, 10, 10, COLOR_TITLE);
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawString("Enter a new PIN using ABXY and the DPad", true, 10, 10, COLOR_TITLE);
|
||||
drawString(allowSkipping ? "Press START to skip, SELECT to reset" : "Press SELECT to reset", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
||||
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
bool reset = false;
|
||||
u8 cnt = 0;
|
||||
u32 charDrawPos = 16 * SPACING_X;
|
||||
|
||||
while(cnt < length)
|
||||
{
|
||||
if(reset)
|
||||
{
|
||||
for(u32 i = 0; i < cnt; i++)
|
||||
drawCharacter((char)enteredPassword[i], true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK);
|
||||
|
||||
cnt = 0;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
pressed = waitInput(false);
|
||||
}
|
||||
while(!(pressed & PIN_BUTTONS));
|
||||
|
||||
@@ -74,14 +84,22 @@ void newPin(bool allowSkipping, u32 pinMode)
|
||||
if(!allowSkipping) pressed &= ~BUTTON_START;
|
||||
|
||||
if(pressed & BUTTON_START) return;
|
||||
|
||||
if(pressed & BUTTON_SELECT)
|
||||
{
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!pressed) continue;
|
||||
|
||||
char key = pinKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; //Add character to password
|
||||
//Add character to password
|
||||
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed);
|
||||
|
||||
//Visualize character on screen
|
||||
drawCharacter(key, true, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
charDrawPos += 2 * SPACING_X;
|
||||
drawCharacter(enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
PinData pin;
|
||||
@@ -125,38 +143,45 @@ bool verifyPin(u32 pinMode)
|
||||
|
||||
initScreens();
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
drawString("Enter the PIN using ABXY and the DPad to proceed", true, 10, 10, COLOR_TITLE);
|
||||
drawString("Press START to shutdown, SELECT to clear", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
||||
|
||||
bool unlock = false;
|
||||
u8 cnt = 0;
|
||||
u32 charDrawPos = 16 * SPACING_X;
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + lengthBlock[0], true, 10 + 5 * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
const char *messageFile = "pinmessage.txt";
|
||||
char message[801];
|
||||
|
||||
u32 messageSize = getFileSize(messageFile);
|
||||
u32 messageSize = fileRead(message, messageFile, sizeof(message) - 1);
|
||||
|
||||
if(messageSize > 0 && messageSize <= 800)
|
||||
{
|
||||
char message[messageSize + 1];
|
||||
|
||||
if(fileRead(message, messageFile, messageSize) == messageSize)
|
||||
if(messageSize != 0)
|
||||
{
|
||||
message[messageSize] = 0;
|
||||
drawString(message, false, 10, 10, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
bool unlock = false,
|
||||
reset = false;
|
||||
u8 cnt = 0;
|
||||
|
||||
while(!unlock)
|
||||
{
|
||||
drawString("Press START to shutdown or enter PIN to proceed", true, 10, 10, COLOR_TITLE);
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + lengthBlock[0], true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
if(reset)
|
||||
{
|
||||
for(u32 i = 0; i < cnt; i++)
|
||||
drawCharacter('*', true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK);
|
||||
|
||||
cnt = 0;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
pressed = waitInput(false);
|
||||
}
|
||||
while(!(pressed & PIN_BUTTONS));
|
||||
|
||||
@@ -164,29 +189,30 @@ bool verifyPin(u32 pinMode)
|
||||
|
||||
pressed &= PIN_BUTTONS;
|
||||
|
||||
if(pressed & BUTTON_SELECT)
|
||||
{
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!pressed) continue;
|
||||
|
||||
char key = pinKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; //Add character to password
|
||||
//Add character to password
|
||||
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed);
|
||||
|
||||
//Visualize character on screen
|
||||
drawCharacter(key, true, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
charDrawPos += 2 * SPACING_X;
|
||||
drawCharacter('*', true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
if(++cnt < lengthBlock[0]) continue;
|
||||
|
||||
if(cnt >= lengthBlock[0])
|
||||
{
|
||||
computePinHash(tmp, enteredPassword);
|
||||
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
|
||||
|
||||
if(!unlock)
|
||||
{
|
||||
charDrawPos = 16 * SPACING_X;
|
||||
cnt = 0;
|
||||
reset = true;
|
||||
|
||||
clearScreens(true, false, false);
|
||||
|
||||
drawString("Wrong PIN, try again", true, 10, 10 + 4 * SPACING_Y, COLOR_RED);
|
||||
}
|
||||
drawString("Wrong PIN, try again", true, 10, 10 + 5 * SPACING_Y, COLOR_RED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,31 +40,30 @@
|
||||
#include "memory.h"
|
||||
#include "cache.h"
|
||||
#include "i2c.h"
|
||||
#include "utils.h"
|
||||
|
||||
vu32 *arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY;
|
||||
static const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26};
|
||||
|
||||
void __attribute__((naked)) arm11Stub(void)
|
||||
{
|
||||
//Wait for the entry to be set
|
||||
while(*arm11Entry == ARM11_STUB_ADDRESS);
|
||||
|
||||
//Jump to it
|
||||
((void (*)())*arm11Entry)();
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
static void invokeArm11Function(void (*func)())
|
||||
{
|
||||
static bool hasCopiedStub = false;
|
||||
|
||||
if(!hasCopiedStub)
|
||||
{
|
||||
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30);
|
||||
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x2C);
|
||||
hasCopiedStub = true;
|
||||
}
|
||||
|
||||
*arm11Entry = (u32)func;
|
||||
while(*arm11Entry);
|
||||
*arm11Entry = ARM11_STUB_ADDRESS;
|
||||
while(*arm11Entry);
|
||||
}
|
||||
|
||||
void deinitScreens(void)
|
||||
@@ -82,7 +81,7 @@ void deinitScreens(void)
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
if(ARESCREENSINITED) invokeArm11Function(ARM11);
|
||||
if(ARESCREENSINITIALIZED) invokeArm11Function(ARM11);
|
||||
}
|
||||
|
||||
void updateBrightness(u32 brightnessIndex)
|
||||
@@ -126,14 +125,10 @@ void swapFramebuffers(bool isAlternate)
|
||||
invokeArm11Function(ARM11);
|
||||
}
|
||||
|
||||
void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate)
|
||||
void clearScreens(bool isAlternate)
|
||||
{
|
||||
static bool clearTopTmp,
|
||||
clearBottomTmp;
|
||||
static volatile struct fb *fbTmp;
|
||||
clearTopTmp = clearTop;
|
||||
clearBottomTmp = clearBottom;
|
||||
fbTmp = clearAlternate ? &fbs[1] : &fbs[0];
|
||||
fbTmp = isAlternate ? &fbs[1] : &fbs[0];
|
||||
|
||||
void __attribute__((naked)) ARM11(void)
|
||||
{
|
||||
@@ -145,29 +140,21 @@ void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate)
|
||||
vu32 *REGs_PSC0 = (vu32 *)0x10400010,
|
||||
*REGs_PSC1 = (vu32 *)0x10400020;
|
||||
|
||||
if(clearTopTmp)
|
||||
{
|
||||
REGs_PSC0[0] = (u32)fbTmp->top_left >> 3; //Start address
|
||||
REGs_PSC0[1] = (u32)(fbTmp->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address
|
||||
REGs_PSC0[2] = 0; //Fill value
|
||||
REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start
|
||||
}
|
||||
|
||||
if(clearBottomTmp)
|
||||
{
|
||||
REGs_PSC1[0] = (u32)fbTmp->bottom >> 3; //Start address
|
||||
REGs_PSC1[1] = (u32)(fbTmp->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address
|
||||
REGs_PSC1[2] = 0; //Fill value
|
||||
REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start
|
||||
}
|
||||
|
||||
while(!((!clearTopTmp || (REGs_PSC0[3] & 2)) && (!clearBottomTmp || (REGs_PSC1[3] & 2))));
|
||||
while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2)));
|
||||
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
flushDCacheRange(&clearTopTmp, 1);
|
||||
flushDCacheRange(&clearBottomTmp, 1);
|
||||
flushDCacheRange((void *)fbTmp, sizeof(struct fb));
|
||||
flushDCacheRange(&fbTmp, 4);
|
||||
invokeArm11Function(ARM11);
|
||||
@@ -288,13 +275,14 @@ void initScreens(void)
|
||||
|
||||
if(needToSetup)
|
||||
{
|
||||
if(!ARESCREENSINITED)
|
||||
if(!ARESCREENSINITIALIZED)
|
||||
{
|
||||
flushDCacheRange(&configData, sizeof(CfgData));
|
||||
invokeArm11Function(initSequence);
|
||||
|
||||
//Turn on backlight
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
||||
wait(3ULL);
|
||||
}
|
||||
else updateBrightness(MULTICONFIG(BRIGHTNESS));
|
||||
|
||||
@@ -303,6 +291,6 @@ void initScreens(void)
|
||||
needToSetup = false;
|
||||
}
|
||||
|
||||
clearScreens(true, true, false);
|
||||
clearScreens(false);
|
||||
swapFramebuffers(false);
|
||||
}
|
||||
@@ -31,9 +31,9 @@
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
#define ARESCREENSINITED (PDN_GPU_CNT != 1)
|
||||
#define ARESCREENSINITIALIZED (PDN_GPU_CNT != 1)
|
||||
|
||||
#define ARM11_STUB_ADDRESS 0x1FFFFFC8
|
||||
#define ARM11_STUB_ADDRESS 0x1FFFFF00
|
||||
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
|
||||
#define SCREEN_TOP_WIDTH 400
|
||||
@@ -53,5 +53,5 @@ extern CfgData configData;
|
||||
void deinitScreens(void);
|
||||
void swapFramebuffers(bool isAlternate);
|
||||
void updateBrightness(u32 brightnessIndex);
|
||||
void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate);
|
||||
void clearScreens(bool isAlternate);
|
||||
void initScreens(void);
|
||||
@@ -91,4 +91,11 @@ start:
|
||||
mov r1, #0x340
|
||||
str r1, [r0]
|
||||
|
||||
@ Clear BSS
|
||||
ldr r0, =__bss_start
|
||||
mov r1, #0
|
||||
ldr r2, =__bss_end
|
||||
sub r2, r0
|
||||
bl memset32
|
||||
|
||||
b main
|
||||
|
||||
@@ -40,14 +40,19 @@ typedef volatile u64 vu64;
|
||||
|
||||
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
||||
|
||||
#define CFG_SYSPROT9 (*(vu8 *)0x10000000)
|
||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||
#define CFG_TWLUNITINFO (*(vu8 *)0x10010014)
|
||||
#define OTP_DEVCONSOLEID (*(vu64 *)0x10012000)
|
||||
#define OTP_TWLCONSOLEID (*(vu64 *)0x10012100)
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
#define ISN3DS (PDN_MPCORE_CFG == 7)
|
||||
#define ISDEVUNIT (CFG_UNITINFO != 0)
|
||||
#define ISA9LH (!PDN_SPI_CNT)
|
||||
#define ISSIGHAX (!(CFG_SYSPROT9 & 2))
|
||||
#define ISFIRMLAUNCH (launchedFirmTidLow[5] != 0)
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* waitInput function based on code by d0k3 https://github.com/d0k3/Decrypt9WIP/blob/master/source/hid.c
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "i2c.h"
|
||||
#include "buttons.h"
|
||||
@@ -27,36 +31,65 @@
|
||||
#include "draw.h"
|
||||
#include "cache.h"
|
||||
|
||||
u32 waitInput(void)
|
||||
static void startChrono(void)
|
||||
{
|
||||
bool pressedKey = false;
|
||||
u32 key;
|
||||
REG_TIMER_CNT(0) = 0; //67MHz
|
||||
for(u32 i = 1; i < 4; i++) REG_TIMER_CNT(i) = 4; //Count-up
|
||||
|
||||
//Wait for no keys to be pressed
|
||||
while(HID_PAD);
|
||||
for(u32 i = 0; i < 4; i++) REG_TIMER_VAL(i) = 0;
|
||||
|
||||
do
|
||||
REG_TIMER_CNT(0) = 0x80; //67MHz; enabled
|
||||
for(u32 i = 1; i < 4; i++) REG_TIMER_CNT(i) = 0x84; //Count-up; enabled
|
||||
}
|
||||
|
||||
static u64 chrono(void)
|
||||
{
|
||||
//Wait for a key to be pressed
|
||||
while(!HID_PAD);
|
||||
u64 res = 0;
|
||||
for(u32 i = 0; i < 4; i++) res |= REG_TIMER_VAL(i) << (16 * i);
|
||||
|
||||
res /= (TICKS_PER_SEC / 1000);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
u32 waitInput(bool isMenu)
|
||||
{
|
||||
static u64 dPadDelay = 0ULL;
|
||||
u32 key,
|
||||
oldKey = HID_PAD;
|
||||
|
||||
if(isMenu)
|
||||
{
|
||||
dPadDelay = dPadDelay > 0ULL ? 87ULL : 143ULL;
|
||||
startChrono();
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
key = HID_PAD;
|
||||
|
||||
//Make sure it's pressed
|
||||
for(u32 i = 0x13000; i > 0; i--)
|
||||
if(!key)
|
||||
{
|
||||
if(key != HID_PAD) break;
|
||||
if(i == 1) pressedKey = true;
|
||||
if(i2cReadRegister(I2C_DEV_MCU, 0x10) == 1) mcuPowerOff();
|
||||
oldKey = 0;
|
||||
dPadDelay = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(key == oldKey && (!isMenu || (!(key & DPAD_BUTTONS) || chrono() < dPadDelay))) continue;
|
||||
|
||||
//Make sure the key is pressed
|
||||
u32 i;
|
||||
for(i = 0; i < 0x13000 && key == HID_PAD; i++);
|
||||
if(i == 0x13000) break;
|
||||
}
|
||||
while(!pressedKey);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void mcuPowerOff(void)
|
||||
{
|
||||
if(!ISFIRMLAUNCH && ARESCREENSINITED) clearScreens(true, true, false);
|
||||
if(!ISFIRMLAUNCH && ARESCREENSINITIALIZED) clearScreens(false);
|
||||
|
||||
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
flushEntireDCache();
|
||||
@@ -65,38 +98,10 @@ void mcuPowerOff(void)
|
||||
while(true);
|
||||
}
|
||||
|
||||
static inline void startChrono(u64 initialTicks)
|
||||
void wait(u64 amount)
|
||||
{
|
||||
REG_TIMER_CNT(0) = 0; //67MHz
|
||||
for(u32 i = 1; i < 4; i++) REG_TIMER_CNT(i) = 4; //Count-up
|
||||
|
||||
for(u32 i = 0; i < 4; i++) REG_TIMER_VAL(i) = (u16)(initialTicks >> (16 * i));
|
||||
|
||||
REG_TIMER_CNT(0) = 0x80; //67MHz; enabled
|
||||
for(u32 i = 1; i < 4; i++) REG_TIMER_CNT(i) = 0x84; //Count-up; enabled
|
||||
}
|
||||
|
||||
static inline void stopChrono(void)
|
||||
{
|
||||
for(u32 i = 0; i < 4; i++) REG_TIMER_CNT(i) &= ~0x80;
|
||||
}
|
||||
|
||||
void chrono(u32 seconds)
|
||||
{
|
||||
startChrono(0);
|
||||
|
||||
u64 startingTicks = 0;
|
||||
for(u32 i = 0; i < 4; i++) startingTicks |= REG_TIMER_VAL(i) << (16 * i);
|
||||
|
||||
u64 res;
|
||||
do
|
||||
{
|
||||
res = 0;
|
||||
for(u32 i = 0; i < 4; i++) res |= REG_TIMER_VAL(i) << (16 * i);
|
||||
}
|
||||
while(res - startingTicks < seconds * TICKS_PER_SEC);
|
||||
|
||||
stopChrono();
|
||||
startChrono();
|
||||
while(chrono() < amount);
|
||||
}
|
||||
|
||||
void error(const char *message)
|
||||
@@ -109,7 +114,7 @@ void error(const char *message)
|
||||
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
waitInput();
|
||||
waitInput(false);
|
||||
}
|
||||
|
||||
mcuPowerOff();
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* waitInput function based on code by d0k3 https://github.com/d0k3/Decrypt9WIP/blob/master/source/hid.c
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
@@ -28,7 +32,7 @@
|
||||
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
||||
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
||||
|
||||
u32 waitInput(void);
|
||||
u32 waitInput(bool isMenu);
|
||||
void mcuPowerOff(void);
|
||||
void chrono(u32 seconds);
|
||||
void wait(u64 amount);
|
||||
void error(const char *message);
|
||||
Reference in New Issue
Block a user