Compare commits
182 Commits
v8.1
...
v9.0-joyco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1182d3a627 | ||
|
|
938cb6492f | ||
|
|
fed62855cb | ||
|
|
4f54596658 | ||
|
|
0b41ed04d5 | ||
|
|
9509a86998 | ||
|
|
6d4d80a798 | ||
|
|
76d274cfe2 | ||
|
|
ccf13be964 | ||
|
|
e36b27ccf0 | ||
|
|
34c80ad476 | ||
|
|
97ae106d8e | ||
|
|
7cb74b74d7 | ||
|
|
337205eb08 | ||
|
|
f36977017b | ||
|
|
e40b547bb6 | ||
|
|
1fd689f5da | ||
|
|
c5c8dca14c | ||
|
|
11f820efa7 | ||
|
|
9074688491 | ||
|
|
1de27c54f1 | ||
|
|
3e67e64faa | ||
|
|
553f8d2533 | ||
|
|
ec7ae35da1 | ||
|
|
7e8da0d236 | ||
|
|
b3e6561072 | ||
|
|
41f32ed983 | ||
|
|
bbadf840ef | ||
|
|
acc50aae46 | ||
|
|
6a68a77973 | ||
|
|
0f4d66dd61 | ||
|
|
d28642d2c3 | ||
|
|
ca4685cc42 | ||
|
|
522f10582d | ||
|
|
239d113177 | ||
|
|
fd80294bf2 | ||
|
|
b379d83469 | ||
|
|
27f352fdf1 | ||
|
|
bfec874a7c | ||
|
|
93561003e8 | ||
|
|
1572bfd989 | ||
|
|
cb945612a3 | ||
|
|
1b440f7f3b | ||
|
|
78791f7b66 | ||
|
|
5def0c18e2 | ||
|
|
0e67b0f026 | ||
|
|
8052946517 | ||
|
|
b6d6cc9750 | ||
|
|
2a840f2c79 | ||
|
|
4eaf791849 | ||
|
|
be0f50b19c | ||
|
|
f30b7b9fb3 | ||
|
|
6a0f332e3c | ||
|
|
16530d3a52 | ||
|
|
20af9c6750 | ||
|
|
fee9f6b427 | ||
|
|
57d03d6333 | ||
|
|
0075fe2aa0 | ||
|
|
d54417ac0a | ||
|
|
648801d432 | ||
|
|
2be2826b0b | ||
|
|
37a9fa1bf4 | ||
|
|
65af93c8ce | ||
|
|
8353b84944 | ||
|
|
d6a89db495 | ||
|
|
f156aa8cdb | ||
|
|
f9adbcc9d9 | ||
|
|
cf8696ac70 | ||
|
|
9c5766f649 | ||
|
|
c9701f93b9 | ||
|
|
56e54cd110 | ||
|
|
55836b48af | ||
|
|
09bfdb9ee1 | ||
|
|
1c2e8dec11 | ||
|
|
506b16db37 | ||
|
|
8f03234e58 | ||
|
|
44b5e10323 | ||
|
|
48303604b0 | ||
|
|
ab2ddbc2ee | ||
|
|
9495bf30bf | ||
|
|
a4899a1bec | ||
|
|
882c6cf0d4 | ||
|
|
a5e18c82d1 | ||
|
|
22a8661fe1 | ||
|
|
8258a98647 | ||
|
|
76dde0e6db | ||
|
|
1a39cb27e4 | ||
|
|
95d2d0a6bd | ||
|
|
98d4345858 | ||
|
|
1e4431dcc9 | ||
|
|
348b175994 | ||
|
|
76f057dafb | ||
|
|
188400c5a9 | ||
|
|
a74d9c6d3e | ||
|
|
f8bcfb2f58 | ||
|
|
d63fc2bc82 | ||
|
|
8e31784996 | ||
|
|
f27cdb4543 | ||
|
|
a39adc8ac1 | ||
|
|
97bef66018 | ||
|
|
bb5518b0f6 | ||
|
|
ac73a96ce2 | ||
|
|
7075004e58 | ||
|
|
ddf8ba5116 | ||
|
|
f46773ba64 | ||
|
|
f67d333457 | ||
|
|
4116c1e00f | ||
|
|
bc1aa15dd7 | ||
|
|
ecd27f7eaa | ||
|
|
618ce671ac | ||
|
|
2a6a655804 | ||
|
|
33436ae2a6 | ||
|
|
9f2b66ac51 | ||
|
|
df93e4797e | ||
|
|
538d1dec77 | ||
|
|
2492c8273a | ||
|
|
d358df48a6 | ||
|
|
f619dafff1 | ||
|
|
0419fc4e30 | ||
|
|
13317b9548 | ||
|
|
18db70a669 | ||
|
|
f79923814d | ||
|
|
c92de03a5f | ||
|
|
3f356da879 | ||
|
|
9efac01c86 | ||
|
|
ca2622af7c | ||
|
|
aa8e0bda8c | ||
|
|
c79f11ee99 | ||
|
|
04d0770b90 | ||
|
|
fbf8a1b6d9 | ||
|
|
a3cb6a622d | ||
|
|
0790a3ceb3 | ||
|
|
4fa12f90fc | ||
|
|
d5d56e7634 | ||
|
|
a964089df4 | ||
|
|
5fd5b4da89 | ||
|
|
322a7050aa | ||
|
|
b7f4ac02c8 | ||
|
|
009f61a8b6 | ||
|
|
26d0cafb39 | ||
|
|
bd1d4a77fe | ||
|
|
f235bc83a8 | ||
|
|
9c6b540905 | ||
|
|
d2e911a58e | ||
|
|
9344a7b434 | ||
|
|
cd194fa5b2 | ||
|
|
49c0ab65df | ||
|
|
8f9c1305e4 | ||
|
|
d4193ec11d | ||
|
|
6b9b0472bc | ||
|
|
7e74258363 | ||
|
|
f6483ec602 | ||
|
|
36c1da1d61 | ||
|
|
6bcb1f8679 | ||
|
|
62932a9639 | ||
|
|
a4629e4b65 | ||
|
|
a0c2b43b34 | ||
|
|
3907c46980 | ||
|
|
7e7ab124a3 | ||
|
|
cfc6cf24bf | ||
|
|
46e9cb6b23 | ||
|
|
ba14efe1f4 | ||
|
|
3d8f62d38f | ||
|
|
3edaf0af64 | ||
|
|
9273a88db7 | ||
|
|
37ba2c15de | ||
|
|
557f2057f7 | ||
|
|
6b5cc93780 | ||
|
|
9760191af8 | ||
|
|
8845e4dd20 | ||
|
|
8cf823f548 | ||
|
|
2538769f3a | ||
|
|
89fca38807 | ||
|
|
dcc0eed69c | ||
|
|
817475257e | ||
|
|
f2861058ba | ||
|
|
5d2a7315d5 | ||
|
|
1520ab7555 | ||
|
|
d4d0fbd73b | ||
|
|
ddb8e98e95 | ||
|
|
fd69b4169f | ||
|
|
b48e0b5c5b |
18
.github/ISSUE_TEMPLATE.md
vendored
18
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,11 +1,13 @@
|
||||
<!--
|
||||
-- THIS IS NOT A SUPPORT FORUM! For support go here:
|
||||
-- Nintendo Hacking: https://discord.gg/MjzatM8y
|
||||
-- Nintendo Homebrew: https://discord.gg/MjzatM8
|
||||
--
|
||||
-- Rosalina feature requests go here: https://github.com/AuroraWright/Luma3DS/issues/752
|
||||
--
|
||||
-- Also check the Wiki (https://github.com/AuroraWright/Luma3DS/wiki) before making an issue.
|
||||
--
|
||||
-- For GBA/DSiWare/DS/AGB_FIRM/TWL_FIRM problems: https://3ds.guide/troubleshooting
|
||||
-- If you're using an emu/redNAND anything related to that must also be installed to sysNAND.
|
||||
-- If you're using an emu/redNAND try installing anything on it to sysNAND.
|
||||
-- Please make sure to read "Enable game patching" https://github.com/AuroraWright/Luma3DS/wiki/Options-and-usage before posting any issues about the "Enable game patching" option(s).
|
||||
--
|
||||
-- Luma updaters that don't support Boot9Strap/Sighax won't work.
|
||||
@@ -18,7 +20,7 @@
|
||||
|
||||
**SysNAND version (+emu/redNAND version if applicable):**
|
||||
|
||||
[e.g. 11.4.0-37U SysNAND, 11.4.0-37J EmuNAND]
|
||||
[e.g. 11.6.0-39U SysNAND, 11.6.0-39J EmuNAND]
|
||||
|
||||
**Entrypoint (How/what you're using to boot Luma3DS):**
|
||||
|
||||
@@ -26,7 +28,7 @@
|
||||
|
||||
**Luma3DS version:**
|
||||
|
||||
[e.g. 7.1 stable or if using nightly/hourly specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/9570e6cbeca53128433abbf5e3473cb8a07fe69e]
|
||||
[e.g. 8.1.1 stable or if using nightly/hourly specify the commit like this https://github.com/AuroraWright/Luma3DS/commit/9570e6cbeca53128433abbf5e3473cb8a07fe69e]
|
||||
<!--You can check which version you're on in System Settings. It will be on the bottom right of the top screen.-->
|
||||
|
||||
|
||||
@@ -51,9 +53,7 @@ Use EmuNAND FIRM if booting with R: ( )
|
||||
|
||||
Enable loading external FIRMs and modules: ( )
|
||||
<!--Firmware (.bin) files are not required by Luma, or NTR CFW anymore.
|
||||
-- If you're having issues with this option enabled try deleting them from the luma folder on the root of the SD card and disabling this option.-->
|
||||
|
||||
Use custom path: ( )
|
||||
-- If you're having issues with this option enabled try deleting them from the luma folder on the root of the SD card or /rw/luma on CTRNAND and disabling this option.-->
|
||||
|
||||
Enable game patching: ( )
|
||||
|
||||
@@ -65,6 +65,8 @@ Patch ARM9 access: ( )
|
||||
|
||||
Set developer UNITINFO: ( )
|
||||
|
||||
Disable ARM11 exception handlers: ( )
|
||||
|
||||
--
|
||||
|
||||
|
||||
@@ -83,6 +85,6 @@ Set developer UNITINFO: ( )
|
||||
|
||||
|
||||
**Dump file:**
|
||||
<!--If the issue leads to a crash you can generate a crash dump by checking the "Enable exception handlers" option.
|
||||
<!--If the issue leads to a crash you must uncheck the "Disable ARM11 exception handlers" option.
|
||||
-- The error message will tell you where the dump is.
|
||||
-- Zip the dmp file and drag & drop it below.-->
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,3 +14,7 @@ exceptions/arm11/build
|
||||
*.elf
|
||||
*.cxi
|
||||
.DS_Store
|
||||
*.dmp
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
|
||||
28
Makefile
28
Makefile
@@ -10,13 +10,6 @@ endif
|
||||
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
size := stat -f%z
|
||||
else
|
||||
size := stat -c%s
|
||||
endif
|
||||
|
||||
name := Luma3DS
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/')
|
||||
version_major := $(shell git describe --tags --match v[0-9]* | cut -c2- | cut -f1 -d- | cut -f1 -d.)
|
||||
@@ -54,6 +47,8 @@ dir_k11_extension := k11_extension
|
||||
dir_sysmodules := sysmodules
|
||||
dir_loader := $(dir_sysmodules)/loader
|
||||
dir_rosalina := $(dir_sysmodules)/rosalina
|
||||
dir_sm := $(dir_sysmodules)/sm
|
||||
dir_pxi := $(dir_sysmodules)/pxi
|
||||
dir_build := build
|
||||
dir_out := out
|
||||
|
||||
@@ -67,7 +62,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
|
||||
bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/chainloader.bin.o $(dir_build)/arm9_exceptions.bin.o
|
||||
|
||||
modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi
|
||||
modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi $(dir_build)/sm.cxi $(dir_build)/pxi.cxi
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
@@ -90,6 +85,8 @@ clean:
|
||||
@$(MAKE) -C $(dir_k11_extension) clean
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) -C $(dir_rosalina) clean
|
||||
@$(MAKE) -C $(dir_sm) clean
|
||||
@$(MAKE) -C $(dir_pxi) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
|
||||
.PRECIOUS: $(dir_build)/%.bin
|
||||
@@ -100,10 +97,13 @@ clean:
|
||||
.PHONY: $(dir_k11_extension)
|
||||
.PHONY: $(dir_loader)
|
||||
.PHONY: $(dir_rosalina)
|
||||
.PHONY: $(dir_sm)
|
||||
.PHONY: $(dir_pxi)
|
||||
|
||||
|
||||
$(dir_out)/$(name)$(revision).7z: all
|
||||
@mkdir -p "$(@D)"
|
||||
@7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser.py
|
||||
@[ -f "$@" ] || 7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser -xr!.DS_Store
|
||||
|
||||
$(dir_out)/boot.firm: $(dir_build)/modules.bin $(dir_build)/arm11.elf $(dir_build)/main.elf $(dir_build)/k11_extension.bin
|
||||
@mkdir -p "$(@D)"
|
||||
@@ -132,6 +132,14 @@ $(dir_build)/rosalina.cxi: $(dir_rosalina)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/sm.cxi: $(dir_sm)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/pxi.cxi: $(dir_pxi)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
@@ -152,7 +160,7 @@ $(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configura
|
||||
$(dir_build)/patches.o: CFLAGS += -DVERSION_MAJOR="$(version_major)" -DVERSION_MINOR="$(version_minor)"\
|
||||
-DVERSION_BUILD="$(version_build)" -DISRELEASE="$(is_release)" -DCOMMIT_HASH="0x$(commit)"
|
||||
$(dir_build)/firm.o: $(dir_build)/modules.bin
|
||||
$(dir_build)/firm.o: CFLAGS += -DLUMA_SECTION0_SIZE="$(shell $(size) $(dir_build)/modules.bin)"
|
||||
$(dir_build)/firm.o: CFLAGS += -DLUMA_SECTION0_SIZE="$(shell wc -c $(dir_build)/modules.bin | tr -d [:space:][:alpha:][:punct:])"
|
||||
|
||||
$(dir_build)/bundled.h: $(bundled)
|
||||
@$(foreach f, $(bundled),\
|
||||
|
||||
@@ -15,9 +15,9 @@ Since Luma3DS v8.0, Luma3DS has its own in-game menu, triggerable by `L+Down+Sel
|
||||
|
||||
First you need to clone the repository with: `git clone https://github.com/AuroraWright/Luma3DS.git`
|
||||
To compile, you'll need [armips](https://github.com/Kingcom/armips) and a build of a recent commit of [makerom](https://github.com/profi200/Project_CTR) added to your PATH. You'll also need to install [firmtool](https://github.com/TuxSH/firmtool), its README contains installation instructions.
|
||||
For now, you'll also need to update your [libctru](https://github.com/smealum/ctrulib) install, building from the latest commit.
|
||||
For your convenience, here are [Windows](http://www91.zippyshare.com/v/ePGpjk9r/file.html) and [Linux](https://mega.nz/#!uQ1T1IAD!Q91O0e12LXKiaXh_YjXD3D5m8_W3FuMI-hEa6KVMRDQ) builds of armips (thanks to who compiled them!).
|
||||
Finally just run `make` and everything should work!
|
||||
You'll also need to update your [libctru](https://github.com/smealum/ctrulib) install, building from the latest commit.
|
||||
Here are [Windows](https://buildbot.orphis.net/armips/) and [Linux](https://mega.nz/#!uQ1T1IAD!Q91O0e12LXKiaXh_YjXD3D5m8_W3FuMI-hEa6KVMRDQ) builds of armips (thanks to who compiled them!) and [makerom](https://github.com/Steveice10/buildtools/tree/master/3ds) (thanks @Steveice10!).
|
||||
Run `make` and everything should work!
|
||||
You can find the compiled files in the `out` folder.
|
||||
|
||||
---
|
||||
|
||||
@@ -11,6 +11,6 @@ SECTIONS
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(4); }
|
||||
.bss : ALIGN(8) { __bss_start = .; *(.bss* COMMON); . = ALIGN(8); __bss_end = .; }
|
||||
|
||||
__stack_top__ = 0x1FFFF000;
|
||||
__stack_top__ = 0x1FFFE000;
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
@@ -120,6 +120,16 @@ static void initScreens(u32 brightnessLevel, struct fb *fbs)
|
||||
*(vu32 *)0x10400584 = 0x10101 * i;
|
||||
}
|
||||
|
||||
static void setupFramebuffers(struct fb *fbs)
|
||||
{
|
||||
*(vu32 *)0x10400468 = (u32)fbs[0].top_left;
|
||||
*(vu32 *)0x1040046c = (u32)fbs[1].top_left;
|
||||
*(vu32 *)0x10400494 = (u32)fbs[0].top_right;
|
||||
*(vu32 *)0x10400498 = (u32)fbs[1].top_right;
|
||||
*(vu32 *)0x10400568 = (u32)fbs[0].bottom;
|
||||
*(vu32 *)0x1040056c = (u32)fbs[1].bottom;
|
||||
}
|
||||
|
||||
static void clearScreens(struct fb *fb)
|
||||
{
|
||||
//Setting up two simultaneous memory fills using the GPU
|
||||
@@ -175,6 +185,9 @@ void main(void)
|
||||
case INIT_SCREENS:
|
||||
initScreens(*(vu32 *)ARM11_PARAMETERS_ADDRESS, (struct fb *)(ARM11_PARAMETERS_ADDRESS + 4));
|
||||
break;
|
||||
case SETUP_FRAMEBUFFERS:
|
||||
setupFramebuffers((struct fb *)ARM11_PARAMETERS_ADDRESS);
|
||||
break;
|
||||
case CLEAR_SCREENS:
|
||||
clearScreens((struct fb *)ARM11_PARAMETERS_ADDRESS);
|
||||
break;
|
||||
|
||||
@@ -50,11 +50,12 @@ struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INIT_SCREENS = 0,
|
||||
SETUP_FRAMEBUFFERS,
|
||||
CLEAR_SCREENS,
|
||||
SWAP_FRAMEBUFFERS,
|
||||
UPDATE_BRIGHTNESS,
|
||||
|
||||
@@ -56,5 +56,5 @@ disableMpuAndJumpToEntrypoints:
|
||||
@ Jump to the ARM9 entrypoint
|
||||
mov r0, r4
|
||||
mov r1, r5
|
||||
ldr r2, =0x2BEEF
|
||||
ldr r2, =0x3BEEF
|
||||
bx r6
|
||||
|
||||
@@ -42,4 +42,4 @@ struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
@@ -13,8 +13,8 @@ dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -mthumb $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
CFLAGS := -Wall -Wextra -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -Os -ffast-math
|
||||
LDFLAGS := -nostartfiles -Wl,--nmagic
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
|
||||
@@ -4,7 +4,7 @@ OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x01FF7FE0;
|
||||
. = 0x01FF8000;
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
|
||||
@@ -43,8 +43,9 @@ typedef struct __attribute__((packed))
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
u32 readMPUConfig(u32 *regionSettings);
|
||||
void FIQHandler(void);
|
||||
void undefinedInstructionHandler(void);
|
||||
void dataAbortHandler(void);
|
||||
void prefetchAbortHandler(void);
|
||||
|
||||
u32 safecpy(void *dst, const void *src, u32 len);
|
||||
|
||||
@@ -22,92 +22,131 @@
|
||||
@ or requiring that modified versions of such material be marked in
|
||||
@ reasonable ways as different from the original version.
|
||||
|
||||
.macro GEN_HANDLER name
|
||||
.global \name
|
||||
.type \name, %function
|
||||
\name:
|
||||
ldr sp, =#0x02000000 @ We make the (full descending) stack point to the end of ITCM for our exception handlers.
|
||||
@ It doesn't matter if we're overwriting stuff here, since we're going to reboot.
|
||||
.macro GEN_USUAL_HANDLER name, index
|
||||
\name\()Handler:
|
||||
ldr sp, =_regs
|
||||
stmia sp, {r0-r7}
|
||||
|
||||
stmfd sp!, {r0-r7} @ FIQ has its own r8-r14 regs
|
||||
ldr r1, =\@ @ macro expansion counter
|
||||
mov r0, #\index
|
||||
b _commonHandler
|
||||
|
||||
.size \name, . - \name
|
||||
.endm
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
.balign 4
|
||||
|
||||
.global _commonHandler
|
||||
.type _commonHandler, %function
|
||||
_commonHandler:
|
||||
mov r1, r0
|
||||
mov r0, sp
|
||||
mrs r2, spsr
|
||||
mov r6, sp
|
||||
mrs r3, cpsr
|
||||
add r6, r0, #(8 * 4)
|
||||
|
||||
orr r3, #0x1c0 @ disable Imprecise Aborts, IRQ and FIQ (equivalent to "cpsid aif" on arm11)
|
||||
orr r3, #0xc0 @ mask interrupts
|
||||
msr cpsr_cx, r3
|
||||
|
||||
tst r2, #0x20
|
||||
bne noSvcBreak
|
||||
cmp r1, #2
|
||||
bne noSvcBreak
|
||||
|
||||
sub r0, lr, #4 @ calling cannotAccessAddress cause more problems that it actually solves... (I've to save a lot of regs and that's a pain tbh)
|
||||
lsr r0, #20 @ we'll just do some address checks (to see if it's in ARM9 internal memory)
|
||||
cmp r0, #0x80
|
||||
bne noSvcBreak
|
||||
ldr r4, [lr, #-4]
|
||||
ldr r5, =#0xe12fff7f
|
||||
cmp r4, r5
|
||||
bne noSvcBreak
|
||||
bic r5, r3, #0xf
|
||||
orr r5, #0x3
|
||||
msr cpsr_c, r5 @ switch to supervisor mode
|
||||
ldmfd sp, {r8-r11}^
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
addne lr, r4, #2 @ adjust address for later
|
||||
moveq lr, r4
|
||||
|
||||
noSvcBreak:
|
||||
ands r4, r2, #0xf @ get the mode that triggered the exception
|
||||
moveq r4, #0xf @ usr => sys
|
||||
bic r5, r3, #0xf
|
||||
orr r5, r4
|
||||
msr cpsr_c, r5 @ change processor mode
|
||||
stmfd r6!, {r8-lr}
|
||||
stmia r6!, {r8-lr}
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
mov sp, r6
|
||||
|
||||
stmfd sp!, {r2,lr} @ it's a bit of a mess, but we will fix that later
|
||||
@ order of saved regs now: cpsr, pc + (2/4/8), r8-r14, r0-r7
|
||||
|
||||
mov r0, sp
|
||||
str lr, [r6], #4
|
||||
str r2, [r6]
|
||||
|
||||
msr cpsr_cxsf, #0xdf @ finally, switch to system mode, mask interrupts and clear flags (in case of double faults)
|
||||
ldr sp, =0x02000000
|
||||
b mainHandler
|
||||
|
||||
GEN_HANDLER FIQHandler
|
||||
GEN_HANDLER undefinedInstructionHandler
|
||||
GEN_HANDLER prefetchAbortHandler
|
||||
GEN_HANDLER dataAbortHandler
|
||||
|
||||
.global readMPUConfig
|
||||
.type readMPUConfig, %function
|
||||
readMPUConfig:
|
||||
stmfd sp!, {r4-r8, lr}
|
||||
mrc p15,0,r1,c6,c0,0
|
||||
mrc p15,0,r2,c6,c1,0
|
||||
mrc p15,0,r3,c6,c2,0
|
||||
mrc p15,0,r4,c6,c3,0
|
||||
mrc p15,0,r5,c6,c4,0
|
||||
mrc p15,0,r6,c6,c5,0
|
||||
mrc p15,0,r7,c6,c6,0
|
||||
mrc p15,0,r8,c6,c7,0
|
||||
stmia r0, {r1-r8}
|
||||
mrc p15,0,r0,c5,c0,2 @ read data access permission bits
|
||||
ldmfd sp!, {r4-r8, pc}
|
||||
.global FIQHandler
|
||||
.type FIQHandler, %function
|
||||
GEN_USUAL_HANDLER FIQ, 0
|
||||
|
||||
.global undefinedInstructionHandler
|
||||
.type undefinedInstructionHandler, %function
|
||||
GEN_USUAL_HANDLER undefinedInstruction, 1
|
||||
|
||||
.global prefetchAbortHandler
|
||||
.type prefetchAbortHandler, %function
|
||||
prefetchAbortHandler:
|
||||
msr cpsr_cx, #0xd7 @ mask interrupts (abort mode)
|
||||
mrs sp, spsr
|
||||
and sp, #0x3f
|
||||
cmp sp, #0x13
|
||||
bne _prefetchAbortNormalHandler
|
||||
|
||||
ldr sp, =BreakPtr
|
||||
ldr sp, [sp]
|
||||
cmp sp, #0
|
||||
beq _prefetchAbortNormalHandler
|
||||
add sp, #(1*4 + 4)
|
||||
cmp lr, sp
|
||||
bne _prefetchAbortNormalHandler
|
||||
|
||||
mov sp, r8
|
||||
pop {r8-r11}
|
||||
ldr lr, [sp, #8]!
|
||||
ldr sp, [sp, #4]
|
||||
msr spsr_cxsf, sp
|
||||
tst sp, #0x20
|
||||
addne lr, #2 @ adjust address for later
|
||||
|
||||
GEN_USUAL_HANDLER _prefetchAbortNormal, 2
|
||||
|
||||
.global dataAbortHandler
|
||||
.type dataAbortHandler, %function
|
||||
dataAbortHandler:
|
||||
msr cpsr_cx, #0xd7 @ mask interrupts (abort mode)
|
||||
mrs sp, spsr
|
||||
and sp, #0x3f
|
||||
cmp sp, #0x1f
|
||||
bne _dataAbortNormalHandler
|
||||
|
||||
sub lr, #8
|
||||
adr sp, safecpy
|
||||
cmp lr, sp
|
||||
blo _j_dataAbortNormalHandler
|
||||
adr sp, _safecpy_end
|
||||
cmp lr, sp
|
||||
bhs _j_dataAbortNormalHandler
|
||||
|
||||
msr spsr_f, #(1 << 30)
|
||||
mov r12, #0
|
||||
adds pc, lr, #4
|
||||
|
||||
_j_dataAbortNormalHandler:
|
||||
add lr, #8
|
||||
|
||||
GEN_USUAL_HANDLER _dataAbortNormal, 3
|
||||
|
||||
|
||||
.global safecpy
|
||||
.type safecpy, %function
|
||||
safecpy:
|
||||
push {r4, lr}
|
||||
mov r3, #0
|
||||
movs r12, #1
|
||||
|
||||
_safecpy_loop:
|
||||
ldrb r4, [r1, r3]
|
||||
cmp r12, #0
|
||||
beq _safecpy_loop_end
|
||||
strb r4, [r0, r3]
|
||||
add r3, #1
|
||||
cmp r3, r2
|
||||
blo _safecpy_loop
|
||||
|
||||
_safecpy_loop_end:
|
||||
mov r0, r3
|
||||
pop {r4, pc}
|
||||
|
||||
_safecpy_end:
|
||||
|
||||
.bss
|
||||
.balign 4
|
||||
_regs: .skip (4 * 17)
|
||||
|
||||
@@ -32,48 +32,10 @@
|
||||
#define REG_DUMP_SIZE 4 * 17
|
||||
#define CODE_DUMP_SIZE 48
|
||||
|
||||
bool cannotAccessAddress(const void *address)
|
||||
{
|
||||
u32 regionSettings[8];
|
||||
u32 addr = (u32)address;
|
||||
|
||||
u32 dataAccessPermissions = readMPUConfig(regionSettings);
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if((dataAccessPermissions & 0xF) == 0 || (regionSettings[i] & 1) == 0)
|
||||
continue; //No access / region not enabled
|
||||
|
||||
u32 regionAddrBase = regionSettings[i] & ~0xFFF;
|
||||
u32 regionSize = 1 << (((regionSettings[i] >> 1) & 0x1F) + 1);
|
||||
|
||||
if(addr >= regionAddrBase && addr < regionAddrBase + regionSize)
|
||||
return false;
|
||||
|
||||
dataAccessPermissions >>= 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) copyMemory(void *dst, const void *src, u32 size, u32 alignment)
|
||||
{
|
||||
u8 *out = (u8 *)dst;
|
||||
const u8 *in = (const u8 *)src;
|
||||
|
||||
if(((u32)src & (alignment - 1)) != 0 || cannotAccessAddress(src) || (size != 0 && cannotAccessAddress((u8 *)src + size - 1)))
|
||||
return 0;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
*out++ = *in++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type)
|
||||
void __attribute__((noreturn)) mainHandler(u32 *registerDump, u32 type)
|
||||
{
|
||||
ExceptionDumpHeader dumpHeader;
|
||||
|
||||
u32 registerDump[REG_DUMP_SIZE / 4];
|
||||
u8 codeDump[CODE_DUMP_SIZE];
|
||||
|
||||
dumpHeader.magic[0] = 0xDEADC0DE;
|
||||
@@ -89,34 +51,32 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type)
|
||||
dumpHeader.codeDumpSize = CODE_DUMP_SIZE;
|
||||
dumpHeader.additionalDataSize = 0;
|
||||
|
||||
//Dump registers
|
||||
//Current order of saved regs: cpsr, pc, r8-r14, r0-r7
|
||||
u32 cpsr = regs[0];
|
||||
u32 pc = regs[1] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
u32 cpsr = registerDump[16];
|
||||
u32 pc = registerDump[15] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[2 + i];
|
||||
for(u32 i = 0; i < 8; i++) registerDump[i] = regs[9 + i];
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
|
||||
|
||||
//Copy register dump and code dump
|
||||
u8 *final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader));
|
||||
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
|
||||
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
|
||||
final += safecpy(final, registerDump, dumpHeader.registerDumpSize);
|
||||
final += safecpy(final, codeDump, dumpHeader.codeDumpSize);
|
||||
|
||||
//Dump stack in place
|
||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
||||
dumpHeader.stackDumpSize = safecpy(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF));
|
||||
|
||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
||||
|
||||
//Copy header (actually optimized by the compiler)
|
||||
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader;
|
||||
|
||||
if(ARESCREENSINITIALIZED) i2cWriteRegister(I2C_DEV_MCU, 0x22, 1 << 0); //Shutdown LCD
|
||||
|
||||
((void (*)())0xFFFF0830)(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot
|
||||
while(true);
|
||||
}
|
||||
|
||||
@@ -26,8 +26,12 @@
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction
|
||||
|
||||
.global BreakPtr
|
||||
BreakPtr: .word 0
|
||||
|
||||
.global handlers
|
||||
handlers:
|
||||
.word FIQHandler
|
||||
.word undefinedInstructionHandler
|
||||
|
||||
@@ -39,3 +39,7 @@ typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
#define ARESCREENSINITIALIZED (PDN_GPU_CNT != 1)
|
||||
|
||||
25
exceptions/exception_dump_parser.py → exceptions/exception_dump_parser/luma3ds_exception_dump_parser/__main__.py
Normal file → Executable file
25
exceptions/exception_dump_parser.py → exceptions/exception_dump_parser/luma3ds_exception_dump_parser/__main__.py
Normal file → Executable file
@@ -90,8 +90,15 @@ def makeRegisterLine(A, rA, B, rB):
|
||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
||||
faultStatusSources = {
|
||||
0b1:'Alignment', 0b100:'Instruction cache maintenance operation fault',
|
||||
0b1100:'External Abort on translation - First-level', 0b1110:'External Abort on translation - Second-level',
|
||||
0b101:'Translation - Section', 0b111:'Translation - Page', 0b11:'Access bit - Section', 0b110:'Access bit - Page',
|
||||
0b1001:'Domain - Section', 0b1011:'Domain - Page', 0b1101:'Permission - Section', 0b1111:'Permission - Page',
|
||||
0b1000:'Precise External Abort', 0b10110:'Imprecise External Abort', 0b10:'Debug event'
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main(args=None):
|
||||
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
|
||||
parser.add_argument("filename")
|
||||
args = parser.parse_args()
|
||||
@@ -134,6 +141,11 @@ if __name__ == "__main__":
|
||||
typeDetailsStr = " (VFP exception)"
|
||||
|
||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
||||
|
||||
if processor == 11 and exceptionType >= 2:
|
||||
xfsr = registers[18] if exceptionType == 2 else registers[17]
|
||||
print("Fault status: " + faultStatusSources[xfsr & 0xf])
|
||||
|
||||
if additionalDataSize != 0:
|
||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
||||
|
||||
@@ -143,6 +155,9 @@ if __name__ == "__main__":
|
||||
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
|
||||
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
|
||||
|
||||
if processor == 11 and exceptionType == 3:
|
||||
print("{0:<15}{1:<20}Access type: {2}".format("FAR", "{0:08x}".format(registers[19]), "Write" if registers[17] & (1 << 11) != 0 else "Read"))
|
||||
|
||||
thumb = registers[16] & 0x20 != 0
|
||||
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
|
||||
|
||||
@@ -151,8 +166,9 @@ if __name__ == "__main__":
|
||||
objdump_res = ""
|
||||
try:
|
||||
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
|
||||
if os.name == "nt":
|
||||
path = ''.join((path[1], ':', path[2:])).replace('/', '\\')
|
||||
|
||||
if os.name == "nt" and path[0] == '/':
|
||||
path = ''.join((path[1], ':', path[2:]))
|
||||
|
||||
objdump_res = subprocess.check_output((
|
||||
path, "-marm", "-b", "binary",
|
||||
@@ -167,3 +183,6 @@ if __name__ == "__main__":
|
||||
|
||||
print("\nStack dump:\n")
|
||||
print(hexdump(registers[13], stackDump))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
13
exceptions/exception_dump_parser/setup.py
Normal file
13
exceptions/exception_dump_parser/setup.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='luma3ds_exception_dump_parser',
|
||||
version='1.2',
|
||||
url='https://github.com/AuroraWright/Luma3DS',
|
||||
author='TuxSH',
|
||||
license='GPLv3',
|
||||
description='Parses Luma3DS exception dumps',
|
||||
install_requires=[''],
|
||||
packages=find_packages(),
|
||||
entry_points={'console_scripts': ['luma3ds_exception_dump_parser=luma3ds_exception_dump_parser.__main__:main']},
|
||||
)
|
||||
@@ -12,12 +12,14 @@
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
|
||||
#define BOOTCFG_NTRCARDBOOT BOOTCONFIG(7, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
SPLASH_DURATION,
|
||||
PIN,
|
||||
NEWCPU
|
||||
};
|
||||
@@ -30,7 +32,6 @@ enum singleOptions
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
DISABLEARM11EXCHANDLERS
|
||||
};
|
||||
|
||||
39
k11_extension/include/debug.h
Normal file
39
k11_extension/include/debug.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "globals.h"
|
||||
#include "kernel.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern KRecursiveLock dbgParamsLock;
|
||||
extern u32 dbgParamWatchpointId, dbgParamDVA, dbgParamWCR, dbgParamContextId;
|
||||
|
||||
KSchedulableInterruptEvent *enableMonitorModeDebugging(KBaseInterruptEvent *this, u32 interruptID);
|
||||
KSchedulableInterruptEvent *disableWatchpoint(KBaseInterruptEvent *this, u32 interruptID);
|
||||
KSchedulableInterruptEvent *setWatchpointWithContextId(KBaseInterruptEvent *this, u32 interruptID);
|
||||
@@ -56,6 +56,7 @@ extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
||||
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
||||
extern void (*SleepThread)(s64 ns);
|
||||
extern Result (*CloseHandle)(Handle handle);
|
||||
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
||||
extern Result (*GetProcessInfo)(s64 *out, Handle processHandle, u32 type);
|
||||
extern Result (*GetThreadInfo)(s64 *out, Handle threadHandle, u32 type);
|
||||
|
||||
@@ -1137,21 +1137,29 @@ typedef struct FcramLayout
|
||||
extern bool isN3DS;
|
||||
extern void *officialSVCs[0x7E];
|
||||
|
||||
#define KPROCESS_OFFSETOF(field) (isN3DS ? offsetof(KProcessN3DS, field) :\
|
||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(KProcessO3DS8x, field) :\
|
||||
offsetof(KProcessO3DSPre8x, field)))
|
||||
#define KPROCESSRELATED_OFFSETOFF(classname, field) (isN3DS ? offsetof(classname##N3DS, field) :\
|
||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? offsetof(classname##O3DS8x, field) :\
|
||||
offsetof(classname##O3DSPre8x, field)))
|
||||
|
||||
#define KPROCESS_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
|
||||
#define KPROCESSRELATED_GET_PTR(obj, field) (isN3DS ? &(obj)->N3DS.field :\
|
||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? &(obj)->O3DS8x.field :\
|
||||
&(obj)->O3DSPre8x.field ))
|
||||
&(obj)->O3DSPre8x.field))
|
||||
|
||||
#define KPROCESS_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
|
||||
#define KPROCESSRELATED_GET_PTR_TYPE(type, obj, field) (isN3DS ? (type *)(&(obj)->N3DS.field) :\
|
||||
((kernelVersion >= SYSTEM_VERSION(2, 44, 6)) ? (type *)(&(obj)->O3DS8x.field) :\
|
||||
(type *)(&(obj)->O3DSPre8x.field) ))
|
||||
(type *)(&(obj)->O3DSPre8x.field)))
|
||||
|
||||
#define KPROCESS_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcess, field)
|
||||
#define KPROCESS_GET_PTR(obj, field) KPROCESSRELATED_GET_PTR(obj, field)
|
||||
#define KPROCESS_GET_PTR_TYPE(type, obj, field) KPROCESSRELATED_GET_PTR_TYPE(type, obj, field)
|
||||
#define KPROCESS_GET_RVALUE(obj, field) *(KPROCESS_GET_PTR(obj, field))
|
||||
#define KPROCESS_GET_RVALUE_TYPE(type, obj, field) *(KPROCESS_GET_PTR_TYPE(type, obj, field))
|
||||
|
||||
#define KPROCESS_GET_RVALUE_TYPE(type, obj, field) *(KPROCESS_GET_PTR(type, obj, field))
|
||||
#define KPROCESSHWINFO_OFFSETOF(field) KPROCESSRELATED_OFFSETOFF(KProcessHwInfo, field)
|
||||
#define KPROCESSHWINFO_GET_PTR(obj, field) KPROCESSRELATED_GET_PTR(obj, field)
|
||||
#define KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field) KPROCESSRELATED_GET_PTR_TYPE(type, obj, field)
|
||||
#define KPROCESSHWINFO_GET_RVALUE(obj, field) *(KPROCESSHWINFO_GET_PTR(obj, field))
|
||||
#define KPROCESSHWINFO_GET_RVALUE_TYPE(type, obj, field) *(KPROCESSHWINFO_GET_PTR_TYPE(type, obj, field))
|
||||
|
||||
static inline u32 idOfProcess(KProcess *process)
|
||||
{
|
||||
@@ -1178,6 +1186,20 @@ static inline KDebug *debugOfProcess(KProcess *process)
|
||||
return KPROCESS_GET_RVALUE(process, debug);
|
||||
}
|
||||
|
||||
static inline const char *classNameOfAutoObject(KAutoObject *object)
|
||||
{
|
||||
const char *name;
|
||||
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
||||
{
|
||||
KClassToken tok;
|
||||
object->vtable->GetClassToken(&tok, object);
|
||||
name = tok.name;
|
||||
}
|
||||
else
|
||||
name = object->vtable->GetClassName(object);
|
||||
return name;
|
||||
}
|
||||
|
||||
extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
||||
|
||||
static inline Result createHandleForProcess(Handle *out, KProcess *process, KAutoObject *obj)
|
||||
|
||||
34
k11_extension/include/svc/GetHandleInfo.h
Normal file
34
k11_extension/include/svc/GetHandleInfo.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include "kernel.h"
|
||||
#include "svc.h"
|
||||
|
||||
Result GetHandleInfoHookWrapper(u32 dummy, Handle handle, u32 type);
|
||||
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type);
|
||||
@@ -30,4 +30,4 @@
|
||||
#include "kernel.h"
|
||||
#include "svc.h"
|
||||
|
||||
void SetWifiEnabled(bool enable);
|
||||
Result SetWifiEnabled(bool enable);
|
||||
|
||||
147
k11_extension/source/debug.c
Normal file
147
k11_extension/source/debug.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
#include "synchronization.h"
|
||||
|
||||
KRecursiveLock dbgParamsLock = { NULL };
|
||||
u32 dbgParamWatchpointId, dbgParamDVA, dbgParamWCR, dbgParamContextId;
|
||||
|
||||
KSchedulableInterruptEvent *enableMonitorModeDebugging(KBaseInterruptEvent *this UNUSED, u32 interruptID UNUSED)
|
||||
{
|
||||
coreBarrier();
|
||||
|
||||
u32 DSCR;
|
||||
__asm__ __volatile__("mrc p14, 0, %[val], c0, c1, 0" : [val] "=r" (DSCR));
|
||||
DSCR |= 0x8000;
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 0" :: [val] "r" (DSCR));
|
||||
|
||||
__dsb();
|
||||
coreBarrier();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void disableWatchpoint0(void)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
// WCR0
|
||||
__asm__ __volatile__("mrc p14, 0, %[val], c0, c0, 7" : [val] "=r" (control));
|
||||
control &= ~1;
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 7" :: [val] "r" (control));
|
||||
|
||||
// BCR4
|
||||
__asm__ __volatile__("mrc p14, 0, %[val], c0, c4, 5" : [val] "=r" (control));
|
||||
control &= ~1;
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 5" :: [val] "r" (control));
|
||||
}
|
||||
|
||||
static void disableWatchpoint1(void)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
// WCR1
|
||||
__asm__ __volatile__("mrc p14, 0, %[val], c0, c1, 7" : [val] "=r" (control));
|
||||
control &= ~1;
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 7" :: [val] "r" (control));
|
||||
|
||||
// BCR5
|
||||
__asm__ __volatile__("mrc p14, 0, %[val], c0, c5, 5" : [val] "=r" (control));
|
||||
control &= ~1;
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 5" :: [val] "r" (control));
|
||||
}
|
||||
|
||||
KSchedulableInterruptEvent *disableWatchpoint(KBaseInterruptEvent *this UNUSED, u32 interruptID UNUSED)
|
||||
{
|
||||
coreBarrier();
|
||||
|
||||
if(dbgParamWatchpointId == 0)
|
||||
disableWatchpoint0();
|
||||
else
|
||||
disableWatchpoint1();
|
||||
|
||||
__dsb();
|
||||
coreBarrier();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void setWatchpoint0WithContextId(u32 DVA, u32 WCR, u32 contextId)
|
||||
{
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/CEGCFFDF.html
|
||||
u32 BCR =
|
||||
(1 << 21) | /* compare with context ID */
|
||||
(1 << 20) | /* linked (with a WRP in our case) */
|
||||
(0xf << 5) | /* byte address select, +0 to +3 as mandated when linking with a WRP */
|
||||
(3 << 1) | /* either privileged modes or user mode, as mandated when linking with a WRP */
|
||||
(1 << 0) ; /* enabled */
|
||||
|
||||
disableWatchpoint0();
|
||||
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 6" :: [val] "r" (DVA));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 4" :: [val] "r" (contextId));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c0, 7" :: [val] "r" (WCR));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c4, 5" :: [val] "r" (BCR));
|
||||
|
||||
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); // DMB
|
||||
}
|
||||
|
||||
static void setWatchpoint1WithContextId(u32 DVA, u32 WCR, u32 contextId)
|
||||
{
|
||||
// http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/CEGCFFDF.html
|
||||
u32 BCR =
|
||||
(1 << 21) | /* compare with context ID */
|
||||
(1 << 20) | /* linked (with a WRP in our case) */
|
||||
(0xf << 5) | /* byte address select, +0 to +3 as mandated when linking with a WRP */
|
||||
(3 << 1) | /* either privileged modes or user mode, as mandated when linking with a WRP */
|
||||
(1 << 0) ; /* enabled */
|
||||
|
||||
disableWatchpoint1();
|
||||
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 6" :: [val] "r" (DVA));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 4" :: [val] "r" (contextId));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c1, 7" :: [val] "r" (WCR));
|
||||
__asm__ __volatile__("mcr p14, 0, %[val], c0, c5, 5" :: [val] "r" (BCR));
|
||||
|
||||
__asm__ __volatile__("mcr p15, 0, %[val], c7, c10, 5" :: [val] "r" (0) : "memory"); // DMB
|
||||
}
|
||||
|
||||
KSchedulableInterruptEvent *setWatchpointWithContextId(KBaseInterruptEvent *this UNUSED, u32 interruptID UNUSED)
|
||||
{
|
||||
coreBarrier();
|
||||
|
||||
if(dbgParamWatchpointId == 0)
|
||||
setWatchpoint0WithContextId(dbgParamDVA, dbgParamWCR, dbgParamContextId);
|
||||
else
|
||||
setWatchpoint1WithContextId(dbgParamDVA, dbgParamWCR, dbgParamContextId);
|
||||
|
||||
__dsb();
|
||||
coreBarrier();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ _commonHandler:
|
||||
|
||||
_no_L2C:
|
||||
|
||||
cps #0x1F
|
||||
msr cpsr_cxsf, #0xdf @ finally, switch to system mode, mask interrupts and clear flags (in case of double faults)
|
||||
ldr sp, =exceptionStackTop
|
||||
ldr sp, [sp]
|
||||
sub sp, #0x100
|
||||
@@ -221,7 +221,8 @@ prefetchAbortHandler:
|
||||
pop {r8-r11}
|
||||
ldr lr, [sp, #8]!
|
||||
ldr sp, [sp, #4]
|
||||
msr spsr, sp
|
||||
msr spsr_cxsf, sp
|
||||
tst sp, #0x20
|
||||
addne lr, #2 @ adjust address for later
|
||||
|
||||
GEN_USUAL_HANDLER _prefetchAbortNormal, 2, 12
|
||||
|
||||
@@ -37,7 +37,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
||||
{
|
||||
if(CONFIG(DISABLEARM11EXCHANDLERS)) return false;
|
||||
|
||||
if((spsr & 0x1f) != 0x10) return true;
|
||||
if((spsr & 0x1F) != 0x10) return true;
|
||||
|
||||
KThread *thread = currentCoreContext->objectContext.currentThread;
|
||||
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||
@@ -65,7 +65,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
||||
extern u32 safecpy_sz;
|
||||
bool isDataAbortExceptionRangeControlled(u32 spsr, u32 addr)
|
||||
{
|
||||
return ((spsr & 0x1F) != 0x10) && (
|
||||
return (!(spsr & 0x20) && (spsr & 0x1F) != 0x10) && (
|
||||
((u32)kernelUsrCopyFuncsStart <= addr && addr < (u32)kernelUsrCopyFuncsEnd) ||
|
||||
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
|
||||
);
|
||||
@@ -96,7 +96,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
||||
registerDump[15] = pc;
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
|
||||
|
||||
//Copy register dump and code dump
|
||||
|
||||
@@ -52,6 +52,7 @@ void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
||||
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
||||
void (*SleepThread)(s64 ns);
|
||||
Result (*CloseHandle)(Handle handle);
|
||||
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
||||
Result (*GetProcessInfo)(s64 *out, Handle processHandle, u32 type);
|
||||
Result (*GetThreadInfo)(s64 *out, Handle threadHandle, u32 type);
|
||||
|
||||
@@ -208,6 +208,7 @@ static void findUsefulSymbols(void)
|
||||
decodeARMBranch((u32 *)officialSVCs[0x01] + 5);
|
||||
SleepThread = (void (*)(s64))officialSVCs[0x0A];
|
||||
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
|
||||
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x29] + 3);
|
||||
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeARMBranch((u32 *)officialSVCs[0x2A] + 3);
|
||||
GetProcessInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x2B] + 3);
|
||||
GetThreadInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x2C] + 3);
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
#include "synchronization.h"
|
||||
#include "svc.h"
|
||||
#include "svc/ControlMemory.h"
|
||||
#include "svc/GetHandleInfo.h"
|
||||
#include "svc/GetSystemInfo.h"
|
||||
#include "svc/GetProcessInfo.h"
|
||||
#include "svc/GetThreadInfo.h"
|
||||
#include "svc/GetSystemInfo.h"
|
||||
#include "svc/GetCFWInfo.h"
|
||||
#include "svc/ConnectToPort.h"
|
||||
#include "svc/SendSyncRequest.h"
|
||||
@@ -103,6 +104,8 @@ void *svcHook(u8 *pageEnd)
|
||||
doingVeryShittyPmResLimitWorkaround = true;
|
||||
}
|
||||
return officialSVCs[0x17];
|
||||
case 0x29:
|
||||
return GetHandleInfoHookWrapper;
|
||||
case 0x2A:
|
||||
return GetSystemInfoHookWrapper;
|
||||
case 0x2B:
|
||||
|
||||
@@ -42,20 +42,12 @@ Result ControlService(ServiceOp op, u32 varg1, u32 varg2)
|
||||
KAutoObject *obj = KProcessHandleTable__ToKAutoObject(handleTable, (Handle)varg2);
|
||||
if(obj == NULL)
|
||||
return 0xD8E007F7; // invalid handle
|
||||
else if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
||||
{
|
||||
KClassToken tok;
|
||||
obj->vtable->GetClassToken(&tok, obj);
|
||||
if(tok.flags == 0x95)
|
||||
session = ((KServerSession *)obj)->parentSession;
|
||||
else if(tok.flags == 0xA5)
|
||||
session = ((KClientSession *)obj)->parentSession;
|
||||
}
|
||||
else
|
||||
{ // not the exact same tests but it should work
|
||||
if(strcmp(obj->vtable->GetClassName(obj), "KServerSession") == 0)
|
||||
{
|
||||
// not the exact same tests but it should work
|
||||
if(strcmp(classNameOfAutoObject(obj), "KServerSession") == 0)
|
||||
session = ((KServerSession *)obj)->parentSession;
|
||||
else if(strcmp(obj->vtable->GetClassName(obj), "KClientSession") == 0)
|
||||
else if(strcmp(classNameOfAutoObject(obj), "KClientSession") == 0)
|
||||
session = ((KClientSession *)obj)->parentSession;
|
||||
}
|
||||
|
||||
|
||||
62
k11_extension/source/svc/GetHandleInfo.c
Normal file
62
k11_extension/source/svc/GetHandleInfo.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#include "svc/GetThreadInfo.h"
|
||||
#include "memory.h"
|
||||
|
||||
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
||||
{
|
||||
if(type == 0x10000) // KDebug and KProcess: get context ID
|
||||
{
|
||||
KProcessHwInfo *hwInfo;
|
||||
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||
KAutoObject *obj;
|
||||
if(handle == CUR_PROCESS_HANDLE)
|
||||
{
|
||||
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
||||
KAutoObject__AddReference(obj);
|
||||
}
|
||||
else
|
||||
obj = KProcessHandleTable__ToKAutoObject(handleTable, handle);
|
||||
|
||||
if(obj == NULL)
|
||||
return 0xD8E007F7;
|
||||
|
||||
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
|
||||
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
|
||||
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
|
||||
hwInfo = hwInfoOfProcess((KProcess *)obj);
|
||||
else
|
||||
hwInfo = NULL;
|
||||
|
||||
*out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1;
|
||||
|
||||
obj->vtable->DecrementReferenceCount(obj);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return GetHandleInfo(out, handle, type);
|
||||
}
|
||||
@@ -73,12 +73,12 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type)
|
||||
*out = (s64)(u64)(u32)codeSetOfProcess(process)->dataSection.section.loadAddress;
|
||||
break;
|
||||
case 0x10008:
|
||||
*out = (isN3DS ? hwInfoOfProcess(process)->N3DS.translationTableBase :
|
||||
(kernelVersion >= SYSTEM_VERSION(2, 44, 6)
|
||||
? hwInfoOfProcess(process)->O3DS8x.translationTableBase
|
||||
: hwInfoOfProcess(process)->O3DSPre8x.translationTableBase)
|
||||
) & ~((1 << (14 - TTBCR)) - 1);
|
||||
{
|
||||
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
|
||||
u32 ttb = KPROCESSHWINFO_GET_RVALUE(hwInfo, translationTableBase);
|
||||
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = 0xD8E007ED; // invalid enum value
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "svc/KernelSetState.h"
|
||||
#include "synchronization.h"
|
||||
#include "ipc.h"
|
||||
#include "debug.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define MAX_DEBUG 3
|
||||
@@ -142,7 +143,41 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
||||
res = SetSyscallDebugEventMask(varg1, (bool)varg2, (const u32 *)varg3);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x10003:
|
||||
{
|
||||
executeFunctionOnCores(enableMonitorModeDebugging, 0xF, 0);
|
||||
break;
|
||||
}
|
||||
case 0x10004:
|
||||
{
|
||||
KRecursiveLock__Lock(&dbgParamsLock);
|
||||
dbgParamWatchpointId = varg1;
|
||||
executeFunctionOnCores(disableWatchpoint, 0xF, 0);
|
||||
KRecursiveLock__Unlock(&dbgParamsLock);
|
||||
break;
|
||||
}
|
||||
case 0x10005:
|
||||
{
|
||||
KRecursiveLock__Lock(&dbgParamsLock);
|
||||
dbgParamWatchpointId = 0;
|
||||
dbgParamDVA = varg1;
|
||||
dbgParamWCR = varg2;
|
||||
dbgParamContextId = varg3;
|
||||
executeFunctionOnCores(setWatchpointWithContextId, 0xF, 0);
|
||||
KRecursiveLock__Unlock(&dbgParamsLock);
|
||||
break;
|
||||
}
|
||||
case 0x10006:
|
||||
{
|
||||
KRecursiveLock__Lock(&dbgParamsLock);
|
||||
dbgParamWatchpointId = 1;
|
||||
dbgParamDVA = varg1;
|
||||
dbgParamWCR = varg2;
|
||||
dbgParamContextId = varg3;
|
||||
executeFunctionOnCores(setWatchpointWithContextId, 0xF, 0);
|
||||
KRecursiveLock__Unlock(&dbgParamsLock);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
res = KernelSetState(type, varg1, varg2, varg3);
|
||||
|
||||
@@ -37,15 +37,8 @@ Result SendSyncRequestHook(Handle handle)
|
||||
bool skip = false;
|
||||
Result res = 0;
|
||||
|
||||
bool isValidClientSession = false;
|
||||
if(clientSession != NULL && kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
||||
{
|
||||
KClassToken tok;
|
||||
clientSession->syncObject.autoObject.vtable->GetClassToken(&tok, &clientSession->syncObject.autoObject);
|
||||
isValidClientSession = tok.flags == 0xA5;
|
||||
}
|
||||
else if(clientSession != NULL) // not the exact same test but it should work
|
||||
isValidClientSession = strcmp(clientSession->syncObject.autoObject.vtable->GetClassName(&clientSession->syncObject.autoObject), "KClientSession") == 0;
|
||||
// not the exact same test but it should work
|
||||
bool isValidClientSession = clientSession != NULL && strcmp(classNameOfAutoObject(&clientSession->syncObject.autoObject), "KClientSession") == 0;
|
||||
|
||||
if(isValidClientSession)
|
||||
{
|
||||
@@ -54,7 +47,7 @@ Result SendSyncRequestHook(Handle handle)
|
||||
case 0x10042:
|
||||
{
|
||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||
if(info != NULL && strcmp(info->name, "srv:pm") == 0)
|
||||
if(info != NULL && kernelVersion >= SYSTEM_VERSION(2, 39, 4) && strcmp(info->name, "srv:pm") == 0)
|
||||
{
|
||||
res = doPublishToProcessHook(handle, cmdbuf);
|
||||
skip = true;
|
||||
@@ -112,7 +105,7 @@ Result SendSyncRequestHook(Handle handle)
|
||||
case 0x50100:
|
||||
{
|
||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||
if(info != NULL && strcmp(info->name, "srv:") == 0)
|
||||
if(info != NULL && (strcmp(info->name, "srv:") == 0 || (kernelVersion < SYSTEM_VERSION(2, 39, 4) && strcmp(info->name, "srv:pm") == 0)))
|
||||
{
|
||||
char name[9] = { 0 };
|
||||
memcpy(name, cmdbuf + 1, 8);
|
||||
@@ -126,6 +119,7 @@ Result SendSyncRequestHook(Handle handle)
|
||||
outClientSession = (KClientSession *)KProcessHandleTable__ToKAutoObject(handleTable, (Handle)cmdbuf[3]);
|
||||
if(outClientSession != NULL)
|
||||
{
|
||||
if(strcmp(classNameOfAutoObject(&outClientSession->syncObject.autoObject), "KClientSession") == 0)
|
||||
SessionInfo_Add(outClientSession->parentSession, name);
|
||||
outClientSession->syncObject.autoObject.vtable->DecrementReferenceCount(&outClientSession->syncObject.autoObject);
|
||||
}
|
||||
@@ -163,7 +157,7 @@ Result SendSyncRequestHook(Handle handle)
|
||||
case 0x4010042:
|
||||
{
|
||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||
if(info != NULL && strcmp(info->name, "srv:pm") == 0)
|
||||
if(info != NULL && kernelVersion < SYSTEM_VERSION(2, 39, 4) && strcmp(info->name, "srv:pm") == 0)
|
||||
{
|
||||
res = doPublishToProcessHook(handle, cmdbuf);
|
||||
skip = true;
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
|
||||
#include "svc/SetWifiEnabled.h"
|
||||
|
||||
void SetWifiEnabled(bool enable)
|
||||
Result SetWifiEnabled(bool enable)
|
||||
{
|
||||
if(enable)
|
||||
CFG11_WIFICNT |= 1;
|
||||
else
|
||||
CFG11_WIFICNT &= ~1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -50,14 +50,7 @@ Result TranslateHandle(u32 *outKAddr, char *outClassName, Handle handle)
|
||||
if(obj == NULL)
|
||||
return 0xD8E007F7; // invalid handle
|
||||
|
||||
if(kernelVersion >= SYSTEM_VERSION(2, 46, 0))
|
||||
{
|
||||
KClassToken tok;
|
||||
obj->vtable->GetClassToken(&tok, obj);
|
||||
name = tok.name;
|
||||
}
|
||||
else
|
||||
name = obj->vtable->GetClassName(obj);
|
||||
name = classNameOfAutoObject(obj);
|
||||
|
||||
if(name == NULL) // shouldn't happen
|
||||
name = "KAutoObject";
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
pop {r1, r2, r12, pc}
|
||||
.endm
|
||||
|
||||
GEN_GETINFO_WRAPPER Handle
|
||||
GEN_GETINFO_WRAPPER System
|
||||
GEN_GETINFO_WRAPPER Process
|
||||
GEN_GETINFO_WRAPPER Thread
|
||||
|
||||
@@ -116,9 +116,7 @@ fname: .ascii "FILE"
|
||||
.align 4
|
||||
kernelcode_start:
|
||||
|
||||
mrs r0, cpsr ; disable interrupts
|
||||
orr r0, #0xC0
|
||||
msr cpsr, r0
|
||||
msr cpsr_cxsf, #0xD3 ; disable interrupts and clear flags
|
||||
|
||||
ldr sp, =copy_launch_stub_stack_top
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
|
||||
#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 (DPAD_BUTTONS | BUTTON_B | BUTTON_X | BUTTON_Y)
|
||||
#define SINGLE_PAYLOAD_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)
|
||||
#define NTRBOOT_BUTTONS (BUTTON_START | BUTTON_SELECT | BUTTON_X)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "strings.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
@@ -78,25 +79,25 @@ void writeConfig(bool isConfigOptions)
|
||||
|
||||
void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
{
|
||||
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
||||
static const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
||||
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"Splash: Off( ) Before( ) After( ) payloads",
|
||||
"Splash duration: 1( ) 3( ) 5( ) 7( ) seconds",
|
||||
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )",
|
||||
};
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot EmuNAND",
|
||||
static const char *singleOptionsText[] = { "( ) Autoboot EmuNAND",
|
||||
"( ) Use EmuNAND FIRM if booting with R",
|
||||
"( ) Enable loading external FIRMs and modules",
|
||||
"( ) Enable game patching",
|
||||
"( ) Show NAND or user string in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Patch ARM9 access",
|
||||
"( ) Set developer UNITINFO",
|
||||
"( ) Disable ARM11 exception handlers",
|
||||
};
|
||||
|
||||
const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
||||
static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n"
|
||||
"It will be booted when no\n"
|
||||
"directional pad buttons are pressed.",
|
||||
|
||||
@@ -110,6 +111,11 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
"\t* 'After payloads' displays it\n"
|
||||
"afterwards.",
|
||||
|
||||
"Select how long the splash screen\n"
|
||||
"displays.\n\n"
|
||||
"This has no effect if the splash\n"
|
||||
"screen is not enabled.",
|
||||
|
||||
"Activate a PIN lock.\n\n"
|
||||
"The PIN will be asked each time\n"
|
||||
"Luma3DS boots.\n\n"
|
||||
@@ -152,7 +158,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
|
||||
"Enable overriding the region and\n"
|
||||
"language configuration and the usage\n"
|
||||
"of patched code binaries,\n"
|
||||
"of patched code binaries, exHeaders,\n"
|
||||
"IPS code patches and LayeredFS\n"
|
||||
"for specific games.\n\n"
|
||||
"Also makes certain DLCs\n"
|
||||
@@ -174,10 +180,6 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
"Enable showing the GBA boot screen\n"
|
||||
"when booting GBA games.",
|
||||
|
||||
"Disable ARM9 exheader access checks.\n\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"
|
||||
@@ -200,11 +202,12 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
u32 enabled;
|
||||
bool visible;
|
||||
} multiOptions[] = {
|
||||
{ .posXs = {19, 24, 29, 34}, .visible = isSdMode },
|
||||
{ .posXs = {21, 26, 31, 36}, .visible = true },
|
||||
{ .posXs = {12, 22, 31, 0}, .visible = true },
|
||||
{ .posXs = {14, 19, 24, 29}, .visible = true },
|
||||
{ .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
|
||||
{ .visible = isSdMode },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = ISN3DS },
|
||||
};
|
||||
|
||||
struct singleOption {
|
||||
@@ -219,7 +222,6 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true },
|
||||
{ .visible = true }
|
||||
};
|
||||
|
||||
@@ -233,14 +235,28 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
|
||||
//Parse the existing options
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
{
|
||||
//Detect the positions where the "x" should go
|
||||
u32 optionNum = 0;
|
||||
for(u32 j = 0; optionNum < 4 && j < strlen(multiOptionsText[i]); j++)
|
||||
if(multiOptionsText[i][j] == '(') multiOptions[i].posXs[optionNum++] = j + 1;
|
||||
while(optionNum < 4) multiOptions[i].posXs[optionNum++] = 0;
|
||||
|
||||
multiOptions[i].enabled = MULTICONFIG(i);
|
||||
}
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
singleOptions[i].enabled = CONFIG(i);
|
||||
|
||||
initScreens();
|
||||
|
||||
static const char *bootTypes[] = { "B9S",
|
||||
"B9S (ntrboot)",
|
||||
"FIRM0",
|
||||
"FIRM1" };
|
||||
|
||||
drawString(true, 10, 10, COLOR_TITLE, CONFIG_TITLE);
|
||||
drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press A to select, START to save");
|
||||
drawFormattedString(false, 10, SCREEN_HEIGHT - 2 * SPACING_Y, COLOR_YELLOW, "Booted from %s via %s", isSdMode ? "SD" : "CTRNAND", bootTypes[(u32)bootType]);
|
||||
|
||||
//Character to display a selected option
|
||||
char selected = 'x';
|
||||
@@ -284,9 +300,9 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput(true);
|
||||
pressed = waitInput(true) & MENU_BUTTONS;
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
while(!pressed);
|
||||
|
||||
if(pressed == BUTTON_START) break;
|
||||
|
||||
@@ -389,12 +405,16 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
configData.config |= (singleOptions[i].enabled ? 1 : 0) << i;
|
||||
|
||||
writeConfig(true);
|
||||
|
||||
u32 newPinMode = MULTICONFIG(PIN);
|
||||
|
||||
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
|
||||
else if(oldPinStatus) fileDelete(PIN_FILE);
|
||||
|
||||
writeConfig(true);
|
||||
else if(oldPinStatus)
|
||||
{
|
||||
if(!fileDelete(PIN_FILE))
|
||||
error("Unable to delete PIN file");
|
||||
}
|
||||
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
wait(2000ULL);
|
||||
|
||||
@@ -34,17 +34,19 @@
|
||||
|
||||
#define CONFIG_FILE "config.bin"
|
||||
#define CONFIG_VERSIONMAJOR 2
|
||||
#define CONFIG_VERSIONMINOR 0
|
||||
#define CONFIG_VERSIONMINOR 3
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
|
||||
#define BOOTCFG_NTRCARDBOOT BOOTCONFIG(7, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
SPLASH_DURATION,
|
||||
PIN,
|
||||
NEWCPU
|
||||
};
|
||||
@@ -57,7 +59,6 @@ enum singleOptions
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
DISABLEARM11EXCHANDLERS
|
||||
};
|
||||
|
||||
@@ -327,7 +327,7 @@ __attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart = 0;
|
||||
|
||||
FirmwareSource firmSource;
|
||||
FirmwareSource firmSource = FIRMWARE_SYSNAND;
|
||||
|
||||
__attribute__((aligned(4))) static const u8 key1s[2][AES_BLOCK_SIZE] = {
|
||||
{0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "fs.h"
|
||||
#include "fmt.h"
|
||||
#include "font.h"
|
||||
#include "config.h"
|
||||
|
||||
bool loadSplash(void)
|
||||
{
|
||||
@@ -49,7 +50,6 @@ bool loadSplash(void)
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
initScreens();
|
||||
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;
|
||||
@@ -57,7 +57,9 @@ bool loadSplash(void)
|
||||
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||
|
||||
swapFramebuffers(true);
|
||||
wait(3000ULL);
|
||||
|
||||
u32 durationIndex = MULTICONFIG(SPLASH_DURATION);
|
||||
wait(1000ULL + (durationIndex * 2000ULL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
* Code for locating the SDMMC struct by Normmatt
|
||||
*/
|
||||
|
||||
|
||||
#include "emunand.h"
|
||||
#include "memory.h"
|
||||
#include "utils.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
|
||||
@@ -32,11 +32,12 @@
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "fmt.h"
|
||||
#include "buttons.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
void installArm9Handlers(void)
|
||||
{
|
||||
memcpy((void *)0x01FF8000, arm9_exceptions_bin + 32, arm9_exceptions_bin_size - 32);
|
||||
memcpy((void *)0x01FF8000, arm9_exceptions_bin, arm9_exceptions_bin_size);
|
||||
|
||||
/* IRQHandler is at 0x08000000, but we won't handle it for some reasons
|
||||
svcHandler is at 0x08000010, but we won't handle svc either */
|
||||
@@ -46,8 +47,10 @@ void installArm9Handlers(void)
|
||||
for(u32 i = 0; i < 4; i++)
|
||||
{
|
||||
*(vu32 *)(0x08000000 + offsets[i]) = 0xE51FF004;
|
||||
*(vu32 *)(0x08000000 + offsets[i] + 4) = *((u32 *)arm9_exceptions_bin + 1 + i);
|
||||
*(vu32 *)(0x08000000 + offsets[i] + 4) = *(vu32 *)(0x01FF8008 + 4 * i);
|
||||
}
|
||||
|
||||
*(vu32 *)0x01FF8004 = 0; //BreakPtr
|
||||
}
|
||||
|
||||
void detectAndProcessExceptionDumps(void)
|
||||
@@ -69,6 +72,18 @@ void detectAndProcessExceptionDumps(void)
|
||||
*registerNames[] = {
|
||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
|
||||
"SP", "LR", "PC", "CPSR", "FPEXC"
|
||||
},
|
||||
*faultStatusNames[] = {
|
||||
"Alignment", "Instr.cache maintenance op.",
|
||||
"Ext.Abort on translation - Lv1", "Ext.Abort on translation - Lv2",
|
||||
"Translation - Section", "Translation - Page", "Access bit - Section", "Access bit - Page",
|
||||
"Domain - Section", "Domain - Page", "Permission - Section", "Permission - Page",
|
||||
"Precise External Abort", "Imprecise External Abort", "Debug event"
|
||||
};
|
||||
|
||||
static const u32 faultStatusValues[] = {
|
||||
0b1, 0b100, 0b1100, 0b1110, 0b101, 0b111, 0b11, 0b110, 0b1001, 0b1011, 0b1101,
|
||||
0b1111, 0b1000, 0b10110, 0b10
|
||||
};
|
||||
|
||||
initScreens();
|
||||
@@ -98,11 +113,25 @@ void detectAndProcessExceptionDumps(void)
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
}
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
}
|
||||
else
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]);
|
||||
|
||||
if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0)
|
||||
if(dumpHeader->processor == 11 && dumpHeader->type >= 2)
|
||||
{
|
||||
u32 xfsr = (dumpHeader->type == 2 ? regs[18] : regs[17]) & 0xF;
|
||||
|
||||
for(u32 i = 0; i < 15; i++)
|
||||
if(xfsr == faultStatusValues[i])
|
||||
{
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Fault status: %s", faultStatusNames[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dumpHeader->additionalDataSize != 0)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE,
|
||||
"Current process: %.8s (%016llX)", (const char *)additionalData, *(vu64 *)(additionalData + 8));
|
||||
posY += SPACING_Y;
|
||||
@@ -117,6 +146,9 @@ void detectAndProcessExceptionDumps(void)
|
||||
posY = drawFormattedString(true, 10 + 22 * SPACING_X, posY, COLOR_WHITE, "%-7s%08X", registerNames[i + 1], regs[20]);
|
||||
}
|
||||
|
||||
if(dumpHeader->processor == 11 && dumpHeader->type == 3)
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "%-7s%08X Access type: %s", "FAR", regs[19], regs[17] & (1u << 11) ? "Write" : "Read");
|
||||
|
||||
posY += SPACING_Y;
|
||||
|
||||
u32 mode = regs[16] & 0xF;
|
||||
@@ -133,6 +165,16 @@ void detectAndProcessExceptionDumps(void)
|
||||
drawFormattedString(false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE, "%02X", *stackDump);
|
||||
}
|
||||
|
||||
static const char *choiceMessage[] = {"Press A to save the crash dump", "Press any other button to shutdown"};
|
||||
|
||||
drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, choiceMessage[0]);
|
||||
drawString(true, 10, posY + SPACING_Y + SPACING_Y , COLOR_WHITE, choiceMessage[1]);
|
||||
|
||||
if(waitInput(false) != BUTTON_A) goto exit;
|
||||
|
||||
drawString(true, 10, posY + SPACING_Y, COLOR_BLACK, choiceMessage[0]);
|
||||
drawString(true, 10, posY + SPACING_Y + SPACING_Y , COLOR_BLACK, choiceMessage[1]);
|
||||
|
||||
char folderPath[12],
|
||||
path[36],
|
||||
fileName[24];
|
||||
@@ -143,15 +185,16 @@ void detectAndProcessExceptionDumps(void)
|
||||
|
||||
if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize))
|
||||
{
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "You can find a dump in the following file:");
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, path) + SPACING_Y;
|
||||
posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "You can find the dump in the following file:");
|
||||
posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "%s:/luma/%s", isSdMode ? "SD" : "CTRNAND", path) + SPACING_Y;
|
||||
}
|
||||
else posY = drawString(true, 10, posY + SPACING_Y, COLOR_RED, "Error writing the dump file");
|
||||
|
||||
drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Press any button to shutdown");
|
||||
|
||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||
|
||||
waitInput(false);
|
||||
|
||||
exit:
|
||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||
mcuPowerOff();
|
||||
}
|
||||
|
||||
@@ -286,3 +286,17 @@ R0.12c (March 04, 2017)
|
||||
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)
|
||||
|
||||
|
||||
|
||||
R0.13 (May 21, 2017)
|
||||
|
||||
Changed heading character of configuration keywords "_" to "FF_".
|
||||
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
|
||||
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
|
||||
Improved cluster allocation time on stretch a deep buried cluster chain.
|
||||
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
|
||||
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
|
||||
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
|
||||
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
|
||||
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FatFs Module Source Files R0.12c
|
||||
FatFs Module Source Files R0.13
|
||||
|
||||
|
||||
FILES
|
||||
@@ -11,11 +11,12 @@ FILES
|
||||
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 modules.
|
||||
ffunicode.c Optional Unicode utility functions.
|
||||
ffsystem.c An example of optional O/S related functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
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
|
||||
storage device. You need to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ DRESULT disk_write (
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) ||
|
||||
return ((pdrv == SDCARD && (*(vu16 *)(SDMMC_BASE + REG_SDSTATUS0) & TMIO_STAT0_WRPROTECT) != 0 && !sdmmc_sdcard_writesectors(sector, count, buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandWrite(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
@@ -91,12 +91,11 @@ DRESULT disk_write (
|
||||
DRESULT disk_ioctl (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
__attribute__((unused))
|
||||
BYTE cmd, /* Control code */
|
||||
__attribute__((unused))
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
return RES_PARERR;
|
||||
return cmd == CTRL_SYNC ? RES_OK : RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
2296
source/fatfs/ff.c
2296
source/fatfs/ff.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12c /
|
||||
/ FatFs - Generic FAT Filesystem module R0.13 /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2017, ChaN, all right reserved.
|
||||
@@ -15,11 +15,12 @@
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 68300 /* Revision ID */
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 87030 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -28,7 +29,7 @@ extern "C" {
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if _FATFS != _FFCONF
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
@@ -36,7 +37,7 @@ extern "C" {
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
@@ -48,20 +49,19 @@ extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode (UTF-16) string */
|
||||
#if _USE_LFN == 0
|
||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||
#endif
|
||||
#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode (UTF-16) string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#define _INC_TCHAR
|
||||
#endif
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#define _INC_TCHAR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -69,8 +69,8 @@ typedef char TCHAR;
|
||||
|
||||
/* Type of file size variables */
|
||||
|
||||
#if _FS_EXFAT
|
||||
#if _USE_LFN == 0
|
||||
#if FF_FS_EXFAT
|
||||
#if !FF_USE_LFN
|
||||
#error LFN must be enabled when enable exFAT
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
@@ -80,36 +80,36 @@ typedef DWORD FSIZE_t;
|
||||
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* File system type (0:N/A) */
|
||||
BYTE drv; /* Physical drive number */
|
||||
BYTE fs_type; /* Filesystem type (0:N/A) */
|
||||
BYTE pdrv; /* Physical drive number */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* File system mount ID */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if _MAX_SS != _MIN_SS
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _USE_LFN != 0
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if _FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#if FF_FS_REENTRANT
|
||||
FF_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH != 0
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if _FS_EXFAT
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
@@ -122,52 +122,52 @@ typedef struct {
|
||||
DWORD dirbase; /* Root directory base sector/cluster */
|
||||
DWORD database; /* Data base sector */
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (_FDID) */
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
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) */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:flagmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data 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 first fragment, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (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 and non-directory object) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} _FDID;
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
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 fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
|
||||
#if !FF_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
@@ -176,16 +176,16 @@ typedef struct {
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
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
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if _USE_FIND
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
@@ -199,9 +199,9 @@ typedef struct {
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if _USE_LFN != 0
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[13]; /* Altenative file name */
|
||||
TCHAR fname[_MAX_LFN + 1]; /* Primary file name */
|
||||
TCHAR fname[FF_MAX_LFN + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[13]; /* File name */
|
||||
#endif
|
||||
@@ -230,7 +230,7 @@ typedef enum {
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
@@ -268,6 +268,7 @@ FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
@@ -280,6 +281,7 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
@@ -292,26 +294,27 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY && !_FS_NORTC
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* Unicode support functions */
|
||||
#if _USE_LFN != 0 /* Unicode - OEM code conversion */
|
||||
WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */
|
||||
WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */
|
||||
#if _USE_LFN == 3 /* Memory functions */
|
||||
/* LFN support functions */
|
||||
#if FF_USE_LFN /* Code conversion (defined in unicode.c) */
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (WCHAR uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
WCHAR ff_wtoupper (WCHAR uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if _FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||
#if FF_FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -358,4 +361,4 @@ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
#endif /* FF_DEFINED */
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file
|
||||
/ FatFs - Configuration file
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 68300 /* Revision ID */
|
||||
#define FFCONF_DEF 87030 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_READONLY 0
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define _FS_MINIMIZE 0
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
@@ -25,43 +25,42 @@
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define _USE_STRFUNC 0
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
#define FF_USE_STRFUNC 0
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _USE_FIND 1
|
||||
#define FF_USE_FIND 1
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define _USE_MKFS 0
|
||||
#define FF_USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
#define FF_USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_EXPAND 0
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_CHMOD 0
|
||||
#define FF_USE_CHMOD 0
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
@@ -69,11 +68,10 @@
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 437
|
||||
#define FF_CODE_PAGE 437
|
||||
/* 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.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No support of extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
@@ -95,47 +93,50 @@
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 2
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN switches the support of long file name (LFN).
|
||||
#define FF_USE_LFN 2
|
||||
#define FF_MAX_LFN 255
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable support of LFN. _MAX_LFN has no effect.
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added
|
||||
/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255.
|
||||
/ to the project. The working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional 608 bytes at exFAT enabled. FF_MAX_LFN can be in range from 12 to 255.
|
||||
/ It should be set 255 to support full featured LFN operations.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree(), must be added to the project. */
|
||||
|
||||
|
||||
#define _LFN_UNICODE 0
|
||||
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16)
|
||||
/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1.
|
||||
/ This option also affects behavior of string I/O functions. */
|
||||
#define FF_LFN_UNICODE 0
|
||||
/* This option switches character encoding on the API, 0:ANSI/OEM or 1:UTF-16,
|
||||
/ when LFN is enabled. Also behavior of string I/O functions will be affected by
|
||||
/ this option. When LFN is not enabled, this option has no effect.
|
||||
*/
|
||||
|
||||
|
||||
#define _STRF_ENCODE 3
|
||||
/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to
|
||||
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* When FF_LFN_UNICODE = 1 with LFN enabled, string I/O functions, f_gets(),
|
||||
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
|
||||
/ This option selects assumption of character encoding ON THE FILE to be
|
||||
/ read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ This option has no effect when _LFN_UNICODE == 0. */
|
||||
*/
|
||||
|
||||
|
||||
#define _FS_RPATH 1
|
||||
/* This option configures support of relative path.
|
||||
#define FF_FS_RPATH 1
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
@@ -147,45 +148,45 @@
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 2
|
||||
#define FF_VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||
/* _STR_VOLUME_ID switches string support of volume ID.
|
||||
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||
/* FF_STR_VOLUME_ID switches string support for volume ID.
|
||||
/ When FF_STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the drive ID strings for each
|
||||
/ logical drives. Number of items must be equal to FF_VOLUMES. Valid characters for
|
||||
/ the drive ID strings are: A-Z and 0-9. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0
|
||||
/* This option switches support of multi-partition on a physical drive.
|
||||
#define FF_MULTI_PARTITION 0
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When multi-partition is enabled (1), each logical drive number can be bound to
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
|
||||
#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, generic memory card and
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command needs to be implemented to
|
||||
/ the disk_ioctl() function. */
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable)
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _FS_NOFSINFO 0
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
@@ -202,36 +203,36 @@
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes.
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the file system object (FATFS) is used for the file data transfer. */
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#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)
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ When enable exFAT, also LFN needs to be enabled.
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 1
|
||||
#define _NORTC_MDAY 1
|
||||
#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
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 5
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2017
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to get current time form real-time clock. _NORTC_MON,
|
||||
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (_FS_READONLY = 1). */
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define _FS_LOCK 0
|
||||
/* The option _FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||
#define FF_FS_LOCK 0
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
@@ -241,23 +242,23 @@
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define _FS_REENTRANT 0
|
||||
#define _FS_TIMEOUT 1000
|
||||
#define _SYNC_t HANDLE
|
||||
/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
#define FF_SYNC_t HANDLE
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The _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*,
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
|
||||
171
source/fatfs/ffsystem.c
Normal file
171
source/fatfs/ffsystem.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample code of OS dependent controls for FatFs */
|
||||
/* (C)ChaN, 2017 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
//const osMutexDef_t Mutex[FF_VOLUMES]; /* CMSIS-RTOS */
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* uITRON */
|
||||
// T_CSEM csem = {TA_TPRI,1,1};
|
||||
// *sobj = acre_sem(&csem);
|
||||
// return (int)(*sobj > 0);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// *sobj = OSMutexCreate(0, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// *sobj = xSemaphoreCreateMutex();
|
||||
// return (int)(*sobj != NULL);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// *sobj = osMutexCreate(Mutex + vol);
|
||||
// return (int)(*sobj != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)CloseHandle(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(del_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// vSemaphoreDelete(sobj);
|
||||
// return 1;
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexDelete(sobj) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(wai_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
ReleaseMutex(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// sig_sem(sobj);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OSMutexPost(sobj);
|
||||
|
||||
/* FreeRTOS */
|
||||
// xSemaphoreGive(sobj);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// osMutexRelease(sobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
15566
source/fatfs/ffunicode.c
Normal file
15566
source/fatfs/ffunicode.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,8 @@
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
#ifndef FF_INTEGER
|
||||
#define FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Unicode - Local code bidirectional converter (C)ChaN, 2015 */
|
||||
/* (SBCS code pages) */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* 437 U.S.
|
||||
/ 720 Arabic
|
||||
/ 737 Greek
|
||||
/ 771 KBL
|
||||
/ 775 Baltic
|
||||
/ 850 Latin 1
|
||||
/ 852 Latin 2
|
||||
/ 855 Cyrillic
|
||||
/ 857 Turkish
|
||||
/ 860 Portuguese
|
||||
/ 861 Icelandic
|
||||
/ 862 Hebrew
|
||||
/ 863 Canadian French
|
||||
/ 864 Arabic
|
||||
/ 865 Nordic
|
||||
/ 866 Russian
|
||||
/ 869 Greek 2
|
||||
*/
|
||||
|
||||
#include "../ff.h"
|
||||
|
||||
|
||||
#if _CODE_PAGE == 437
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 720
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */
|
||||
0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
|
||||
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
|
||||
0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 737
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */
|
||||
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
|
||||
0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
|
||||
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
|
||||
0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 771
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */
|
||||
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D,
|
||||
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||
0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 775
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */
|
||||
0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
|
||||
0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
|
||||
0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
|
||||
0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 850
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
|
||||
0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 852
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
|
||||
0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
|
||||
0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 855
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */
|
||||
0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
|
||||
0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
|
||||
0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
|
||||
0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
|
||||
0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 857
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
|
||||
0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
|
||||
0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
|
||||
0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 860
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
|
||||
0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 861
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
|
||||
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 862
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */
|
||||
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
|
||||
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 863
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
|
||||
0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
|
||||
0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 864
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */
|
||||
0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
|
||||
0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
|
||||
0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
|
||||
0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F,
|
||||
0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9,
|
||||
0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9,
|
||||
0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1,
|
||||
0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 865
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */
|
||||
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
|
||||
0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
|
||||
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
|
||||
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 866
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */
|
||||
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
|
||||
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
|
||||
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
|
||||
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
|
||||
0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#elif _CODE_PAGE == 869
|
||||
#define _TBLDEF 1
|
||||
static
|
||||
const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */
|
||||
0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
|
||||
0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
|
||||
0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510,
|
||||
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3,
|
||||
0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580,
|
||||
0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384,
|
||||
0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !_TBLDEF || !_USE_LFN
|
||||
#error This file is not needed at current configuration. Remove from the project.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
WCHAR ff_convert ( /* Converted character, Returns zero on error */
|
||||
WCHAR chr, /* Character code to be converted */
|
||||
UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */
|
||||
)
|
||||
{
|
||||
WCHAR c;
|
||||
|
||||
|
||||
if (chr < 0x80) { /* ASCII */
|
||||
c = chr;
|
||||
|
||||
} else {
|
||||
if (dir) { /* OEM code to Unicode */
|
||||
c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80];
|
||||
|
||||
} else { /* Unicode to OEM code */
|
||||
for (c = 0; c < 0x80; c++) {
|
||||
if (chr == Tbl[c]) break;
|
||||
}
|
||||
c = (c + 0x80) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WCHAR ff_wtoupper ( /* Returns upper converted character */
|
||||
WCHAR chr /* Unicode character to be upper converted (BMP only) */
|
||||
)
|
||||
{
|
||||
/* Compressed upper conversion table */
|
||||
static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */
|
||||
/* Basic Latin */
|
||||
0x0061,0x031A,
|
||||
/* Latin-1 Supplement */
|
||||
0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178,
|
||||
/* Latin Extended-A */
|
||||
0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106,
|
||||
/* Latin Extended-B */
|
||||
0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
|
||||
0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128,
|
||||
0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A,
|
||||
/* IPA Extensions */
|
||||
0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
|
||||
/* Greek, Coptic */
|
||||
0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311,
|
||||
0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118,
|
||||
0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
|
||||
/* Cyrillic */
|
||||
0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144,
|
||||
/* Armenian */
|
||||
0x0561,0x0426,
|
||||
|
||||
0x0000
|
||||
};
|
||||
static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */
|
||||
/* Phonetic Extensions */
|
||||
0x1D7D,0x0001,0x2C63,
|
||||
/* Latin Extended Additional */
|
||||
0x1E00,0x0196, 0x1EA0,0x015A,
|
||||
/* Greek Extended */
|
||||
0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606,
|
||||
0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608,
|
||||
0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
|
||||
0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
|
||||
0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC,
|
||||
/* Letterlike Symbols */
|
||||
0x214E,0x0001,0x2132,
|
||||
/* Number forms */
|
||||
0x2170,0x0210, 0x2184,0x0001,0x2183,
|
||||
/* Enclosed Alphanumerics */
|
||||
0x24D0,0x051A, 0x2C30,0x042F,
|
||||
/* Latin Extended-C */
|
||||
0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102,
|
||||
/* Coptic */
|
||||
0x2C80,0x0164,
|
||||
/* Georgian Supplement */
|
||||
0x2D00,0x0826,
|
||||
/* Full-width */
|
||||
0xFF41,0x031A,
|
||||
|
||||
0x0000
|
||||
};
|
||||
const WCHAR *p;
|
||||
WCHAR bc, nc, cmd;
|
||||
|
||||
|
||||
p = chr < 0x1000 ? cvt1 : cvt2;
|
||||
for (;;) {
|
||||
bc = *p++; /* Get block base */
|
||||
if (!bc || chr < bc) break;
|
||||
nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
|
||||
if (chr < bc + nc) { /* In the block? */
|
||||
switch (cmd) {
|
||||
case 0: chr = p[chr - bc]; break; /* Table conversion */
|
||||
case 1: chr -= (chr - bc) & 1; break; /* Case pairs */
|
||||
case 2: chr -= 16; break; /* Shift -16 */
|
||||
case 3: chr -= 32; break; /* Shift -32 */
|
||||
case 4: chr -= 48; break; /* Shift -48 */
|
||||
case 5: chr -= 26; break; /* Shift -26 */
|
||||
case 6: chr += 8; break; /* Shift +8 */
|
||||
case 7: chr -= 80; break; /* Shift -80 */
|
||||
case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!cmd) p += nc;
|
||||
}
|
||||
|
||||
return chr;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ static bool checkFirm(u32 firmSize)
|
||||
|
||||
static inline u32 loadFirmFromStorage(FirmwareType firmType)
|
||||
{
|
||||
const char *firmwareFiles[] = {
|
||||
static const char *firmwareFiles[] = {
|
||||
"native.firm",
|
||||
"twl.firm",
|
||||
"agb.firm",
|
||||
@@ -123,7 +123,7 @@ static inline u32 loadFirmFromStorage(FirmwareType firmType)
|
||||
"cetk_sysupdater"
|
||||
};
|
||||
|
||||
u32 firmSize = fileRead(firm, firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
||||
u32 firmSize = fileRead(firm, firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
||||
|
||||
if(!firmSize) return 0;
|
||||
|
||||
@@ -137,7 +137,7 @@ static inline u32 loadFirmFromStorage(FirmwareType firmType)
|
||||
|
||||
u8 cetk[0xA50];
|
||||
|
||||
if(fileRead(cetk, firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk))
|
||||
if(fileRead(cetk, cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk))
|
||||
error("The cetk is missing or corrupted.");
|
||||
|
||||
firmSize = decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize);
|
||||
@@ -152,16 +152,42 @@ static inline u32 loadFirmFromStorage(FirmwareType firmType)
|
||||
|
||||
u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
|
||||
{
|
||||
u32 firmVersion,
|
||||
firmSize;
|
||||
|
||||
bool ctrNandError = isSdMode && !mountFs(false, false);
|
||||
|
||||
if(!ctrNandError)
|
||||
{
|
||||
//Load FIRM from CTRNAND
|
||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||
firmVersion = firmRead(firm, (u32)*firmType);
|
||||
|
||||
if(firmVersion == 0xFFFFFFFF) error("Failed to get the CTRNAND FIRM.");
|
||||
if(firmVersion == 0xFFFFFFFF) ctrNandError = true;
|
||||
else
|
||||
{
|
||||
firmSize = decryptExeFs((Cxi *)firm);
|
||||
|
||||
u32 firmSize = decryptExeFs((Cxi *)firm);
|
||||
if(!firmSize || !checkFirm(firmSize)) ctrNandError = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!firmSize) error("Failed to decrypt the CTRNAND FIRM.");
|
||||
bool loadedFromStorage = false;
|
||||
|
||||
if(!checkFirm(firmSize)) error("The CTRNAND FIRM is invalid or corrupted.");
|
||||
if(loadFromStorage || ctrNandError)
|
||||
{
|
||||
u32 result = loadFirmFromStorage(*firmType);
|
||||
|
||||
if(result != 0)
|
||||
{
|
||||
loadedFromStorage = true;
|
||||
firmSize = result;
|
||||
}
|
||||
else if(ctrNandError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one.");
|
||||
}
|
||||
|
||||
//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 %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND");
|
||||
|
||||
if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000)
|
||||
{
|
||||
@@ -173,23 +199,6 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF
|
||||
*firmType = NATIVE_FIRM1X2X;
|
||||
}
|
||||
|
||||
bool loadedFromStorage = false;
|
||||
|
||||
if(loadFromStorage)
|
||||
{
|
||||
u32 result = loadFirmFromStorage(*firmType);
|
||||
|
||||
if(result != 0)
|
||||
{
|
||||
loadedFromStorage = true;
|
||||
firmSize = result;
|
||||
}
|
||||
}
|
||||
|
||||
//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 %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND");
|
||||
|
||||
if(loadedFromStorage || ISDEVUNIT)
|
||||
{
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
@@ -335,7 +344,7 @@ static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool lo
|
||||
}
|
||||
}
|
||||
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode, bool doUnitinfoPatch)
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isFirmProtEnabled, bool isSafeMode, bool doUnitinfoPatch)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
|
||||
*arm11Section1 = (u8 *)firm + firm->section[1].offset;
|
||||
@@ -376,7 +385,7 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora
|
||||
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, firm->section[2].address, firmVersion);
|
||||
|
||||
//Apply FIRM0/1 writes patches on SysNAND to protect A9LH
|
||||
else ret += patchFirmWrites(process9Offset, process9Size);
|
||||
else if(isFirmProtEnabled) ret += patchFirmWrites(process9Offset, process9Size);
|
||||
|
||||
//Apply firmlaunch patches
|
||||
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
||||
@@ -403,7 +412,7 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora
|
||||
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
|
||||
ret += patchKernel9Panic(arm9Section, kernel9Size);
|
||||
|
||||
if(CONFIG(PATCHACCESS)) ret += patchP9AccessChecks(process9Offset, process9Size);
|
||||
ret += patchP9AccessChecks(process9Offset, process9Size);
|
||||
|
||||
mergeSection0(NATIVE_FIRM, firmVersion, loadFromStorage);
|
||||
firm->section[0].size = 0;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode);
|
||||
void loadHomebrewFirm(u32 pressed);
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode, bool doUnitinfoPatch);
|
||||
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isFirmProtEnabled, bool isSafeMode, bool doUnitinfoPatch);
|
||||
u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch);
|
||||
u32 patchAgbFirm(bool loadFromStorage, bool doUnitinfoPatch);
|
||||
u32 patch1x2xNativeAndSafeFirm(void);
|
||||
|
||||
@@ -263,7 +263,7 @@ u32 vsprintf(char *buf, const char *fmt, va_list args)
|
||||
//Integer number formats - set up the flags and "break"
|
||||
case 'X':
|
||||
flags |= UPPERCASE;
|
||||
|
||||
//Falls through
|
||||
case 'x':
|
||||
isHex = true;
|
||||
break;
|
||||
@@ -271,7 +271,7 @@ u32 vsprintf(char *buf, const char *fmt, va_list args)
|
||||
case 'd':
|
||||
case 'i':
|
||||
flags |= SIGN;
|
||||
|
||||
//Falls through
|
||||
case 'u':
|
||||
isHex = false;
|
||||
break;
|
||||
|
||||
41
source/fs.c
41
source/fs.c
@@ -51,8 +51,7 @@ static bool switchToMainDir(bool isSd)
|
||||
case FR_OK:
|
||||
return true;
|
||||
case FR_NO_PATH:
|
||||
f_mkdir(mainDir);
|
||||
return switchToMainDir(isSd);
|
||||
return f_mkdir(mainDir) == FR_OK && switchToMainDir(isSd);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -67,6 +66,7 @@ bool mountFs(bool isSd, bool switchToCtrNand)
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
{
|
||||
FIL file;
|
||||
FRESULT result = FR_OK;
|
||||
u32 ret = 0;
|
||||
|
||||
if(f_open(&file, path, FA_READ) != FR_OK) return ret;
|
||||
@@ -74,10 +74,10 @@ u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
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);
|
||||
result = f_read(&file, dest, size, (unsigned int *)&ret);
|
||||
result |= f_close(&file);
|
||||
|
||||
return ret;
|
||||
return result == FR_OK ? ret : 0;
|
||||
}
|
||||
|
||||
u32 getFileSize(const char *path)
|
||||
@@ -88,17 +88,18 @@ u32 getFileSize(const char *path)
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
FIL file;
|
||||
FRESULT result;
|
||||
|
||||
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||
{
|
||||
case FR_OK:
|
||||
{
|
||||
unsigned int written;
|
||||
f_write(&file, buffer, size, &written);
|
||||
f_truncate(&file);
|
||||
f_close(&file);
|
||||
result = f_write(&file, buffer, size, &written);
|
||||
if(result == FR_OK) result = f_truncate(&file);
|
||||
result |= f_close(&file);
|
||||
|
||||
return (u32)written == size;
|
||||
return result == FR_OK && (u32)written == size;
|
||||
}
|
||||
case FR_NO_PATH:
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
@@ -107,18 +108,18 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
char folder[i + 1];
|
||||
memcpy(folder, path, i);
|
||||
folder[i] = 0;
|
||||
f_mkdir(folder);
|
||||
result = f_mkdir(folder);
|
||||
}
|
||||
|
||||
return fileWrite(buffer, path, size);
|
||||
return result == FR_OK && fileWrite(buffer, path, size);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void fileDelete(const char *path)
|
||||
bool fileDelete(const char *path)
|
||||
{
|
||||
f_unlink(path);
|
||||
return f_unlink(path) == FR_OK;
|
||||
}
|
||||
|
||||
bool findPayload(char *path, u32 pressed)
|
||||
@@ -181,9 +182,7 @@ bool payloadMenu(char *path)
|
||||
payloadNum++;
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(!payloadNum) return false;
|
||||
if(f_closedir(&dir) != FR_OK || !payloadNum) return false;
|
||||
|
||||
u32 pressed = 0,
|
||||
selectedPayload = 0;
|
||||
@@ -205,9 +204,9 @@ bool payloadMenu(char *path)
|
||||
{
|
||||
do
|
||||
{
|
||||
pressed = waitInput(true);
|
||||
pressed = waitInput(true) & MENU_BUTTONS;
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
while(!pressed);
|
||||
|
||||
u32 oldSelectedPayload = selectedPayload;
|
||||
|
||||
@@ -251,7 +250,7 @@ bool payloadMenu(char *path)
|
||||
|
||||
u32 firmRead(void *dest, u32 firmType)
|
||||
{
|
||||
const char *firmFolders[][2] = {{"00000002", "20000002"},
|
||||
static const char *firmFolders[][2] = {{"00000002", "20000002"},
|
||||
{"00000102", "20000102"},
|
||||
{"00000202", "20000202"},
|
||||
{"00000003", "20000003"},
|
||||
@@ -281,9 +280,7 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(firmVersion == 0xFFFFFFFF) goto exit;
|
||||
if(f_closedir(&dir) != FR_OK || firmVersion == 0xFFFFFFFF) goto exit;
|
||||
|
||||
//Complete the string with the .app name
|
||||
sprintf(path, "%s/%08x.app", folderPath, firmVersion);
|
||||
|
||||
@@ -34,7 +34,7 @@ bool mountFs(bool isSd, bool switchToCtrNand);
|
||||
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);
|
||||
bool fileDelete(const char *path);
|
||||
bool findPayload(char *path, u32 pressed);
|
||||
bool payloadMenu(char *path);
|
||||
u32 firmRead(void *dest, u32 firmType);
|
||||
|
||||
@@ -37,24 +37,34 @@
|
||||
#include "crypto.h"
|
||||
#include "memory.h"
|
||||
#include "screen.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
|
||||
extern CfgData configData;
|
||||
extern ConfigurationStatus needConfig;
|
||||
extern FirmwareSource firmSource;
|
||||
|
||||
bool isFirmlaunch = false,
|
||||
isSdMode;
|
||||
bool isSdMode;
|
||||
u16 launchedPath[41];
|
||||
BootType bootType;
|
||||
|
||||
void main(int argc, char **argv, u32 magicWord)
|
||||
{
|
||||
bool isSafeMode = false,
|
||||
isNoForceFlagSet = false;
|
||||
bool isFirmProtEnabled,
|
||||
isSafeMode = false,
|
||||
isNoForceFlagSet = false,
|
||||
isNtrBoot;
|
||||
FirmwareType firmType;
|
||||
FirmwareSource nandType;
|
||||
const vu8 *bootMediaStatus = (const vu8 *)0x1FFFE00C;
|
||||
const vu32 *bootPartitionsStatus = (const vu32 *)0x1FFFE010;
|
||||
|
||||
if((magicWord & 0xFFFF) == 0xBEEF && argc >= 1) //Normal boot
|
||||
//Shell closed, no error booting NTRCARD, NAND paritions not even considered
|
||||
isNtrBoot = bootMediaStatus[3] == 2 && !bootMediaStatus[1] && !bootPartitionsStatus[0] && !bootPartitionsStatus[1];
|
||||
|
||||
if((magicWord & 0xFFFF) == 0xBEEF && argc >= 1) //Normal (B9S) boot
|
||||
{
|
||||
bootType = isNtrBoot ? B9SNTR : B9S;
|
||||
|
||||
u32 i;
|
||||
for(i = 0; i < 40 && argv[0][i] != 0; i++) //Copy and convert the path to UTF-16
|
||||
launchedPath[i] = argv[0][i];
|
||||
@@ -62,21 +72,39 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
}
|
||||
else if(magicWord == 0xBABE && argc == 2) //Firmlaunch
|
||||
{
|
||||
bootType = FIRMLAUNCH;
|
||||
|
||||
u32 i;
|
||||
u16 *p = (u16 *)argv[0];
|
||||
for(i = 0; i < 40 && p[i] != 0; i++)
|
||||
launchedPath[i] = p[i];
|
||||
launchedPath[i] = 0;
|
||||
|
||||
isFirmlaunch = true;
|
||||
}
|
||||
else if(magicWord == 0xB002) //FIRM/NTRCARD boot
|
||||
{
|
||||
if(isNtrBoot) bootType = NTR;
|
||||
else
|
||||
{
|
||||
const char *path;
|
||||
if(!((vu8 *)bootPartitionsStatus)[2])
|
||||
{
|
||||
bootType = FIRM0;
|
||||
path = "firm0:";
|
||||
}
|
||||
else
|
||||
{
|
||||
static const char argv[] = "firm0:";
|
||||
for(u32 i = 0; i < sizeof(argv); i++) //Copy and convert the path to UTF-16
|
||||
launchedPath[i] = argv[i];
|
||||
bootType = FIRM1;
|
||||
path = "firm1:";
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 7; i++) //Copy and convert the path to UTF-16
|
||||
launchedPath[i] = path[i];
|
||||
}
|
||||
|
||||
setupKeyslots();
|
||||
}
|
||||
else error("Launched using an unsupported loader.");
|
||||
|
||||
if(memcmp(launchedPath, u"sdmc", 8) == 0)
|
||||
{
|
||||
if(!mountFs(true, false)) error("Failed to mount SD.");
|
||||
@@ -84,17 +112,21 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
}
|
||||
else if(memcmp(launchedPath, u"nand", 8) == 0)
|
||||
{
|
||||
firmSource = FIRMWARE_SYSNAND;
|
||||
if(!mountFs(false, true)) error("Failed to mount CTRNAND.");
|
||||
isSdMode = false;
|
||||
}
|
||||
else if(memcmp(launchedPath, u"firm", 8) == 0)
|
||||
else if(bootType == NTR || memcmp(launchedPath, u"firm", 8) == 0)
|
||||
{
|
||||
setupKeyslots();
|
||||
|
||||
if(mountFs(true, false)) isSdMode = true;
|
||||
else if(mountFs(false, true)) isSdMode = false;
|
||||
else error("Failed to mount SD and CTRNAND.");
|
||||
|
||||
if(bootType == NTR)
|
||||
{
|
||||
while(HID_PAD & NTRBOOT_BUTTONS);
|
||||
loadHomebrewFirm(0);
|
||||
mcuPowerOff();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -112,7 +144,7 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
||||
|
||||
//Determine if this is a firmlaunch boot
|
||||
if(isFirmlaunch)
|
||||
if(bootType == FIRMLAUNCH)
|
||||
{
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
|
||||
|
||||
@@ -131,6 +163,7 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
isFirmProtEnabled = !BOOTCFG_NTRCARDBOOT;
|
||||
|
||||
goto boot;
|
||||
}
|
||||
@@ -139,6 +172,7 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
installArm9Handlers();
|
||||
|
||||
firmType = NATIVE_FIRM;
|
||||
isFirmProtEnabled = bootType != NTR;
|
||||
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
@@ -146,7 +180,6 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
//If it's a MCU reboot, try to force boot options
|
||||
if(CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
|
||||
{
|
||||
|
||||
//Always force a SysNAND boot when quitting AGB_FIRM
|
||||
if(CFG_BOOTENV == 7)
|
||||
{
|
||||
@@ -173,7 +206,7 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
u32 pinMode = MULTICONFIG(PIN);
|
||||
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
||||
|
||||
//If no configuration file exists or SELECT is held, load configuration menu
|
||||
//If no configuration file exists or SELECT is held or if booted from NTRCARD, load configuration menu
|
||||
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT);
|
||||
|
||||
if(shouldLoadConfigMenu)
|
||||
@@ -205,13 +238,15 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
|
||||
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
||||
|
||||
bool autoBootEmu = CONFIG(AUTOBOOTEMU);
|
||||
|
||||
if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START)
|
||||
{
|
||||
loadHomebrewFirm(0);
|
||||
pressed = HID_PAD;
|
||||
}
|
||||
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadHomebrewFirm(pressed);
|
||||
else if((((pressed & SINGLE_PAYLOAD_BUTTONS) || (!autoBootEmu && (pressed & DPAD_BUTTONS))) && !(pressed & (BUTTON_L1 | BUTTON_R1))) ||
|
||||
(((pressed & L_PAYLOAD_BUTTONS) || (autoBootEmu && (pressed & DPAD_BUTTONS))) && (pressed & BUTTON_L1))) loadHomebrewFirm(pressed);
|
||||
|
||||
if(splashMode == 2) loadSplash();
|
||||
|
||||
@@ -235,7 +270,7 @@ void main(int argc, char **argv, u32 magicWord)
|
||||
|
||||
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
||||
with their own FIRM */
|
||||
else firmSource = nandType = (CONFIG(AUTOBOOTEMU) == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||
else firmSource = nandType = (autoBootEmu == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||
|
||||
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
||||
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||
@@ -271,20 +306,20 @@ boot:
|
||||
{
|
||||
locateEmuNand(&nandType);
|
||||
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
|
||||
else if((*(vu16 *)(SDMMC_BASE + REG_SDSTATUS0) & TMIO_STAT0_WRPROTECT) == 0) //Make sure the SD card isn't write protected
|
||||
error("The SD card is locked, EmuNAND can not be used.\nPlease turn the write protection switch off.");
|
||||
}
|
||||
|
||||
//Same if we're using EmuNAND as the FIRM source
|
||||
else if(firmSource != FIRMWARE_SYSNAND)
|
||||
locateEmuNand(&firmSource);
|
||||
|
||||
if(!isFirmlaunch)
|
||||
if(bootType != FIRMLAUNCH)
|
||||
{
|
||||
configData.bootConfig = ((u32)isNoForceFlagSet << 6) | ((u32)firmSource << 3) | (u32)nandType;
|
||||
configData.bootConfig = ((bootType == NTR ? 1 : 0) << 7) | ((u32)isNoForceFlagSet << 6) | ((u32)firmSource << 3) | (u32)nandType;
|
||||
writeConfig(false);
|
||||
}
|
||||
|
||||
if(isSdMode && !mountFs(false, false)) error("Failed to mount CTRNAND.");
|
||||
|
||||
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
|
||||
u32 firmVersion = loadNintendoFirm(&firmType, firmSource, loadFromStorage, isSafeMode);
|
||||
|
||||
@@ -293,7 +328,7 @@ boot:
|
||||
switch(firmType)
|
||||
{
|
||||
case NATIVE_FIRM:
|
||||
res = patchNativeFirm(firmVersion, nandType, loadFromStorage, isSafeMode, doUnitinfoPatch);
|
||||
res = patchNativeFirm(firmVersion, nandType, loadFromStorage, isFirmProtEnabled, isSafeMode, doUnitinfoPatch);
|
||||
break;
|
||||
case TWL_FIRM:
|
||||
res = patchTwlFirm(firmVersion, loadFromStorage, doUnitinfoPatch);
|
||||
@@ -310,6 +345,6 @@ boot:
|
||||
|
||||
if(res != 0) error("Failed to apply %u FIRM patch(es).", res);
|
||||
|
||||
if(!isFirmlaunch) deinitScreens();
|
||||
if(bootType != FIRMLAUNCH) deinitScreens();
|
||||
launchFirm(0, NULL);
|
||||
}
|
||||
|
||||
@@ -454,7 +454,9 @@ u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||
|
||||
if(temp == NULL) return 1;
|
||||
|
||||
u32 *off = (u32 *)(temp - 0xA);
|
||||
u32 *off;
|
||||
|
||||
for(off = (u32 *)(temp - 2); *off != 0xE5801000; off--); //Until str r1, [r0]
|
||||
|
||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||
{
|
||||
@@ -491,7 +493,15 @@ u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||
*addr = 0xE12FFF7F;
|
||||
|
||||
/*
|
||||
mov r8, sp
|
||||
bkpt 0xffff
|
||||
*/
|
||||
addr[0] = 0xE1A0800D;
|
||||
addr[1] = 0xE12FFF7F;
|
||||
|
||||
*(vu32 *)0x01FF8004 = arm9SvcTable[0x3C]; //BreakPtr
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
11
source/pin.c
11
source/pin.c
@@ -146,11 +146,20 @@ bool verifyPin(u32 pinMode)
|
||||
|
||||
initScreens();
|
||||
|
||||
swapFramebuffers(true);
|
||||
|
||||
drawString(true, 10, 10, COLOR_TITLE, "Enter the PIN using ABXY and the DPad to proceed");
|
||||
drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press START to shutdown, SELECT to clear");
|
||||
|
||||
drawFormattedString(true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE, "PIN (%u digits): ", lengthBlock[0]);
|
||||
|
||||
bool isBottomSplashValid = getFileSize("splashpin.bin") == SCREEN_BOTTOM_FBSIZE;
|
||||
if(isBottomSplashValid)
|
||||
{
|
||||
isBottomSplashValid = fileRead(fbs[0].bottom, "splashpin.bin", SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const char *messageFile = "pinmessage.txt";
|
||||
char message[801];
|
||||
|
||||
@@ -161,6 +170,8 @@ bool verifyPin(u32 pinMode)
|
||||
message[messageSize] = 0;
|
||||
drawString(false, 10, 10, COLOR_WHITE, message);
|
||||
}
|
||||
}
|
||||
swapFramebuffers(false);
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
@@ -107,10 +107,13 @@ void initScreens(void)
|
||||
}
|
||||
else updateBrightness(MULTICONFIG(BRIGHTNESS));
|
||||
|
||||
memcpy((void *)ARM11_PARAMETERS_ADDRESS, fbs, sizeof(fbs));
|
||||
invokeArm11Function(SETUP_FRAMEBUFFERS);
|
||||
|
||||
clearScreens(true);
|
||||
needToSetup = false;
|
||||
}
|
||||
|
||||
clearScreens(false);
|
||||
clearScreens(true);
|
||||
swapFramebuffers(false);
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INIT_SCREENS = 0,
|
||||
SETUP_FRAMEBUFFERS,
|
||||
CLEAR_SCREENS,
|
||||
SWAP_FRAMEBUFFERS,
|
||||
UPDATE_BRIGHTNESS,
|
||||
|
||||
@@ -27,9 +27,23 @@
|
||||
.global _start
|
||||
_start:
|
||||
@ Disable interrupts and switch to supervisor mode (also clear flags)
|
||||
mov r4, #0x13
|
||||
orr r4, #0x1C0
|
||||
msr cpsr_cxsf, r4
|
||||
msr cpsr_cxsf, #0xD3
|
||||
|
||||
@ Check if r0-r2 are 0 (r0-sp are supposed to be 0), and for regions 0, 5 and 7 of the MPU config
|
||||
@ This is not foolproof but should work well enough
|
||||
cmp r0, #0
|
||||
cmpeq r1, #0
|
||||
cmpeq r2, #0
|
||||
ldreq r4, =0x20000035
|
||||
mrceq p15, 0, r5, c6, c0, 0
|
||||
cmpeq r4, r5
|
||||
mrceq p15, 0, r5, c6, c5, 0
|
||||
ldreq r4, =0x07FF801D
|
||||
cmpeq r4, r5
|
||||
mrceq p15, 0, r5, c6, c7, 0
|
||||
ldreq r4, =0x1FFFE019
|
||||
cmpeq r4, r5
|
||||
ldreq r2, =0xB002
|
||||
|
||||
mov r9, r0
|
||||
mov r10, r1
|
||||
|
||||
@@ -114,8 +114,19 @@ typedef enum FirmwareType
|
||||
NATIVE_FIRM1X2X
|
||||
} FirmwareType;
|
||||
|
||||
extern bool isFirmlaunch,
|
||||
isSdMode;
|
||||
typedef enum bootType
|
||||
{
|
||||
B9S = 0,
|
||||
B9SNTR,
|
||||
FIRM0,
|
||||
FIRM1,
|
||||
FIRMLAUNCH,
|
||||
NTR
|
||||
} BootType;
|
||||
|
||||
extern bool isSdMode;
|
||||
|
||||
extern BootType bootType;
|
||||
|
||||
extern u16 launchedFirmTidLow[8];
|
||||
extern u16 launchedPath[41];
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ u32 waitInput(bool isMenu)
|
||||
u64 initialValue = 0ULL;
|
||||
u32 key,
|
||||
oldKey = HID_PAD;
|
||||
bool shouldShellShutdown = bootType != B9SNTR && bootType != NTR;
|
||||
|
||||
if(isMenu)
|
||||
{
|
||||
@@ -85,7 +86,8 @@ u32 waitInput(bool isMenu)
|
||||
|
||||
if(!key)
|
||||
{
|
||||
if((i2cReadRegister(I2C_DEV_MCU, 0x10) & 1)== 1) mcuPowerOff();
|
||||
if((!(i2cReadRegister(I2C_DEV_MCU, 0xF) & 2) && shouldShellShutdown) ||
|
||||
(i2cReadRegister(I2C_DEV_MCU, 0x10) & 1) == 1) mcuPowerOff();
|
||||
oldKey = 0;
|
||||
dPadDelay = 0;
|
||||
continue;
|
||||
@@ -104,7 +106,10 @@ u32 waitInput(bool isMenu)
|
||||
|
||||
void mcuPowerOff(void)
|
||||
{
|
||||
if(!isFirmlaunch && ARESCREENSINITIALIZED) clearScreens(false);
|
||||
if(bootType != FIRMLAUNCH && ARESCREENSINITIALIZED) clearScreens(false);
|
||||
|
||||
//Shutdown LCD
|
||||
if(ARESCREENSINITIALIZED) i2cWriteRegister(I2C_DEV_MCU, 0x22, 1 << 0);
|
||||
|
||||
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
flushEntireDCache();
|
||||
@@ -131,7 +136,7 @@ void error(const char *fmt, ...)
|
||||
vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(!isFirmlaunch)
|
||||
if(bootType != FIRMLAUNCH)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math $(INCLUDE) -DARM11 -D_3DS
|
||||
CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -O2 -ffast-math $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -specs=3dsx.specs $(ASFLAGS) -Wl,--section-start,.text=0x14000000
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
|
||||
@@ -64,17 +64,20 @@ _start:
|
||||
load r3, customPath
|
||||
pathRedir_1:
|
||||
ldrb r2, [r3], #1
|
||||
strh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
strneh r2, [r0], #2
|
||||
bne pathRedir_1
|
||||
sub r0, r0, #2
|
||||
pathRedir_2:
|
||||
ldrh r2, [r1], #2
|
||||
cmp r2, #0x3A ; ':'
|
||||
bne pathRedir_2
|
||||
; Skip a slash if there are two after the mountpoint,
|
||||
; as some games mistakenly have those
|
||||
ldrh r3, [r1, #2]
|
||||
cmp r3, #0x2F ; '/'
|
||||
pathRedir_3:
|
||||
ldrh r2, [r1], #2
|
||||
strh r2, [r0], #2
|
||||
strneh r2, [r0], #2
|
||||
cmp r2, #0
|
||||
bne pathRedir_3
|
||||
ldmfd sp!, {r0-r3}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#define MAX_SESSIONS 1
|
||||
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
|
||||
|
||||
u32 config, multiConfig, bootConfig;
|
||||
bool isN3DS, isSafeMode, isSdMode;
|
||||
|
||||
const char CODE_PATH[] = {0x01, 0x00, 0x00, 0x00, 0x2E, 0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00};
|
||||
|
||||
typedef struct
|
||||
@@ -30,6 +33,28 @@ static u64 g_cached_prog_handle;
|
||||
static exheader_header g_exheader;
|
||||
static char g_ret_buf[1024];
|
||||
|
||||
static inline void loadCFWInfo(void)
|
||||
{
|
||||
s64 out;
|
||||
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
|
||||
config = (u32)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 4))) svcBreak(USERBREAK_ASSERT);
|
||||
multiConfig = (u32)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 5))) svcBreak(USERBREAK_ASSERT);
|
||||
bootConfig = (u32)out;
|
||||
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x201))) svcBreak(USERBREAK_ASSERT);
|
||||
isN3DS = (bool)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x202))) svcBreak(USERBREAK_ASSERT);
|
||||
isSafeMode = (bool)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT);
|
||||
isSdMode = (bool)out;
|
||||
|
||||
IFile file;
|
||||
if(isSafeMode) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted
|
||||
}
|
||||
|
||||
static int lzss_decompress(u8 *end)
|
||||
{
|
||||
unsigned int v1; // r1@2
|
||||
@@ -116,6 +141,8 @@ static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int i
|
||||
u64 size;
|
||||
u64 total;
|
||||
|
||||
if(!CONFIG(PATCHGAMES) || !loadTitleCodeSection(progid, (u8 *)shared->text_addr, (u64)shared->total_size << 12))
|
||||
{
|
||||
archivePath.type = PATH_BINARY;
|
||||
archivePath.data = &prog_handle;
|
||||
archivePath.size = 8;
|
||||
@@ -155,6 +182,7 @@ static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int i
|
||||
{
|
||||
lzss_decompress((u8 *)shared->text_addr + size);
|
||||
}
|
||||
}
|
||||
|
||||
u16 progver = g_exheader.codesetinfo.flags.remasterversion[0] | (g_exheader.codesetinfo.flags.remasterversion[1] << 8);
|
||||
|
||||
@@ -212,7 +240,9 @@ static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
exheader->accessdesc.arm11systemlocalcaps.storageinfo.accessinfo[0] |= 0x480;
|
||||
|
||||
// Tweak 3dsx placeholder title exheader
|
||||
if (nbSection0Modules == 6 && exheader->arm11systemlocalcaps.programid == HBLDR_3DSX_TID)
|
||||
if (nbSection0Modules == 6)
|
||||
{
|
||||
if(exheader->arm11systemlocalcaps.programid == HBLDR_3DSX_TID)
|
||||
{
|
||||
Handle hbldr = 0;
|
||||
res = HBLDR_Init(&hbldr);
|
||||
@@ -229,6 +259,14 @@ static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 originalProgId = exheader->arm11systemlocalcaps.programid;
|
||||
if(CONFIG(PATCHGAMES) && loadTitleExheader(exheader->arm11systemlocalcaps.programid, exheader))
|
||||
{
|
||||
exheader->arm11systemlocalcaps.programid = originalProgId;
|
||||
exheader->accessdesc.arm11systemlocalcaps.programid = originalProgId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -586,6 +624,8 @@ int main()
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
loadCFWInfo();
|
||||
|
||||
g_active_handles = 2;
|
||||
g_cached_prog_handle = 0;
|
||||
index = 1;
|
||||
|
||||
@@ -9,6 +9,14 @@ void memcpy(void *dest, const void *src, u32 size)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
|
||||
void memset32(void *dest, u32 filler, u32 size)
|
||||
{
|
||||
u32 *dest32 = (u32 *)dest;
|
||||
|
||||
for(u32 i = 0; i < size / 4; i++)
|
||||
dest32[i] = filler;
|
||||
}
|
||||
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1,
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
#include <3ds/types.h>
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
void memset32(void *dest, u32 filler, u32 size);
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
@@ -3,12 +3,8 @@
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "fsldr.h"
|
||||
#include "ifile.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
static u32 config, multiConfig, bootConfig;
|
||||
static bool isN3DS, isSafeMode, isSdMode;
|
||||
|
||||
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, s32 offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
@@ -32,7 +28,7 @@ static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, s3
|
||||
return i;
|
||||
}
|
||||
|
||||
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||
Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||
{
|
||||
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
@@ -73,32 +69,6 @@ static u32 checkLumaDir(const char *path)
|
||||
return dirCheck(archiveId, path) ? archiveId : 0;
|
||||
}
|
||||
|
||||
static inline void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
s64 out;
|
||||
|
||||
if(infoLoaded) return;
|
||||
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
|
||||
config = (u32)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 4))) svcBreak(USERBREAK_ASSERT);
|
||||
multiConfig = (u32)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 5))) svcBreak(USERBREAK_ASSERT);
|
||||
bootConfig = (u32)out;
|
||||
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x201))) svcBreak(USERBREAK_ASSERT);
|
||||
isN3DS = (bool)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x202))) svcBreak(USERBREAK_ASSERT);
|
||||
isSafeMode = (bool)out;
|
||||
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT);
|
||||
isSdMode = (bool)out;
|
||||
|
||||
IFile file;
|
||||
if(isSafeMode) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted
|
||||
infoLoaded = true;
|
||||
}
|
||||
|
||||
static inline bool secureInfoExists(void)
|
||||
{
|
||||
static bool exists = false;
|
||||
@@ -353,7 +323,7 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* 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 */
|
||||
@@ -363,22 +333,60 @@ static inline bool loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return true;
|
||||
if(!openLumaFile(&file, path)) return false;
|
||||
|
||||
bool ret;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = false;
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) goto error;
|
||||
else
|
||||
{
|
||||
u64 total;
|
||||
|
||||
ret = R_SUCCEEDED(IFile_Read(&file, &total, code, fileSize)) && total == fileSize;
|
||||
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) goto error;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
|
||||
error:
|
||||
IFile_Close(&file);
|
||||
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
while(true);
|
||||
}
|
||||
|
||||
bool loadTitleExheader(u64 progId, exheader_header *exheader)
|
||||
{
|
||||
/* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/exheader.bin"
|
||||
If it exists it should be a decrypted exheader */
|
||||
|
||||
char path[] = "/luma/titles/0000000000000000/exheader.bin";
|
||||
progIdToStr(path + 28, progId);
|
||||
|
||||
IFile file;
|
||||
|
||||
if(!openLumaFile(&file, path)) return false;
|
||||
|
||||
u64 fileSize;
|
||||
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize != sizeof(exheader_header)) goto error;
|
||||
else
|
||||
{
|
||||
u64 total;
|
||||
|
||||
if(R_FAILED(IFile_Read(&file, &total, exheader, fileSize)) || total != fileSize) goto error;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
IFile_Close(&file);
|
||||
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
while(true);
|
||||
}
|
||||
|
||||
static inline bool loadTitleLocaleConfig(u64 progId, u8 *mask, u8 *regionId, u8 *languageId, u8 *countryId, u8 *stateId)
|
||||
@@ -570,8 +578,6 @@ static inline bool patchLayeredFs(u64 progId, u8 *code, u32 size, u32 textSize,
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress)
|
||||
{
|
||||
loadCFWInfo();
|
||||
|
||||
if(progId == 0x0004003000008F02LL || //USA Home Menu
|
||||
progId == 0x0004003000008202LL || //JPN Home Menu
|
||||
progId == 0x0004003000009802LL || //EUR Home Menu
|
||||
@@ -644,22 +650,6 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
off[1] = 0xE12FFF1E; //bx lr
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x42, 0xE0, 0x1E, 0xFF
|
||||
};
|
||||
|
||||
u8 mostRecentFpdVer = 10;
|
||||
|
||||
u8 *off = memsearch(code, pattern, textSize, sizeof(pattern));
|
||||
|
||||
if(off == NULL) goto error;
|
||||
|
||||
//Allow online access to work with old friends modules
|
||||
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
}
|
||||
|
||||
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||
progId == 0x0004001000020000LL || //JPN MSET
|
||||
progId == 0x0004001000022000LL || //EUR MSET
|
||||
@@ -755,6 +745,25 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
}
|
||||
}
|
||||
|
||||
if(progVer > 0x12)
|
||||
{
|
||||
static const u8 pattern[] = {
|
||||
0x00, 0xB1, 0x15, 0x00
|
||||
};
|
||||
|
||||
u8 *roStart = code + ((textSize + 4095) & 0xFFFFF000),
|
||||
*start = memsearch(roStart, pattern, roSize, sizeof(pattern));
|
||||
|
||||
if(start == NULL) goto error;
|
||||
|
||||
start++;
|
||||
u8 *end;
|
||||
for(end = start + 8; *(u32 *)end != 0xCC010000; end += 8)
|
||||
if(end >= roStart + roSize - 12) goto error;
|
||||
|
||||
memset32(start, 0, end - start);
|
||||
}
|
||||
|
||||
s64 nbSection0Modules;
|
||||
svcGetSystemInfo(&nbSection0Modules, 26, 0);
|
||||
|
||||
@@ -873,8 +882,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
|
||||
if(CONFIG(PATCHGAMES))
|
||||
{
|
||||
if(!loadTitleCodeSection(progId, code, size) ||
|
||||
!applyCodeIpsPatch(progId, code, size)) goto error;
|
||||
if(!applyCodeIpsPatch(progId, code, size)) goto error;
|
||||
|
||||
if((u32)((progId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)
|
||||
{
|
||||
@@ -894,5 +902,4 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
||||
|
||||
error:
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
while(true);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "exheader.h"
|
||||
#include "ifile.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))
|
||||
@@ -12,12 +14,14 @@
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(6, 1)
|
||||
#define BOOTCFG_NTRCARDBOOT BOOTCONFIG(7, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
SPLASH,
|
||||
SPLASH_DURATION,
|
||||
PIN,
|
||||
NEWCPU
|
||||
};
|
||||
@@ -30,9 +34,14 @@ enum singleOptions
|
||||
PATCHGAMES,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PATCHACCESS,
|
||||
PATCHUNITINFO,
|
||||
DISABLEARM11EXCHANDLERS
|
||||
};
|
||||
|
||||
extern u32 config, multiConfig, bootConfig;
|
||||
extern bool isN3DS, isSafeMode, isSdMode;
|
||||
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress);
|
||||
Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags);
|
||||
bool loadTitleCodeSection(u64 progId, u8 *code, u32 size);
|
||||
bool loadTitleExheader(u64 progId, exheader_header *exheader);
|
||||
|
||||
21
sysmodules/pxi/LICENSE
Normal file
21
sysmodules/pxi/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 TuxSH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
47
sysmodules/pxi/Makefile
Normal file
47
sysmodules/pxi/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
name := pxi
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
dir_out := ../../$(dir_build)
|
||||
|
||||
LIBS := -lctru
|
||||
LIBDIRS := $(CTRULIB)
|
||||
LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -g -ffast-math -mword-relocations \
|
||||
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -specs=3dsx.specs -Wl,--gc-sections $(ARCH)
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.c))
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).cxi
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).cxi: $(dir_build)/$(name).elf
|
||||
@makerom -f ncch -rsf $(name).rsf -nocodepadding -o $@ -elf $<
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
|
||||
|
||||
$(dir_build)/memory.o : CFLAGS += -O3
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
15
sysmodules/pxi/README.md
Normal file
15
sysmodules/pxi/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 3ds_pxi
|
||||
Open source replacement of the ARM11 PXI system module.
|
||||
This is licensed under the MIT license.
|
||||
|
||||
# Usage
|
||||
To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/.
|
||||
|
||||
# Credits
|
||||
This list is not complete at all:
|
||||
|
||||
* @Subv, for the process patch that used to be used in Luma3DS which I modified to assist me in PXI sysmodule reverse-engineering
|
||||
* @yifanlu, for the work his own work on loader
|
||||
* @Mrrraou, for intensive testing back in June/July 2016
|
||||
* @jackron, for makerom support and help
|
||||
* Many #Cakey and #3dsdev folks I haven't mentioned here, etc.
|
||||
123
sysmodules/pxi/pxi.rsf
Normal file
123
sysmodules/pxi/pxi.rsf
Normal file
@@ -0,0 +1,123 @@
|
||||
BasicInfo:
|
||||
Title : pxi
|
||||
CompanyCode : "00"
|
||||
ProductCode : lennybuilder
|
||||
ContentType : Application
|
||||
Logo : None
|
||||
|
||||
TitleInfo:
|
||||
UniqueId : 0x14
|
||||
Category : Base
|
||||
Version : 2
|
||||
|
||||
Option:
|
||||
UseOnSD : false
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses exefs code
|
||||
|
||||
AccessControlInfo:
|
||||
IdealProcessor : 1
|
||||
AffinityMask : 3
|
||||
|
||||
Priority : 13
|
||||
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : false
|
||||
PermitMainFunctionArgument : false
|
||||
CanShareDeviceMemory : false
|
||||
RunnableOnSleep : true
|
||||
SpecialMemoryArrange : false
|
||||
ResourceLimitCategory : Other
|
||||
|
||||
CoreVersion : 2
|
||||
DescVersion : 2
|
||||
|
||||
MemoryType : Base # Application / System / Base
|
||||
HandleTableSize: 0
|
||||
IORegisterMapping:
|
||||
- 1ec63000
|
||||
SystemCallAccess:
|
||||
AcceptSession: 74
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
BindInterrupt: 80
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreatePort: 71
|
||||
CreateSemaphore: 21
|
||||
CreateSessionToPort: 72
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
FlushProcessDataCache: 84
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetDmaState: 87
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
InvalidateProcessDataCache: 82
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
ReplyAndReceive1: 75
|
||||
ReplyAndReceive2: 76
|
||||
ReplyAndReceive3: 77
|
||||
ReplyAndReceive4: 78
|
||||
ReplyAndReceive: 79
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
StartInterProcessDma: 85
|
||||
StopDma: 86
|
||||
StoreProcessDataCache: 83
|
||||
UnmapMemoryBlock: 32
|
||||
UnbindInterrupt: 81
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
InterruptNumbers:
|
||||
- 0x50
|
||||
- 0x51
|
||||
- 0x52
|
||||
- 0x53
|
||||
ServiceAccessControl:
|
||||
FileSystemAccess:
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 0KB # It doesn't use any save data.
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x1000
|
||||
42
sysmodules/pxi/source/MyThread.c
Normal file
42
sysmodules/pxi/source/MyThread.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
MyThread.c:
|
||||
Small threading library, based off ctrulib.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "MyThread.h"
|
||||
#include "memory.h"
|
||||
|
||||
static void _thread_begin(void* arg)
|
||||
{
|
||||
MyThread *t = (MyThread *)arg;
|
||||
t->ep();
|
||||
MyThread_Exit();
|
||||
}
|
||||
|
||||
Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity)
|
||||
{
|
||||
t->ep = entrypoint;
|
||||
t->stacktop = (u8 *)stack + stackSize;
|
||||
|
||||
return svcCreateThread(&t->handle, _thread_begin, (u32)t, (u32*)t->stacktop, prio, affinity);
|
||||
}
|
||||
|
||||
Result MyThread_Join(MyThread *thread, s64 timeout_ns)
|
||||
{
|
||||
if (thread == NULL) return 0;
|
||||
Result res = svcWaitSynchronization(thread->handle, timeout_ns);
|
||||
if(R_FAILED(res)) return res;
|
||||
|
||||
svcCloseHandle(thread->handle);
|
||||
thread->handle = (Handle)0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void MyThread_Exit(void)
|
||||
{
|
||||
svcExitThread();
|
||||
}
|
||||
28
sysmodules/pxi/source/MyThread.h
Normal file
28
sysmodules/pxi/source/MyThread.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
MyThread.h:
|
||||
Small threading library, based off ctrulib.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include <3ds/result.h>
|
||||
#include <3ds/svc.h>
|
||||
#include <3ds/synchronization.h>
|
||||
|
||||
#define THREAD_STACK_SIZE 0x1000
|
||||
|
||||
typedef struct MyThread
|
||||
{
|
||||
Handle handle;
|
||||
void (*ep)(void);
|
||||
bool finished;
|
||||
void* stacktop;
|
||||
} MyThread;
|
||||
|
||||
Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity);
|
||||
Result MyThread_Join(MyThread *thread, s64 timeout_ns);
|
||||
void MyThread_Exit(void);
|
||||
139
sysmodules/pxi/source/PXI.c
Normal file
139
sysmodules/pxi/source/PXI.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
PXI.c:
|
||||
PXI I/O functions.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "PXI.h"
|
||||
|
||||
void PXIReset(void)
|
||||
{
|
||||
REG_PXI_SYNC = 0;
|
||||
REG_PXI_CNT = CNT_CLEAR_SEND_FIFO;
|
||||
|
||||
for(u32 i = 0; i < 16; i += 2)
|
||||
{
|
||||
REG_PXI_RECV;
|
||||
REG_PXI_RECV;
|
||||
}
|
||||
|
||||
REG_PXI_CNT = 0;
|
||||
REG_PXI_CNT = CNT_ENABLE_FIFOs | CNT_ACKNOWLEDGE_FIFO_ERROR | CNT_CLEAR_SEND_FIFO;
|
||||
}
|
||||
|
||||
void PXITriggerSync9IRQ(void)
|
||||
{
|
||||
REG_PXI_INTERRUPT_CNT |= SYNC_TRIGGER_SYNC9_IRQ;
|
||||
}
|
||||
|
||||
bool PXIIsSendFIFOFull(void)
|
||||
{
|
||||
return (REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS) != 0;
|
||||
}
|
||||
|
||||
void PXISendByte(u8 byte)
|
||||
{
|
||||
REG_PXI_BYTE_SENT_TO_REMOTE = byte;
|
||||
}
|
||||
|
||||
void PXISendWord(u32 word)
|
||||
{
|
||||
while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS);
|
||||
REG_PXI_SEND = word;
|
||||
}
|
||||
|
||||
void PXISendBuffer(const u32 *buffer, u32 nbWords)
|
||||
{
|
||||
for(; nbWords > 0; nbWords--)
|
||||
{
|
||||
while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS);
|
||||
REG_PXI_SEND = *buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
bool PXIIsReceiveFIFOEmpty(void)
|
||||
{
|
||||
return (REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS) != 0;
|
||||
}
|
||||
|
||||
u8 PXIReceiveByte(void)
|
||||
{
|
||||
return REG_PXI_BYTE_RECEIVED_FROM_REMOTE;
|
||||
}
|
||||
|
||||
u32 PXIReceiveWord(void)
|
||||
{
|
||||
while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS);
|
||||
return REG_PXI_RECV;
|
||||
}
|
||||
|
||||
void PXIReceiveBuffer(u32 *buffer, u32 nbWords)
|
||||
{
|
||||
for(; nbWords > 0; nbWords--)
|
||||
{
|
||||
while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS);
|
||||
*buffer++ = REG_PXI_RECV;
|
||||
}
|
||||
}
|
||||
|
||||
Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt)
|
||||
{
|
||||
Result res = 0;
|
||||
u32 mask = CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
|
||||
if(receiveFIFONotEmptyInterrupt != NULL)
|
||||
{
|
||||
res = svcBindInterrupt(0x53, *receiveFIFONotEmptyInterrupt, 0, false);
|
||||
if(R_FAILED(res))
|
||||
{
|
||||
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
|
||||
return res;
|
||||
}
|
||||
REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ;
|
||||
}
|
||||
|
||||
if(sendFIFOEmptyInterrupt != NULL)
|
||||
{
|
||||
res = svcBindInterrupt(0x52, *sendFIFOEmptyInterrupt, 0, false);
|
||||
if(R_FAILED(res))
|
||||
{
|
||||
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
|
||||
return res;
|
||||
}
|
||||
REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
|
||||
}
|
||||
|
||||
if(syncInterrupt != NULL)
|
||||
{
|
||||
res = svcBindInterrupt(0x50, *syncInterrupt, 0, false);
|
||||
if(R_FAILED(res))
|
||||
{
|
||||
unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt);
|
||||
return res;
|
||||
}
|
||||
REG_PXI_INTERRUPT_CNT |= SYNC_ENABLE_SYNC11_IRQ;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt)
|
||||
{
|
||||
if(receiveFIFONotEmptyInterrupt != NULL)
|
||||
{
|
||||
REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ;
|
||||
svcUnbindInterrupt(0x53, *receiveFIFONotEmptyInterrupt);
|
||||
}
|
||||
|
||||
if(sendFIFOEmptyInterrupt != NULL)
|
||||
{
|
||||
REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ;
|
||||
svcUnbindInterrupt(0x52, *sendFIFOEmptyInterrupt);
|
||||
}
|
||||
if(syncInterrupt != NULL)
|
||||
{
|
||||
REG_PXI_INTERRUPT_CNT &= ~SYNC_ENABLE_SYNC11_IRQ;
|
||||
svcUnbindInterrupt(0x50, *syncInterrupt);
|
||||
}
|
||||
}
|
||||
47
sysmodules/pxi/source/PXI.h
Normal file
47
sysmodules/pxi/source/PXI.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
PXI.h:
|
||||
PXI I/O functions.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#define PXI_REGS_BASE 0x1EC63000
|
||||
#define REG_PXI_SYNC *(vu32 *)(PXI_REGS_BASE + 0)
|
||||
#define REG_PXI_BYTE_RECEIVED_FROM_REMOTE *(vu8 *)(PXI_REGS_BASE)
|
||||
#define REG_PXI_BYTE_SENT_TO_REMOTE *(vu8 *)(PXI_REGS_BASE + 1)
|
||||
#define REG_PXI_INTERRUPT_CNT *(vu8 *)(PXI_REGS_BASE + 3)
|
||||
#define SYNC_TRIGGER_SYNC9_IRQ (1U << 6)
|
||||
#define SYNC_ENABLE_SYNC11_IRQ (1U << 7)
|
||||
|
||||
#define REG_PXI_CNT *(vu16 *)(PXI_REGS_BASE + 4)
|
||||
#define CNT_SEND_FIFO_FULL_STATUS (1U << 1)
|
||||
#define CNT_ENABLE_SEND_FIFO_EMPTY_IRQ (1U << 2)
|
||||
#define CNT_CLEAR_SEND_FIFO (1U << 3)
|
||||
#define CNT_RECEIVE_FIFO_EMPTY_STATUS (1U << 8)
|
||||
#define CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ (1U << 10)
|
||||
#define CNT_ACKNOWLEDGE_FIFO_ERROR (1U << 14)
|
||||
#define CNT_ENABLE_FIFOs (1U << 15)
|
||||
|
||||
#define REG_PXI_SEND *(vu32 *)(PXI_REGS_BASE + 8)
|
||||
#define REG_PXI_RECV *(vu32 *)(PXI_REGS_BASE + 12)
|
||||
|
||||
void PXIReset(void);
|
||||
void PXITriggerSync9IRQ(void);
|
||||
|
||||
bool PXIIsSendFIFOFull(void);
|
||||
void PXISendByte(u8 byte);
|
||||
void PXISendWord(u32 word);
|
||||
void PXISendBuffer(const u32 *buffer, u32 nbWords);
|
||||
|
||||
bool PXIIsReceiveFIFOEmpty(void);
|
||||
u8 PXIReceiveByte(void);
|
||||
u32 PXIReceiveWord(void);
|
||||
void PXIReceiveBuffer(u32 *buffer, u32 nbWords);
|
||||
|
||||
Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt);
|
||||
void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt);
|
||||
94
sysmodules/pxi/source/common.h
Normal file
94
sysmodules/pxi/source/common.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
common.h:
|
||||
Common types and global variables.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
typedef enum SessionState
|
||||
{
|
||||
STATE_IDLE = 0,
|
||||
STATE_RECEIVED_FROM_ARM11 = 1,
|
||||
STATE_SENT_TO_ARM9 = 2,
|
||||
STATE_RECEIVED_FROM_ARM9 = 3
|
||||
} SessionState;
|
||||
|
||||
typedef struct SessionData
|
||||
{
|
||||
SessionState state;
|
||||
u32 buffer[0x100/4];
|
||||
|
||||
Handle handle;
|
||||
u32 usedStaticBuffers;
|
||||
|
||||
RecursiveLock lock;
|
||||
} SessionData;
|
||||
|
||||
#define NB_STATIC_BUFFERS 21
|
||||
|
||||
typedef struct SessionManager
|
||||
{
|
||||
Handle sendAllBuffersToArm9Event, replySemaphore, PXISRV11CommandReceivedEvent, PXISRV11ReplySentEvent;
|
||||
u32 latest_PXI_MC5_val, pendingArm9Commands;
|
||||
u32 receivedServiceId;
|
||||
RecursiveLock senderLock;
|
||||
bool sendingDisabled;
|
||||
SessionData sessionData[10];
|
||||
|
||||
u32 currentlyProvidedStaticBuffers, freeStaticBuffers;
|
||||
} SessionManager;
|
||||
|
||||
//Page alignment is mandatory there
|
||||
extern u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x1000/4];
|
||||
|
||||
extern Handle PXISyncInterrupt, PXITransferMutex;
|
||||
extern Handle terminationRequestedEvent;
|
||||
extern bool shouldTerminate;
|
||||
extern SessionManager sessionManager;
|
||||
|
||||
extern const u32 nbStaticBuffersByService[10];
|
||||
|
||||
static inline Result assertSuccess(Result res)
|
||||
{
|
||||
if(R_FAILED(res)) svcBreak(USERBREAK_PANIC);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline s32 getMSBPosition(u32 val)
|
||||
{
|
||||
return 31 - (s32) __builtin_clz(val);
|
||||
}
|
||||
|
||||
static inline s32 getLSBPosition(u32 val)
|
||||
{
|
||||
return __builtin_ffs(val) - 1;
|
||||
}
|
||||
|
||||
static inline u32 clearMSBs(u32 val, u32 nb)
|
||||
{
|
||||
for(u32 i = 0; i < nb; i++)
|
||||
{
|
||||
s32 pos = getMSBPosition(val);
|
||||
if(pos == -1) break;
|
||||
val &= ~(1 << pos);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 countNbBitsSet(u32 val)
|
||||
{
|
||||
u32 nb = 0;
|
||||
while(val != 0)
|
||||
{
|
||||
val = clearMSBs(val, 1);
|
||||
nb++;
|
||||
}
|
||||
|
||||
return nb;
|
||||
}
|
||||
211
sysmodules/pxi/source/main.c
Normal file
211
sysmodules/pxi/source/main.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
main.c
|
||||
(De)initialization stuff. It's also here where sessions are being accepted.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "PXI.h"
|
||||
#include "common.h"
|
||||
#include "MyThread.h"
|
||||
#include "receiver.h"
|
||||
#include "sender.h"
|
||||
#include "memory.h"
|
||||
|
||||
Handle PXISyncInterrupt = 0, PXITransferMutex = 0;
|
||||
Handle terminationRequestedEvent = 0;
|
||||
bool shouldTerminate = false;
|
||||
SessionManager sessionManager = {0};
|
||||
|
||||
const char *serviceNames[10] =
|
||||
{
|
||||
"pxi:mc",
|
||||
|
||||
"PxiFS0",
|
||||
"PxiFS1",
|
||||
"PxiFSB",
|
||||
"PxiFSR",
|
||||
|
||||
"PxiPM",
|
||||
|
||||
"pxi:dev", //in the official PXI module maxSessions(pxi:dev) == 2. It doesn't matter anyways, since srvSysRegisterService is always called with 1
|
||||
"pxi:am9",
|
||||
"pxi:ps9",
|
||||
|
||||
"pxi:srv11",
|
||||
};
|
||||
|
||||
const u32 nbStaticBuffersByService[10] = {0, 2, 2, 2, 2, 1, 4, 4, 4, 0};
|
||||
|
||||
u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x400] = {{0}};
|
||||
|
||||
static inline void initPXI(void)
|
||||
{
|
||||
Result res;
|
||||
|
||||
Handle handles[2] = {0};
|
||||
|
||||
PXIReset();
|
||||
|
||||
if(PXISyncInterrupt != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B
|
||||
assertSuccess(svcCreateEvent(&PXISyncInterrupt, RESET_ONESHOT));
|
||||
|
||||
if(PXITransferMutex != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B
|
||||
assertSuccess(svcCreateMutex(&PXITransferMutex, false));
|
||||
|
||||
assertSuccess(svcCreateEvent(&handles[0], RESET_ONESHOT)); //receive FIFO not empty
|
||||
assertSuccess(svcCreateEvent(&handles[1], RESET_ONESHOT)); //send FIFO empty
|
||||
assertSuccess(bindPXIInterrupts(&PXISyncInterrupt, &handles[0], &handles[1]));
|
||||
|
||||
s32 handleIndex;
|
||||
do
|
||||
{
|
||||
while(!PXIIsSendFIFOFull()) PXISendWord(0);
|
||||
|
||||
res = assertSuccess(svcWaitSynchronization(handles[0], 0LL));
|
||||
if(R_DESCRIPTION(res) == RD_TIMEOUT)
|
||||
assertSuccess(svcWaitSynchronizationN(&handleIndex, handles, 2, false, -1LL));
|
||||
else
|
||||
handleIndex = 0;
|
||||
} while(handleIndex != 0);
|
||||
|
||||
|
||||
|
||||
unbindPXIInterrupts(NULL, &handles[0], &handles[1]);
|
||||
|
||||
PXISendByte(1);
|
||||
while(PXIReceiveByte() < 1);
|
||||
|
||||
while (!PXIIsReceiveFIFOEmpty())
|
||||
PXIReceiveWord();
|
||||
|
||||
PXISendByte(2);
|
||||
while(PXIReceiveByte() < 2);
|
||||
|
||||
svcCloseHandle(handles[0]);
|
||||
svcCloseHandle(handles[1]);
|
||||
}
|
||||
|
||||
static inline void exitPXI(void)
|
||||
{
|
||||
unbindPXIInterrupts(&PXISyncInterrupt, NULL, NULL);
|
||||
svcCloseHandle(PXITransferMutex);
|
||||
svcCloseHandle(PXISyncInterrupt);
|
||||
PXIReset();
|
||||
}
|
||||
|
||||
static u8 ALIGN(8) receiverStack[THREAD_STACK_SIZE];
|
||||
static u8 ALIGN(8) senderStack[THREAD_STACK_SIZE];
|
||||
static u8 ALIGN(8) PXISRV11HandlerStack[THREAD_STACK_SIZE];
|
||||
|
||||
// this is called before main
|
||||
void __appInit()
|
||||
{
|
||||
assertSuccess(svcCreateEvent(&terminationRequestedEvent, RESET_STICKY));
|
||||
|
||||
assertSuccess(svcCreateEvent(&sessionManager.sendAllBuffersToArm9Event, RESET_ONESHOT));
|
||||
assertSuccess(svcCreateSemaphore(&sessionManager.replySemaphore, 0, 9));
|
||||
assertSuccess(svcCreateEvent(&sessionManager.PXISRV11CommandReceivedEvent, RESET_ONESHOT));
|
||||
assertSuccess(svcCreateEvent(&sessionManager.PXISRV11ReplySentEvent, RESET_ONESHOT));
|
||||
initPXI();
|
||||
|
||||
for(Result res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL))
|
||||
{
|
||||
res = srvInit();
|
||||
if(R_FAILED(res) && res != (Result)0xD88007FA)
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
}
|
||||
|
||||
// this is called after main exits
|
||||
void __appExit()
|
||||
{
|
||||
srvExit();
|
||||
exitPXI();
|
||||
|
||||
svcCloseHandle(terminationRequestedEvent);
|
||||
svcCloseHandle(sessionManager.sendAllBuffersToArm9Event);
|
||||
svcCloseHandle(sessionManager.replySemaphore);
|
||||
svcCloseHandle(sessionManager.PXISRV11CommandReceivedEvent);
|
||||
svcCloseHandle(sessionManager.PXISRV11ReplySentEvent);
|
||||
}
|
||||
|
||||
// stubs for non-needed pre-main functions
|
||||
void __system_initSyscalls(){}
|
||||
|
||||
|
||||
Result __sync_init(void);
|
||||
Result __sync_fini(void);
|
||||
|
||||
void __ctru_exit()
|
||||
{
|
||||
__appExit();
|
||||
__sync_fini();
|
||||
svcExitProcess();
|
||||
}
|
||||
|
||||
void initSystem()
|
||||
{
|
||||
__sync_init();
|
||||
__system_initSyscalls();
|
||||
__appInit();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Handle handles[10] = {0}; //notification handle + service handles
|
||||
MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0};
|
||||
|
||||
for(u32 i = 0; i < 9; i++)
|
||||
assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1));
|
||||
|
||||
assertSuccess(MyThread_Create(&receiverThread, receiver, receiverStack, THREAD_STACK_SIZE, 0x2D, -2));
|
||||
assertSuccess(MyThread_Create(&senderThread, sender, senderStack, THREAD_STACK_SIZE, 0x2D, -2));
|
||||
assertSuccess(MyThread_Create(&PXISRV11HandlerThread, PXISRV11Handler, PXISRV11HandlerStack, THREAD_STACK_SIZE, 0x2D, -2));
|
||||
|
||||
assertSuccess(srvEnableNotification(&handles[0]));
|
||||
|
||||
while(!shouldTerminate)
|
||||
{
|
||||
s32 index = 0;
|
||||
assertSuccess(svcWaitSynchronizationN(&index, handles, 10, false, -1LL));
|
||||
|
||||
if(index == 0)
|
||||
{
|
||||
u32 notificationId;
|
||||
assertSuccess(srvReceiveNotification(¬ificationId));
|
||||
if(notificationId == 0x100) shouldTerminate = true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Handle session = 0;
|
||||
SessionData *data = &sessionManager.sessionData[index - 1];
|
||||
assertSuccess(svcAcceptSession(&session, handles[index]));
|
||||
|
||||
RecursiveLock_Lock(&sessionManager.senderLock);
|
||||
if(data->handle != 0)
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
data->handle = session;
|
||||
assertSuccess(svcSignalEvent(sessionManager.sendAllBuffersToArm9Event));
|
||||
|
||||
RecursiveLock_Unlock(&sessionManager.senderLock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 PXIMC_OnPXITerminate = 0x10000; //TODO: see if this is correct
|
||||
sessionManager.sessionData[0].state = STATE_SENT_TO_ARM9;
|
||||
sendPXICmdbuf(NULL, 0, &PXIMC_OnPXITerminate);
|
||||
|
||||
assertSuccess(MyThread_Join(&receiverThread, -1LL));
|
||||
assertSuccess(MyThread_Join(&senderThread, -1LL));
|
||||
assertSuccess(MyThread_Join(&PXISRV11HandlerThread, -1LL));
|
||||
|
||||
for(u32 i = 0; i < 10; i++)
|
||||
svcCloseHandle(handles[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
19
sysmodules/pxi/source/memory.c
Normal file
19
sysmodules/pxi/source/memory.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
memory.c:
|
||||
Memory functions
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
//Adpated from CakesFW
|
||||
void memcpy(void *dest, const void *src, u32 size)
|
||||
{
|
||||
u8 *destc = (u8 *)dest;
|
||||
const u8 *srcc = (const u8 *)src;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
13
sysmodules/pxi/source/memory.h
Normal file
13
sysmodules/pxi/source/memory.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
memory.h:
|
||||
Memory functions
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
69
sysmodules/pxi/source/receiver.c
Normal file
69
sysmodules/pxi/source/receiver.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
receiver.c:
|
||||
Fetches replies coming from Process9, writing them in the appropriate buffer.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "receiver.h"
|
||||
#include "PXI.h"
|
||||
#include "memory.h"
|
||||
|
||||
static inline void receiveFromArm9(void)
|
||||
{
|
||||
u32 serviceId = PXIReceiveWord();
|
||||
|
||||
//The offcical implementation can return 0xD90043FA
|
||||
if(((serviceId >= 10)) || (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
sessionManager.receivedServiceId = serviceId;
|
||||
RecursiveLock_Lock(&sessionManager.sessionData[serviceId].lock);
|
||||
u32 replyHeader = PXIReceiveWord();
|
||||
u32 replySizeWords = (replyHeader & 0x3F) + ((replyHeader & 0xFC0) >> 6) + 1;
|
||||
|
||||
if(replySizeWords > 0x40) svcBreak(USERBREAK_PANIC);
|
||||
|
||||
u32 *buf = sessionManager.sessionData[serviceId].buffer;
|
||||
|
||||
buf[0] = replyHeader;
|
||||
PXIReceiveBuffer(buf + 1, replySizeWords - 1);
|
||||
sessionManager.sessionData[serviceId].state = STATE_RECEIVED_FROM_ARM9;
|
||||
RecursiveLock_Unlock(&sessionManager.sessionData[serviceId].lock);
|
||||
|
||||
if(serviceId == 0 && shouldTerminate)
|
||||
{
|
||||
assertSuccess(svcSignalEvent(terminationRequestedEvent));
|
||||
return;
|
||||
}
|
||||
|
||||
if(serviceId != 9)
|
||||
{
|
||||
s32 count;
|
||||
assertSuccess(svcReleaseSemaphore(&count, sessionManager.replySemaphore, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertSuccess(svcSignalEvent(sessionManager.PXISRV11CommandReceivedEvent));
|
||||
assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL));
|
||||
if( (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9))
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
}
|
||||
|
||||
void receiver(void)
|
||||
{
|
||||
Handle handles[] = {PXISyncInterrupt, terminationRequestedEvent};
|
||||
|
||||
assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL));
|
||||
while(true)
|
||||
{
|
||||
s32 index;
|
||||
assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL));
|
||||
|
||||
if(index == 1) return;
|
||||
while(!PXIIsReceiveFIFOEmpty())
|
||||
receiveFromArm9();
|
||||
}
|
||||
}
|
||||
13
sysmodules/pxi/source/receiver.h
Normal file
13
sysmodules/pxi/source/receiver.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
receiver.h:
|
||||
Fetches replies coming from Process9, writing them in the appropriate buffer.
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void receiver(void);
|
||||
300
sysmodules/pxi/source/sender.c
Normal file
300
sysmodules/pxi/source/sender.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
sender.c
|
||||
Handles commands from arm11 processes, then sends them to Process9, and replies to arm11 processes the replies received from Process9 (=> receiver.c).
|
||||
(except for PXISRV11)
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#include "sender.h"
|
||||
#include "PXI.h"
|
||||
#include "memory.h"
|
||||
|
||||
Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer)
|
||||
{
|
||||
|
||||
Result res = 0;
|
||||
|
||||
if(additionalHandle != NULL)
|
||||
{
|
||||
s32 index = 1;
|
||||
bool cancelled = false;
|
||||
Handle handles[2] = {PXITransferMutex, *additionalHandle};
|
||||
|
||||
res = assertSuccess(svcWaitSynchronization(PXITransferMutex, 0LL));
|
||||
if(R_DESCRIPTION(res) == RD_TIMEOUT)
|
||||
{
|
||||
assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL));
|
||||
cancelled = index == 1;
|
||||
}
|
||||
else
|
||||
cancelled = false;
|
||||
|
||||
if(cancelled)
|
||||
return 0xD92043FB; // cancel requested
|
||||
}
|
||||
else
|
||||
assertSuccess(svcWaitSynchronization(PXITransferMutex, -1LL));
|
||||
|
||||
PXISendWord(serviceId & 0xFF);
|
||||
PXITriggerSync9IRQ(); //notify arm9
|
||||
PXISendBuffer(buffer, (buffer[0] & 0x3F) + ((buffer[0] & 0xFC0) >> 6) + 1);
|
||||
|
||||
svcReleaseMutex(PXITransferMutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void updateTLSForStaticBuffers(void)
|
||||
{
|
||||
u32 *staticBufs = getThreadStaticBuffers();
|
||||
u32 val = sessionManager.currentlyProvidedStaticBuffers;
|
||||
for(u32 i = 0; i < 4; i++)
|
||||
{
|
||||
s32 pos = getLSBPosition(val);
|
||||
if(pos != -1)
|
||||
{
|
||||
staticBufs[2 * i] = IPC_Desc_StaticBuffer(0x1000, 0);
|
||||
staticBufs[2 * i + 1] = (u32)&staticBuffers[pos];
|
||||
val &= ~(1 << pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
staticBufs[2 * i] = IPC_Desc_StaticBuffer(0, 0);
|
||||
staticBufs[2 * i + 1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void acquireStaticBuffers(void)
|
||||
{
|
||||
u32 freeStaticBuffersOrig = sessionManager.freeStaticBuffers;
|
||||
sessionManager.freeStaticBuffers = clearMSBs(sessionManager.freeStaticBuffers, 4);
|
||||
sessionManager.currentlyProvidedStaticBuffers = ~sessionManager.freeStaticBuffers & freeStaticBuffersOrig;
|
||||
updateTLSForStaticBuffers();
|
||||
}
|
||||
|
||||
static void releaseStaticBuffers(u32 *src, u32 nb)
|
||||
{
|
||||
u32 val = clearMSBs(*src, nb);
|
||||
sessionManager.freeStaticBuffers |= ~val & *src;
|
||||
*src = val;
|
||||
}
|
||||
|
||||
void sender(void)
|
||||
{
|
||||
Handle handles[12] = {terminationRequestedEvent, sessionManager.sendAllBuffersToArm9Event, sessionManager.replySemaphore};
|
||||
Handle replyTarget = 0;
|
||||
Result res = 0;
|
||||
s32 index;
|
||||
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
u32 nbIdleSessions = 0;
|
||||
u32 posToServiceId[9] = {0};
|
||||
RecursiveLock_Lock(&sessionManager.senderLock);
|
||||
|
||||
//Setting static buffers is needed for IPC translation types 2 and 3 (otherwise ReplyAndReceive will dereference NULL)
|
||||
sessionManager.freeStaticBuffers = (1 << NB_STATIC_BUFFERS) - 1;
|
||||
acquireStaticBuffers();
|
||||
|
||||
do
|
||||
{
|
||||
if(replyTarget == 0) //send to arm9
|
||||
{
|
||||
for(u32 i = 0; i < 9; i++)
|
||||
{
|
||||
SessionData *data = &sessionManager.sessionData[i];
|
||||
if(data->handle == 0 || data->state != STATE_RECEIVED_FROM_ARM11)
|
||||
continue;
|
||||
|
||||
if(sessionManager.sendingDisabled)
|
||||
{
|
||||
if (sessionManager.pendingArm9Commands != 0 || sessionManager.latest_PXI_MC5_val == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
else
|
||||
sessionManager.pendingArm9Commands++;
|
||||
|
||||
RecursiveLock_Lock(&data->lock);
|
||||
data->state = STATE_SENT_TO_ARM9;
|
||||
res = sendPXICmdbuf(&terminationRequestedEvent, i, data->buffer);
|
||||
RecursiveLock_Unlock(&data->lock);
|
||||
|
||||
if(R_FAILED(res))
|
||||
goto terminate;
|
||||
|
||||
}
|
||||
cmdbuf[0] = 0xFFFF0000; //Kernel11
|
||||
}
|
||||
|
||||
nbIdleSessions = 0;
|
||||
for(u32 i = 0; i < 9; i++)
|
||||
{
|
||||
if(sessionManager.sessionData[i].handle != 0 && sessionManager.sessionData[i].state == STATE_IDLE)
|
||||
{
|
||||
handles[3 + nbIdleSessions] = sessionManager.sessionData[i].handle;
|
||||
posToServiceId[nbIdleSessions++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&sessionManager.senderLock);
|
||||
res = svcReplyAndReceive(&index, handles, 3 + nbIdleSessions, replyTarget);
|
||||
RecursiveLock_Lock(&sessionManager.senderLock);
|
||||
|
||||
if((u32)res == 0xC920181A) //session closed by remote
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if(index == -1)
|
||||
for(i = 0; i < 9 && replyTarget != sessionManager.sessionData[i].handle; i++);
|
||||
|
||||
else
|
||||
i = posToServiceId[index - 3];
|
||||
|
||||
if(i >= 9) svcBreak(USERBREAK_PANIC);
|
||||
|
||||
svcCloseHandle(sessionManager.sessionData[i].handle);
|
||||
sessionManager.sessionData[i].handle = replyTarget = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(R_FAILED(res) || (u32)index >= 3 + nbIdleSessions)
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
switch(index)
|
||||
{
|
||||
case 0: //terminaton requested
|
||||
break;
|
||||
|
||||
case 1:
|
||||
replyTarget = 0;
|
||||
continue;
|
||||
|
||||
case 2: //arm9 reply
|
||||
{
|
||||
u32 sessionId = 0;
|
||||
for(sessionId = 0; sessionId < 9 && sessionManager.sessionData[sessionId].state != STATE_RECEIVED_FROM_ARM9; sessionId++);
|
||||
if(sessionId == 9) svcBreak(USERBREAK_PANIC);
|
||||
SessionData *data = &sessionManager.sessionData[sessionId];
|
||||
|
||||
RecursiveLock_Lock(&data->lock);
|
||||
if(data->state != STATE_RECEIVED_FROM_ARM9) svcBreak(USERBREAK_PANIC);
|
||||
if(sessionManager.latest_PXI_MC5_val == 2)
|
||||
{
|
||||
if(sessionManager.pendingArm9Commands != 0) svcBreak(USERBREAK_PANIC);
|
||||
sessionManager.sendingDisabled = false;
|
||||
}
|
||||
else if(sessionManager.latest_PXI_MC5_val == 0)
|
||||
(sessionManager.pendingArm9Commands)--;
|
||||
|
||||
u32 bufSize = 4 * ((data->buffer[0] & 0x3F) + ((data->buffer[0] & 0xFC0) >> 6) + 1);
|
||||
if(bufSize > 0x100) svcBreak(USERBREAK_PANIC);
|
||||
memcpy(cmdbuf, data->buffer, bufSize);
|
||||
|
||||
releaseStaticBuffers(&data->usedStaticBuffers, 4);
|
||||
|
||||
data->state = STATE_IDLE;
|
||||
replyTarget = data->handle;
|
||||
|
||||
RecursiveLock_Unlock(&data->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
default: //arm11 command received
|
||||
{
|
||||
u32 serviceId = posToServiceId[index - 3];
|
||||
SessionData *data = &sessionManager.sessionData[serviceId];
|
||||
RecursiveLock_Lock(&data->lock);
|
||||
|
||||
if(data->state != STATE_IDLE) svcBreak(USERBREAK_PANIC);
|
||||
|
||||
if(!(serviceId == 0 && (cmdbuf[0] >> 16) == 5)) //if not pxi:mc 5
|
||||
sessionManager.latest_PXI_MC5_val = 0;
|
||||
else if((u8)(cmdbuf[1]) != 0)
|
||||
{
|
||||
sessionManager.latest_PXI_MC5_val = 1;
|
||||
if(sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC);
|
||||
sessionManager.sendingDisabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sessionManager.latest_PXI_MC5_val = 2;
|
||||
if(!sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
|
||||
u32 bufSize = 4 * ((cmdbuf[0] & 0x3F) + ((cmdbuf[0] & 0xFC0) >> 6) + 1);
|
||||
if(bufSize > 0x100) svcBreak(USERBREAK_PANIC);
|
||||
memcpy(data->buffer, cmdbuf, bufSize);
|
||||
|
||||
data->state = STATE_RECEIVED_FROM_ARM11;
|
||||
replyTarget = 0;
|
||||
|
||||
releaseStaticBuffers(&sessionManager.currentlyProvidedStaticBuffers, 4 - nbStaticBuffersByService[serviceId]);
|
||||
data->usedStaticBuffers = sessionManager.currentlyProvidedStaticBuffers;
|
||||
acquireStaticBuffers();
|
||||
|
||||
RecursiveLock_Unlock(&data->lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(index != 0);
|
||||
|
||||
terminate:
|
||||
for(u32 i = 0; i < 9; i++)
|
||||
{
|
||||
if(sessionManager.sessionData[i].handle != 0)
|
||||
svcCloseHandle(sessionManager.sessionData[i].handle);
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&sessionManager.senderLock);
|
||||
}
|
||||
|
||||
void PXISRV11Handler(void)
|
||||
{
|
||||
// Assumption: only 1 request is sent to this service at a time
|
||||
Handle handles[] = {sessionManager.PXISRV11CommandReceivedEvent, terminationRequestedEvent};
|
||||
SessionData *data = &sessionManager.sessionData[9];
|
||||
|
||||
data->state = STATE_SENT_TO_ARM9;
|
||||
assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent));
|
||||
|
||||
while(true)
|
||||
{
|
||||
s32 index;
|
||||
assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL));
|
||||
|
||||
if(index == 1) return;
|
||||
else
|
||||
{
|
||||
RecursiveLock_Lock(&data->lock);
|
||||
|
||||
if(data->state != STATE_RECEIVED_FROM_ARM9)
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
|
||||
data->state = STATE_IDLE;
|
||||
|
||||
if(data->buffer[0] >> 16 != 1)
|
||||
{
|
||||
data->buffer[0] = 0x40;
|
||||
data->buffer[1] = 0xD900182F; //unimplemented/invalid command
|
||||
}
|
||||
else
|
||||
{
|
||||
data->buffer[0] = 0x10040;
|
||||
data->buffer[1] = srvPublishToSubscriber(data->buffer[1], 1);
|
||||
|
||||
data->state = STATE_RECEIVED_FROM_ARM11;
|
||||
if(data->buffer[1] == 0xD8606408)
|
||||
svcBreak(USERBREAK_PANIC);
|
||||
}
|
||||
|
||||
assertSuccess(sendPXICmdbuf(&terminationRequestedEvent, 9, data->buffer));
|
||||
data->state = STATE_SENT_TO_ARM9;
|
||||
assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent));
|
||||
RecursiveLock_Unlock(&data->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
sysmodules/pxi/source/sender.h
Normal file
15
sysmodules/pxi/source/sender.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
sender.h
|
||||
Handles commands from arm11 processes, then sends them to Process9, and replies to arm11 processes the replies received from Process9 (=> receiver.c) (except for PXISRV11).
|
||||
|
||||
(c) TuxSH, 2016-2017
|
||||
This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer);
|
||||
void sender(void);
|
||||
void PXISRV11Handler(void);
|
||||
@@ -6,11 +6,6 @@ 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
|
||||
@@ -26,7 +21,7 @@ INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ARCH := -mcpu=mpcore -mfloat-abi=hard
|
||||
ASFLAGS := -g $(ARCH)
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -mtp=soft -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -mtp=soft -fno-builtin -std=c11 -O2 -ffast-math -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections -fdata-sections $(INCLUDE) -I$(dir_include) -DARM11 -D_3DS
|
||||
LDFLAGS := -specs=3dsx.specs -g $(ARCH) -mtp=soft -Wl,--section-start,.text=0x14000000 -Wl,--gc-sections
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#define COLOR_TITLE RGB565(0x00, 0x26, 0x1F)
|
||||
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
||||
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
||||
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
||||
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
||||
|
||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||
|
||||
@@ -111,7 +111,7 @@ typedef struct GDBContext
|
||||
u32 nbWatchpoints;
|
||||
u32 watchpoints[2];
|
||||
|
||||
bool isGDB;
|
||||
bool enableExternalMemoryAccess;
|
||||
char *commandData, *commandEnd;
|
||||
int latestSentPacketSize;
|
||||
char buffer[GDB_BUF_LEN + 4];
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
|
||||
|
||||
GDB_DECLARE_QUERY_HANDLER(Rcmd);
|
||||
|
||||
@@ -35,3 +35,6 @@ extern Menu rosalinaMenu;
|
||||
void RosalinaMenu_TakeScreenshot(void);
|
||||
void RosalinaMenu_ShowCredits(void);
|
||||
void RosalinaMenu_ProcessList(void);
|
||||
void RosalinaMenu_PowerOff(void);
|
||||
void RosalinaMenu_Reboot(void);
|
||||
void RosalinaMenu_Cheats(void);
|
||||
|
||||
36
sysmodules/rosalina/include/menus/cheats.h
Normal file
36
sysmodules/rosalina/include/menus/cheats.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016-2017 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
|
||||
* * Requiring preservation of specified reasonable legal notices or
|
||||
* author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
* * Prohibiting misrepresentation of the origin of that material,
|
||||
* or requiring that modified versions of such material be marked in
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
/* This file was entirely written by Duckbill */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define CHEATS_PER_MENU_PAGE 18
|
||||
|
||||
void RosalinaMenu_Cheats(void);
|
||||
void Cheat_ApplyKeyCheats();
|
||||
@@ -35,5 +35,3 @@ void MiscellaneousMenu_SwitchBoot3dsxTargetTitle(void);
|
||||
void MiscellaneousMenu_ChangeMenuCombo(void);
|
||||
void MiscellaneousMenu_SaveSettings(void);
|
||||
void MiscellaneousMenu_InputRedirection(void);
|
||||
void MiscellaneousMenu_PowerOff(void);
|
||||
void MiscellaneousMenu_Reboot(void);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user