Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a9d0e2569 | ||
|
|
041ca8451e | ||
|
|
2382e6d82c | ||
|
|
f9a1f1a79b | ||
|
|
a736e4602a | ||
|
|
5202ba8826 | ||
|
|
0f1bc98bb7 | ||
|
|
d40722af53 | ||
|
|
c9781ab626 | ||
|
|
4cbf4e93e7 | ||
|
|
1f68c2da42 | ||
|
|
d786c292f3 | ||
|
|
ada3e09784 | ||
|
|
c928b3b68b | ||
|
|
ae9c29629c | ||
|
|
3fd4603553 | ||
|
|
c14526068a | ||
|
|
180b2a6f4d | ||
|
|
b926ab7dd2 | ||
|
|
19d8861d27 | ||
|
|
70e82e627b | ||
|
|
67e229e5a9 | ||
|
|
d0d6baaa85 | ||
|
|
fd4352a1d6 | ||
|
|
08808da741 | ||
|
|
a11124a3d6 | ||
|
|
d2d6e786c7 | ||
|
|
a76c943c01 | ||
|
|
3c64a3a234 | ||
|
|
06060c67b5 | ||
|
|
0f64fd73ec | ||
|
|
2323528975 | ||
|
|
22b38a0f88 | ||
|
|
034c0f8d7c | ||
|
|
b4cd67cb8c | ||
|
|
be83d77187 | ||
|
|
615cf81a9f | ||
|
|
c60ef7fe82 | ||
|
|
fea5a111a9 | ||
|
|
7eebfd4f6a | ||
|
|
939965b5a0 | ||
|
|
a0334120a6 | ||
|
|
308417a48e | ||
|
|
85533411c9 | ||
|
|
6d4a84a325 | ||
|
|
89350b1edd | ||
|
|
9e58e4ed7a | ||
|
|
f8f4ecea27 | ||
|
|
e9449f86bf | ||
|
|
4fabe1f704 | ||
|
|
ef3ba896d5 | ||
|
|
649b160292 | ||
|
|
697bc74535 | ||
|
|
0e9ff44746 | ||
|
|
49d49e637a | ||
|
|
b3f0d13aa4 | ||
|
|
bd1547710e | ||
|
|
7479bf8092 | ||
|
|
d4d281bb9c | ||
|
|
68bf134017 | ||
|
|
c7b90f739f | ||
|
|
160bc36ae8 | ||
|
|
464bf1680c | ||
|
|
c51eaba18c | ||
|
|
7dbded99a2 | ||
|
|
6351e5b8c3 | ||
|
|
5a0f2e4dfc | ||
|
|
6d06ea6975 | ||
|
|
4180261f1f | ||
|
|
e882dd7aaf | ||
|
|
9e851d2dfd | ||
|
|
21a3edb150 | ||
|
|
3aacbd17ce | ||
|
|
6f8a9421ef | ||
|
|
bb437f6f7b | ||
|
|
0001f301f8 | ||
|
|
f7bbc4bfec | ||
|
|
a127a38438 | ||
|
|
16225b97d7 | ||
|
|
0587c14162 | ||
|
|
a181bba9f2 | ||
|
|
993e564fbb | ||
|
|
956829864c | ||
|
|
24186a7148 | ||
|
|
3475cfe1e6 | ||
|
|
6b64a10362 | ||
|
|
5e99fb3aa0 | ||
|
|
060d8e9945 | ||
|
|
1026471842 | ||
|
|
645208ec82 | ||
|
|
f4c48a64ca | ||
|
|
c80ac985fe | ||
|
|
ac9bdc7665 | ||
|
|
217d75024d | ||
|
|
12e5b4adb9 | ||
|
|
945f80993b | ||
|
|
5f32779ceb | ||
|
|
e8ebb2f7fe | ||
|
|
034e63669a | ||
|
|
c75ed567fc | ||
|
|
1e2bf61dcf | ||
|
|
de4fea77a1 | ||
|
|
441c143b3e | ||
|
|
feff28a4fe | ||
|
|
b3d25ce64a | ||
|
|
0f44205959 | ||
|
|
1861d556a6 | ||
|
|
9b1df43687 | ||
|
|
56e0157d64 | ||
|
|
d23cd5acd5 | ||
|
|
39a9eb5ccb | ||
|
|
c413b6d07f | ||
|
|
85a59264c9 | ||
|
|
7f96e47b4f | ||
|
|
02b5c69802 | ||
|
|
b5eb108393 | ||
|
|
0a9cd09cc3 | ||
|
|
af451b4997 | ||
|
|
a5f8ccc3ea | ||
|
|
28cdcfed9c | ||
|
|
ea3e45d40a | ||
|
|
8daf3ebe3f | ||
|
|
7ad55ed61e | ||
|
|
96e46b2234 | ||
|
|
094b88b6aa | ||
|
|
03a6e343db | ||
|
|
b42f9ffb41 | ||
|
|
39280e397a | ||
|
|
887707ea87 | ||
|
|
279f39bc13 | ||
|
|
b7b734bad1 | ||
|
|
d01d9b53f2 | ||
|
|
c542bc5cf3 | ||
|
|
1f5b824c27 | ||
|
|
cb84a6c7ea | ||
|
|
61564e5fcf | ||
|
|
a437e533f5 | ||
|
|
6b88953517 | ||
|
|
9b9f784c26 | ||
|
|
6dfb33191f | ||
|
|
078fce4b3d | ||
|
|
dcb09a9472 | ||
|
|
8ce395caa5 | ||
|
|
e9410c86b1 | ||
|
|
42885560ed | ||
|
|
796e4dd060 | ||
|
|
9468582d83 | ||
|
|
6707a36ffe | ||
|
|
09cc7c903c | ||
|
|
9be7481c14 | ||
|
|
9f68ce0d70 | ||
|
|
4748c0292c | ||
|
|
c3ebce1666 | ||
|
|
b1a428f6bc | ||
|
|
3e0b928db0 | ||
|
|
09380a19ff | ||
|
|
04978ebb01 | ||
|
|
478ebeadf8 | ||
|
|
13fd33a61d | ||
|
|
4bdba9f8e9 | ||
|
|
99829b3cf7 | ||
|
|
d4a3ce2d0d | ||
|
|
a58cb05f2c | ||
|
|
9ab6d107b4 | ||
|
|
42cbead456 | ||
|
|
90ebe78c8e | ||
|
|
bf81ec252e | ||
|
|
899ad1887a | ||
|
|
cb06cf83ff | ||
|
|
aede5a5331 | ||
|
|
5a41663ddc | ||
|
|
7f6ef45f37 | ||
|
|
32e2b85f3a | ||
|
|
6fe748f58c | ||
|
|
e74eda16ce | ||
|
|
eea2076123 | ||
|
|
23fd26630f | ||
|
|
e53c186144 | ||
|
|
b6b309140b | ||
|
|
af83d31e8d | ||
|
|
9b58208c12 | ||
|
|
005855dccb | ||
|
|
723dd542ee | ||
|
|
30b3af634c | ||
|
|
38995d3231 | ||
|
|
5167c66127 | ||
|
|
b4d94da531 | ||
|
|
8872a243fc | ||
|
|
02bed5d37d | ||
|
|
7c70cc2c36 | ||
|
|
9f3178afec | ||
|
|
6c25ed4607 | ||
|
|
e34ca44715 | ||
|
|
899a6853a9 | ||
|
|
1131589c19 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,7 +1,8 @@
|
||||
out
|
||||
CakeHax
|
||||
CakeBrah
|
||||
build
|
||||
loader/build
|
||||
screeninit/build
|
||||
injector/build
|
||||
*.bin
|
||||
*.3dsx
|
||||
*.smdh
|
||||
|
||||
2
CakeBrah
2
CakeBrah
Submodule CakeBrah updated: aca4aa7d3b...42ebe0d0bc
2
CakeHax
2
CakeHax
Submodule CakeHax updated: 6b8fca0b37...5245c7b9dc
161
Makefile
161
Makefile
@@ -1,135 +1,122 @@
|
||||
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
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
OPENSSL := openssl
|
||||
|
||||
PYTHON3 := python
|
||||
PYTHON_VER_MAJOR := $(word 2, $(subst ., , $(shell python --version 2>&1)))
|
||||
ifneq ($(PYTHON_VER_MAJOR), 3)
|
||||
PYTHON3 := py -3
|
||||
endif
|
||||
|
||||
name := ReiNand
|
||||
name := Luma3DS
|
||||
version := $(shell git describe --abbrev=0 --tags)
|
||||
|
||||
dir_source := source
|
||||
dir_data := data
|
||||
dir_build := build
|
||||
dir_patches := patches
|
||||
dir_loader := loader
|
||||
dir_screeninit := screeninit
|
||||
dir_injector := injector
|
||||
dir_mset := CakeHax
|
||||
dir_out := out
|
||||
dir_emu := emunand
|
||||
dir_reboot := reboot
|
||||
dir_ninjhax := CakeBrah
|
||||
dir_build := build
|
||||
dir_out := out
|
||||
|
||||
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main
|
||||
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) --no-print-directory
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostartfiles
|
||||
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Aurora Wright" --no-print-directory
|
||||
|
||||
objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/screeninit.h
|
||||
|
||||
.PHONY: all
|
||||
all: launcher emunand emunando3ds reboot reboot2 rebootntr reboot2ntr ninjhax
|
||||
all: launcher a9lh ninjhax
|
||||
|
||||
.PHONY: launcher
|
||||
launcher: $(dir_out)/$(name).dat
|
||||
launcher: $(dir_out)/$(name).dat
|
||||
|
||||
.PHONY: emunand
|
||||
emunand: $(dir_out)/rei-n3ds/emunand/emunand.bin
|
||||
|
||||
.PHONY: emunando3ds
|
||||
emunand: $(dir_out)/rei-o3ds/emunand/emunand.bin
|
||||
|
||||
.PHONY: reboot
|
||||
reboot: $(dir_out)/rei-o3ds/reboot/reboot1.bin
|
||||
|
||||
.PHONY: reboot2
|
||||
reboot: $(dir_out)/rei-o3ds/reboot/reboot2.bin
|
||||
|
||||
.PHONY: rebootntr
|
||||
reboot: $(dir_out)/ntr-o3ds/reboot/reboot1.bin
|
||||
|
||||
.PHONY: reboot2ntr
|
||||
reboot: $(dir_out)/ntr-o3ds/reboot/reboot2.bin
|
||||
.PHONY: a9lh
|
||||
a9lh: $(dir_out)/arm9loaderhax.bin
|
||||
|
||||
.PHONY: ninjhax
|
||||
ninjhax: $(dir_out)/3ds/$(name)
|
||||
|
||||
.PHONY: release
|
||||
release: $(dir_out)/$(name)$(version).7z
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||
rm -rf $(dir_out) $(dir_build)
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) -C $(dir_screeninit) clean
|
||||
@$(MAKE) -C $(dir_injector) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
|
||||
.PHONY: $(dir_out)/$(name).dat
|
||||
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)/rei-n3ds/ $(dir_out)/rei-o3ds/
|
||||
$(dir_out):
|
||||
@mkdir -p "$(dir_out)/luma/payloads"
|
||||
|
||||
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
||||
dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||
|
||||
$(dir_out)/3ds/$(name):
|
||||
@mkdir -p "$(dir_out)/3ds/$(name)"
|
||||
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||
|
||||
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||
@cp -a $(dir_build)/main.bin $@
|
||||
|
||||
$(dir_out)/3ds/$(name): $(dir_out)
|
||||
@mkdir -p "$@"
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax)
|
||||
@mv $(dir_out)/$(name).3dsx $@
|
||||
@mv $(dir_out)/$(name).smdh $@
|
||||
@mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@
|
||||
|
||||
$(dir_out)/rei-n3ds/: $(dir_data)/firmware.bin
|
||||
@mkdir -p "$(dir_out)/rei-n3ds"
|
||||
@cp -av $(dir_data)/firmware.bin $@
|
||||
|
||||
$(dir_out)/rei-o3ds/: $(dir_data)/firmwareo3ds.bin
|
||||
@mkdir -p "$(dir_out)/rei-o3ds"
|
||||
@cp -av $(dir_data)/firmwareo3ds.bin $(dir_out)/rei-o3ds/firmware.bin
|
||||
|
||||
$(dir_out)/rei-n3ds/emunand/emunand.bin: $(dir_emu)/emuCode.s
|
||||
@armips $<
|
||||
@mkdir -p "$(dir_out)/rei-n3ds/emunand"
|
||||
@mv emunand.bin $(dir_out)/rei-n3ds/emunand
|
||||
|
||||
$(dir_out)/rei-o3ds/emunand/emunand.bin: $(dir_emu)/emuCodeo3ds.s
|
||||
@armips $<
|
||||
@mkdir -p "$(dir_out)/rei-o3ds/emunand"
|
||||
@mv emunand.bin $(dir_out)/rei-o3ds/emunand
|
||||
|
||||
$(dir_out)/rei-o3ds/reboot/reboot1.bin: $(dir_reboot)/rebootCode.s
|
||||
@armips $<
|
||||
@mkdir -p "$(dir_out)/rei-o3ds/reboot"
|
||||
@mv reboot1.bin $(dir_out)/rei-o3ds/reboot
|
||||
|
||||
$(dir_out)/rei-o3ds/reboot/reboot2.bin: $(dir_reboot)/rebootCode.s
|
||||
@mv reboot2.bin $(dir_out)/rei-o3ds/reboot
|
||||
|
||||
$(dir_out)/ntr-o3ds/reboot/reboot1.bin: $(dir_reboot)/rebootCodeNtr.s
|
||||
@armips $<
|
||||
@mkdir -p "$(dir_out)/ntr-o3ds/reboot"
|
||||
@mv reboot1.bin $(dir_out)/ntr-o3ds/reboot
|
||||
|
||||
$(dir_out)/ntr-o3ds/reboot/reboot2.bin: $(dir_reboot)/rebootCodeNtr.s
|
||||
@mv reboot2.bin $(dir_out)/ntr-o3ds/reboot
|
||||
$(dir_out)/$(name)$(version).7z: launcher a9lh ninjhax
|
||||
@7z a -mx $@ ./$(@D)/*
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/main.elf: $(objects_cfw)
|
||||
# FatFs requires libgcc for __aeabi_uidiv
|
||||
$(CC) -nostartfiles $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
$(dir_build)/main.elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s $(dir_injector)/Makefile
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@armips $(word 2,$^)
|
||||
@$(MAKE) -C $(dir_injector)
|
||||
@mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(@D)
|
||||
@bin2c -o $@ -n emunand $(@D)/emunand.bin -n reboot $(@D)/reboot.bin -n injector $(@D)/injector.cxi
|
||||
|
||||
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||
@$(MAKE) -C $(dir_loader)
|
||||
@mv $(dir_loader)/loader.bin $(@D)
|
||||
@bin2c -o $@ -n loader $(@D)/loader.bin
|
||||
|
||||
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
|
||||
@$(MAKE) -C $(dir_screeninit)
|
||||
@mv $(dir_screeninit)/screeninit.bin $(@D)
|
||||
@bin2c -o $@ -n screeninit $(@D)/screeninit.bin
|
||||
|
||||
$(dir_build)/memory.o : CFLAGS += -O3
|
||||
$(dir_build)/config.o : CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\""
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $<
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $<
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
31
README.md
31
README.md
@@ -1,26 +1,23 @@
|
||||
# AuReiNand
|
||||
*A modification of the ReiNand custom firmware*
|
||||
# Luma3DS
|
||||
*Noob-proof (N)3DS "Custom Firmware"*
|
||||
|
||||
**Compiling:**
|
||||
|
||||
You'll need armips added to your Path. [HERE](https://www.dropbox.com/s/ceuv2qeqp38lpah/armips.exe?dl=0) is a pre-compiled version.
|
||||
First you need to clone the repository recursively with: 'git clone --recursive https://github.com/AuroraWright/Luma3DS.git'
|
||||
To compile, you'll need [armips](https://github.com/Kingcom/armips), [bin2c](https://sourceforge.net/projects/bin2c/), and a recent build of [makerom](https://github.com/profi200/Project_CTR) added to your PATH.
|
||||
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 can find the compiled files in the 'out' folder.
|
||||
|
||||
Lastly, just run Make and everything should work!
|
||||
**Setup / Usage / Features:**
|
||||
|
||||
Copy everything in 'out' folder to SD root and run!
|
||||
|
||||
**Usage / Features:**
|
||||
|
||||
See https://github.com/Reisyukaku/ReiNand and http://gbatemp.net/threads/reinand-mod-o3ds-n3ds-sysnand.411110
|
||||
|
||||
The FIRMs you need are here:
|
||||
|
||||
Latest (10.4): http://www95.zippyshare.com/v/78942FBa/file.html
|
||||
|
||||
NTR-compatible (10.2 for N3DS, 9.6 for O3DS): http://www66.zippyshare.com/v/gUbSYGfE/file.html
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki
|
||||
|
||||
**Credits:**
|
||||
|
||||
Rei as this is 99% his code (this is no more than a mod)
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
||||
|
||||
Everyone he credited on his GitHub :P
|
||||
**Licensing:**
|
||||
|
||||
This software is licensed under the terms of the GPLv3.
|
||||
You can find a copy of the license in the LICENSE.txt file.
|
||||
@@ -1,50 +0,0 @@
|
||||
.nds
|
||||
|
||||
sdmmc equ 0x434D4453 ;dummy
|
||||
|
||||
.create "emunand.bin", 0x0801A4C0
|
||||
.org 0x0801A4C0
|
||||
.arm
|
||||
nand_sd:
|
||||
; Original code that still needs to be executed.
|
||||
mov r4, r0
|
||||
mov r5, r1
|
||||
mov r7, r2
|
||||
mov r6, r3
|
||||
; End.
|
||||
|
||||
; If we're already trying to access the SD, return.
|
||||
ldr r2, [r0, #4]
|
||||
ldr r1, =sdmmc
|
||||
cmp r2, r1
|
||||
beq nand_sd_ret
|
||||
|
||||
str r1, [r0, #4] ; Set object to be SD
|
||||
ldr r2, [r0, #8] ; Get sector to read
|
||||
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
|
||||
ldr r3, =nand_offset
|
||||
ldr r3, [r3]
|
||||
add r2, r3 ; Add the offset to the NAND in the SD.
|
||||
|
||||
ldreq r3, =ncsd_header_offset
|
||||
ldreq r3, [r3]
|
||||
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector.
|
||||
|
||||
str r2, [r0, #8] ; Store sector to read
|
||||
|
||||
nand_sd_ret:
|
||||
; Restore registers.
|
||||
mov r1, r5
|
||||
mov r2, r7
|
||||
mov r3, r6
|
||||
|
||||
; Return 4 bytes behind where we got called,
|
||||
; due to the offset of this function being stored there.
|
||||
mov r0, lr
|
||||
add r0, #4
|
||||
bx r0
|
||||
.pool
|
||||
nand_offset: .ascii "NAND" ; for rednand this should be 1
|
||||
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
|
||||
.close
|
||||
BIN
icon.png
BIN
icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 1.6 KiB |
21
injector/LICENSE
Normal file
21
injector/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yifan Lu
|
||||
|
||||
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.
|
||||
51
injector/Makefile
Executable file
51
injector/Makefile
Executable file
@@ -0,0 +1,51 @@
|
||||
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
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_build := 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 -flto -ffast-math -mword-relocations \
|
||||
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH)
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.c))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).cxi
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).cxi: $(dir_build)/$(name).elf
|
||||
@makerom -f ncch -rsf loader.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)
|
||||
30
injector/README.md
Normal file
30
injector/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
3DS Loader Replacement
|
||||
======================
|
||||
|
||||
This is an open source implementation of 3DS `loader` system module--with
|
||||
additional features. The current aim of the project is to provide a nice
|
||||
entry point for patching 3DS modules.
|
||||
|
||||
## Roadmap
|
||||
Right now, this can serve as an open-source replacement for the built in loader.
|
||||
There is additional support for patching any executable after it's loaded but
|
||||
before it starts. For example, you can patch `menu` to skip region checks and
|
||||
have region free game launching directly from the home menu. There is also
|
||||
support for SDMC reading (not found in original loader implementation) which
|
||||
means that patches can be loaded from the SD card. Ultimately, there would be
|
||||
a patch system that supports easy loading of patches from the SD card.
|
||||
|
||||
## Build
|
||||
You need a working 3DS build environment with a fairly recent copy of devkitARM,
|
||||
ctrulib, and makerom. If you see any errors in the build process, it's likely
|
||||
that you're using an older version.
|
||||
|
||||
Currently, there is no support for FIRM building, so you need to do some steps
|
||||
manually. First, you have to add padding to make sure the NCCH is of the right
|
||||
size to drop in as a replacement. A hacky way is
|
||||
[this patch](http://pastebin.com/nyKXLnNh) which adds junk data. Play around
|
||||
with the size value to get the NCCH to be the exact same size as the one
|
||||
found in your decrypted FIRM dump.
|
||||
|
||||
Once you have a NCCH of the right size, just replace it in your decrypted FIRM
|
||||
and find a way to launch it (for example with ReiNAND).
|
||||
115
injector/loader.rsf
Normal file
115
injector/loader.rsf
Normal file
@@ -0,0 +1,115 @@
|
||||
BasicInfo:
|
||||
Title : loader
|
||||
CompanyCode : "00"
|
||||
ProductCode : 0828builder
|
||||
ContentType : Application
|
||||
Logo : None
|
||||
|
||||
TitleInfo:
|
||||
UniqueId : 0x13
|
||||
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 : 20
|
||||
|
||||
DisableDebug : true
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : false
|
||||
PermitMainFunctionArgument : false
|
||||
CanShareDeviceMemory : false
|
||||
RunnableOnSleep : true
|
||||
SpecialMemoryArrange : true
|
||||
ResourceLimitCategory : Other
|
||||
|
||||
CoreVersion : 2
|
||||
DescVersion : 2
|
||||
|
||||
MemoryType : Base # Application / System / Base
|
||||
HandleTableSize: 0
|
||||
SystemCallAccess:
|
||||
AcceptSession: 74
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateCodeSet: 115
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreatePort: 71
|
||||
CreateProcess: 117
|
||||
CreateSemaphore: 21
|
||||
CreateSessionToPort: 72
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
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
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
RandomStub: 116
|
||||
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
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
InterruptNumbers:
|
||||
ServiceAccessControl:
|
||||
- fs:LDR
|
||||
FileSystemAccess:
|
||||
- DirectSdmc
|
||||
- CtrNandRw
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 0KB # It doesn't use any save data.
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x1000
|
||||
97
injector/source/exheader.h
Executable file
97
injector/source/exheader.h
Executable file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 reserved[5];
|
||||
u8 flag;
|
||||
u8 remasterversion[2];
|
||||
} PACKED exheader_systeminfoflags;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 address;
|
||||
u32 nummaxpages;
|
||||
u32 codesize;
|
||||
} PACKED exheader_codesegmentinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 name[8];
|
||||
exheader_systeminfoflags flags;
|
||||
exheader_codesegmentinfo text;
|
||||
u8 stacksize[4];
|
||||
exheader_codesegmentinfo ro;
|
||||
u8 reserved[4];
|
||||
exheader_codesegmentinfo data;
|
||||
u32 bsssize;
|
||||
} PACKED exheader_codesetinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 programid[0x30];
|
||||
} PACKED exheader_dependencylist;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 savedatasize[4];
|
||||
u8 reserved[4];
|
||||
u8 jumpid[8];
|
||||
u8 reserved2[0x30];
|
||||
} PACKED exheader_systeminfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 extsavedataid[8];
|
||||
u8 systemsavedataid[8];
|
||||
u8 reserved[8];
|
||||
u8 accessinfo[7];
|
||||
u8 otherattributes;
|
||||
} PACKED exheader_storageinfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 programid;
|
||||
u8 flags[8];
|
||||
u16 resourcelimitdescriptor[0x10];
|
||||
exheader_storageinfo storageinfo;
|
||||
u64 serviceaccesscontrol[0x20];
|
||||
u8 reserved[0x1f];
|
||||
u8 resourcelimitcategory;
|
||||
} PACKED exheader_arm11systemlocalcaps;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 descriptors[28];
|
||||
u8 reserved[0x10];
|
||||
} PACKED exheader_arm11kernelcapabilities;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8 descriptors[15];
|
||||
u8 descversion;
|
||||
} PACKED exheader_arm9accesscontrol;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// systemcontrol info {
|
||||
// coreinfo {
|
||||
exheader_codesetinfo codesetinfo;
|
||||
exheader_dependencylist deplist;
|
||||
// }
|
||||
exheader_systeminfo systeminfo;
|
||||
// }
|
||||
// accesscontrolinfo {
|
||||
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||
exheader_arm9accesscontrol arm9accesscontrol;
|
||||
// }
|
||||
struct {
|
||||
u8 signature[0x100];
|
||||
u8 ncchpubkeymodulus[0x100];
|
||||
exheader_arm11systemlocalcaps arm11systemlocalcaps;
|
||||
exheader_arm11kernelcapabilities arm11kernelcaps;
|
||||
exheader_arm9accesscontrol arm9accesscontrol;
|
||||
} PACKED accessdesc;
|
||||
} PACKED exheader_header;
|
||||
109
injector/source/fsldr.c
Normal file
109
injector/source/fsldr.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <3ds.h>
|
||||
#include "fsldr.h"
|
||||
#include "fsreg.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
#define SDK_VERSION 0x70200C8
|
||||
|
||||
static Handle fsldrHandle;
|
||||
static int fsldrRefCount;
|
||||
|
||||
// MAKE SURE fsreg has been init before calling this
|
||||
static Result fsldrPatchPermissions(void)
|
||||
{
|
||||
u32 pid;
|
||||
Result res;
|
||||
FS_ProgramInfo info;
|
||||
u32 storage[8] = {0};
|
||||
|
||||
storage[6] = 0x680; // SDMC access and NAND access flag
|
||||
info.programId = 0x0004013000001302LL; // loader PID
|
||||
info.mediaType = MEDIATYPE_NAND;
|
||||
res = svcGetProcessId(&pid, 0xFFFF8001);
|
||||
if (R_SUCCEEDED(res))
|
||||
{
|
||||
res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, (u8 *)storage);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result fsldrInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&fsldrRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&fsldrHandle, "fs:LDR");
|
||||
if (R_SUCCEEDED(ret))
|
||||
{
|
||||
fsldrPatchPermissions();
|
||||
ret = FSLDR_InitializeWithSdkVersion(fsldrHandle, SDK_VERSION);
|
||||
ret = FSLDR_SetPriority(0);
|
||||
if (R_FAILED(ret)) svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
else
|
||||
{
|
||||
AtomicDecrement(&fsldrRefCount);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsldrExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&fsldrRefCount)) return;
|
||||
svcCloseHandle(fsldrHandle);
|
||||
}
|
||||
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x861,1,2); // 0x8610042
|
||||
cmdbuf[1] = version;
|
||||
cmdbuf[2] = 32;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(session))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_SetPriority(u32 priority)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x862,1,0); // 0x8620040
|
||||
cmdbuf[1] = priority;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_Archive archive, FS_Path path, u32 openFlags, u32 attributes)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x803,8,4); // 0x8030204
|
||||
cmdbuf[1] = 0;
|
||||
cmdbuf[2] = archive.id;
|
||||
cmdbuf[3] = archive.lowPath.type;
|
||||
cmdbuf[4] = archive.lowPath.size;
|
||||
cmdbuf[5] = path.type;
|
||||
cmdbuf[6] = path.size;
|
||||
cmdbuf[7] = openFlags;
|
||||
cmdbuf[8] = attributes;
|
||||
cmdbuf[9] = IPC_Desc_StaticBuffer(archive.lowPath.size, 2);
|
||||
cmdbuf[10] = (u32) archive.lowPath.data;
|
||||
cmdbuf[11] = IPC_Desc_StaticBuffer(path.size, 0);
|
||||
cmdbuf[12] = (u32) path.data;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsldrHandle))) return ret;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
9
injector/source/fsldr.h
Normal file
9
injector/source/fsldr.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
Result fsldrInit(void);
|
||||
void fsldrExit(void);
|
||||
Result FSLDR_InitializeWithSdkVersion(Handle session, u32 version);
|
||||
Result FSLDR_SetPriority(u32 priority);
|
||||
Result FSLDR_OpenFileDirectly(Handle* out, FS_Archive archive, FS_Path path, u32 openFlags, u32 attributes);
|
||||
116
injector/source/fsreg.c
Normal file
116
injector/source/fsreg.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "fsreg.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle fsregHandle;
|
||||
static int fsregRefCount;
|
||||
|
||||
Result fsregInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&fsregRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&fsregHandle, "fs:REG");
|
||||
|
||||
if (R_FAILED(ret)) AtomicDecrement(&fsregRefCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsregExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&fsregRefCount)) return;
|
||||
svcCloseHandle(fsregHandle);
|
||||
}
|
||||
|
||||
Result FSREG_CheckHostLoadId(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x406,2,0); // 0x4060080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x404,4,0); // 0x4040100
|
||||
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
*prog_handle = *(u64 *)&cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x403,3,0); // 0x40300C0
|
||||
cmdbuf[1] = entry_count;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
cmdbuf[64] = ((entry_count << 10) << 14) | 2;
|
||||
cmdbuf[65] = (u32) exheader;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_UnloadProgram(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x405,2,0); // 0x4050080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_Unregister(u32 pid)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x402,1,0); // 0x4020040
|
||||
cmdbuf[1] = pid;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x401,0xf,0); // 0x40103C0
|
||||
cmdbuf[1] = pid;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
memcpy(&cmdbuf[4], &info->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[6] = info->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[6])+1, &info->padding, 7);
|
||||
memcpy((u8 *)&cmdbuf[8], storageinfo, 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(fsregHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
13
injector/source/fsreg.h
Normal file
13
injector/source/fsreg.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "exheader.h"
|
||||
|
||||
Result fsregInit(void);
|
||||
void fsregExit(void);
|
||||
Result FSREG_CheckHostLoadId(u64 prog_handle);
|
||||
Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title);
|
||||
Result FSREG_GetProgramInfo(exheader_header *exheader, u32 entry_count, u64 prog_handle);
|
||||
Result FSREG_UnloadProgram(u64 prog_handle);
|
||||
Result FSREG_Unregister(u32 pid);
|
||||
Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo);
|
||||
66
injector/source/ifile.c
Normal file
66
injector/source/ifile.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <3ds.h>
|
||||
#include "ifile.h"
|
||||
#include "fsldr.h"
|
||||
|
||||
Result IFile_Open(IFile *file, FS_Archive archive, FS_Path path, u32 flags)
|
||||
{
|
||||
Result res;
|
||||
|
||||
res = FSLDR_OpenFileDirectly(&file->handle, archive, path, flags, 0);
|
||||
file->pos = 0;
|
||||
file->size = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result IFile_Close(IFile *file)
|
||||
{
|
||||
return FSFILE_Close(file->handle);
|
||||
}
|
||||
|
||||
Result IFile_GetSize(IFile *file, u64 *size)
|
||||
{
|
||||
Result res;
|
||||
|
||||
res = FSFILE_GetSize(file->handle, size);
|
||||
file->size = *size;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len)
|
||||
{
|
||||
u32 read;
|
||||
u32 left;
|
||||
char *buf;
|
||||
u64 cur;
|
||||
Result res;
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
*total = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = (char *)buffer;
|
||||
cur = 0;
|
||||
left = len;
|
||||
while (1)
|
||||
{
|
||||
res = FSFILE_Read(file->handle, &read, file->pos, buf, left);
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cur += read;
|
||||
file->pos += read;
|
||||
if (read == left)
|
||||
{
|
||||
break;
|
||||
}
|
||||
buf += read;
|
||||
left -= read;
|
||||
}
|
||||
|
||||
*total = cur;
|
||||
return res;
|
||||
}
|
||||
15
injector/source/ifile.h
Normal file
15
injector/source/ifile.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Handle handle;
|
||||
u64 pos;
|
||||
u64 size;
|
||||
} IFile;
|
||||
|
||||
Result IFile_Open(IFile *file, FS_Archive archive, FS_Path path, u32 flags);
|
||||
Result IFile_Close(IFile *file);
|
||||
Result IFile_GetSize(IFile *file, u64 *size);
|
||||
Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len);
|
||||
592
injector/source/loader.c
Normal file
592
injector/source/loader.c
Normal file
@@ -0,0 +1,592 @@
|
||||
#include <3ds.h>
|
||||
#include "memory.h"
|
||||
#include "patcher.h"
|
||||
#include "exheader.h"
|
||||
#include "ifile.h"
|
||||
#include "fsldr.h"
|
||||
#include "fsreg.h"
|
||||
#include "pxipm.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
#define MAX_SESSIONS 1
|
||||
|
||||
const char CODE_PATH[] = {0x01, 0x00, 0x00, 0x00, 0x2E, 0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u32 text_addr;
|
||||
u32 text_size;
|
||||
u32 ro_addr;
|
||||
u32 ro_size;
|
||||
u32 data_addr;
|
||||
u32 data_size;
|
||||
u32 total_size;
|
||||
} prog_addrs_t;
|
||||
|
||||
static Handle g_handles[MAX_SESSIONS+2];
|
||||
static int g_active_handles;
|
||||
static u64 g_cached_prog_handle;
|
||||
static exheader_header g_exheader;
|
||||
static char g_ret_buf[1024];
|
||||
|
||||
static int lzss_decompress(u8 *end)
|
||||
{
|
||||
unsigned int v1; // r1@2
|
||||
u8 *v2; // r2@2
|
||||
u8 *v3; // r3@2
|
||||
u8 *v4; // r1@2
|
||||
char v5; // r5@4
|
||||
char v6; // t1@4
|
||||
signed int v7; // r6@4
|
||||
int v9; // t1@7
|
||||
u8 *v11; // r3@8
|
||||
int v12; // r12@8
|
||||
int v13; // t1@8
|
||||
int v14; // t1@8
|
||||
unsigned int v15; // r7@8
|
||||
int v16; // r12@8
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
if ( end )
|
||||
{
|
||||
v1 = *((u32 *)end - 2);
|
||||
v2 = &end[*((u32 *)end - 1)];
|
||||
v3 = &end[-(v1 >> 24)];
|
||||
v4 = &end[-(v1 & 0xFFFFFF)];
|
||||
while ( v3 > v4 )
|
||||
{
|
||||
v6 = *(v3-- - 1);
|
||||
v5 = v6;
|
||||
v7 = 8;
|
||||
while ( 1 )
|
||||
{
|
||||
if ( (v7-- < 1) )
|
||||
break;
|
||||
if ( v5 & 0x80 )
|
||||
{
|
||||
v13 = *(v3 - 1);
|
||||
v11 = v3 - 1;
|
||||
v12 = v13;
|
||||
v14 = *(v11 - 1);
|
||||
v3 = v11 - 1;
|
||||
v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2;
|
||||
v16 = v12 + 32;
|
||||
do
|
||||
{
|
||||
ret = v2[v15];
|
||||
*(v2-- - 1) = ret;
|
||||
v16 -= 16;
|
||||
}
|
||||
while ( !(v16 < 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
v9 = *(v3-- - 1);
|
||||
ret = v9;
|
||||
*(v2-- - 1) = v9;
|
||||
}
|
||||
v5 *= 2;
|
||||
if ( v3 <= v4 )
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Result allocate_shared_mem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
||||
{
|
||||
u32 dummy;
|
||||
|
||||
memcpy(shared, vaddr, sizeof(prog_addrs_t));
|
||||
shared->text_addr = 0x10000000;
|
||||
shared->ro_addr = shared->text_addr + (shared->text_size << 12);
|
||||
shared->data_addr = shared->ro_addr + (shared->ro_size << 12);
|
||||
return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
|
||||
}
|
||||
|
||||
static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int is_compressed)
|
||||
{
|
||||
IFile file;
|
||||
FS_Archive archive;
|
||||
FS_Path path;
|
||||
Result res;
|
||||
u64 size;
|
||||
u64 total;
|
||||
|
||||
archive.id = ARCHIVE_SAVEDATA_AND_CONTENT2;
|
||||
archive.lowPath.type = PATH_BINARY;
|
||||
archive.lowPath.data = &prog_handle;
|
||||
archive.lowPath.size = 8;
|
||||
//archive.handle = prog_handle; // not needed
|
||||
path.type = PATH_BINARY;
|
||||
path.data = CODE_PATH;
|
||||
path.size = sizeof(CODE_PATH);
|
||||
if (R_FAILED(IFile_Open(&file, archive, path, FS_OPEN_READ)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// get file size
|
||||
if (R_FAILED(IFile_GetSize(&file, &size)))
|
||||
{
|
||||
IFile_Close(&file);
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// check size
|
||||
if (size > (u64)shared->total_size << 12)
|
||||
{
|
||||
IFile_Close(&file);
|
||||
return 0xC900464F;
|
||||
}
|
||||
|
||||
// read code
|
||||
res = IFile_Read(&file, &total, (void *)shared->text_addr, size);
|
||||
IFile_Close(&file); // done reading
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
// decompress
|
||||
if (is_compressed)
|
||||
{
|
||||
lzss_decompress((u8 *)shared->text_addr + size);
|
||||
}
|
||||
|
||||
// patch
|
||||
patchCode(progid, (u8 *)shared->text_addr, shared->total_size << 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
|
||||
if (prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
//so use PXIPM if FSREG fails OR returns "info", is the second condition a bug?
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return PXIPM_GetProgramInfo(exheader, prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FSREG_GetProgramInfo(exheader, 1, prog_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result loader_LoadProcess(Handle *process, u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
int count;
|
||||
u32 flags;
|
||||
u32 desc;
|
||||
u32 dummy;
|
||||
prog_addrs_t shared_addr;
|
||||
prog_addrs_t vaddr;
|
||||
Handle codeset;
|
||||
CodeSetInfo codesetinfo;
|
||||
u32 data_mem_size;
|
||||
u64 progid;
|
||||
|
||||
// make sure the cached info corrosponds to the current prog_handle
|
||||
if (g_cached_prog_handle != prog_handle)
|
||||
{
|
||||
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||
g_cached_prog_handle = prog_handle;
|
||||
if (res < 0)
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// get kernel flags
|
||||
flags = 0;
|
||||
for (count = 0; count < 28; count++)
|
||||
{
|
||||
desc = g_exheader.arm11kernelcaps.descriptors[count];
|
||||
if (0x1FE == desc >> 23)
|
||||
{
|
||||
flags = desc & 0xF00;
|
||||
}
|
||||
}
|
||||
if (flags == 0)
|
||||
{
|
||||
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
|
||||
}
|
||||
|
||||
// allocate process memory
|
||||
vaddr.text_addr = g_exheader.codesetinfo.text.address;
|
||||
vaddr.text_size = (g_exheader.codesetinfo.text.codesize + 4095) >> 12;
|
||||
vaddr.ro_addr = g_exheader.codesetinfo.ro.address;
|
||||
vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + 4095) >> 12;
|
||||
vaddr.data_addr = g_exheader.codesetinfo.data.address;
|
||||
vaddr.data_size = (g_exheader.codesetinfo.data.codesize + 4095) >> 12;
|
||||
data_mem_size = (g_exheader.codesetinfo.data.codesize + g_exheader.codesetinfo.bsssize + 4095) >> 12;
|
||||
vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
|
||||
if ((res = allocate_shared_mem(&shared_addr, &vaddr, flags)) < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
// load code
|
||||
progid = g_exheader.arm11systemlocalcaps.programid;
|
||||
if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
|
||||
{
|
||||
memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8);
|
||||
codesetinfo.program_id = progid;
|
||||
codesetinfo.text_addr = vaddr.text_addr;
|
||||
codesetinfo.text_size = vaddr.text_size;
|
||||
codesetinfo.text_size_total = vaddr.text_size;
|
||||
codesetinfo.ro_addr = vaddr.ro_addr;
|
||||
codesetinfo.ro_size = vaddr.ro_size;
|
||||
codesetinfo.ro_size_total = vaddr.ro_size;
|
||||
codesetinfo.rw_addr = vaddr.data_addr;
|
||||
codesetinfo.rw_size = vaddr.data_size;
|
||||
codesetinfo.rw_size_total = data_mem_size;
|
||||
res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)shared_addr.text_addr, (void *)shared_addr.ro_addr, (void *)shared_addr.data_addr);
|
||||
if (res >= 0)
|
||||
{
|
||||
res = svcCreateProcess(process, codeset, g_exheader.arm11kernelcaps.descriptors, count);
|
||||
svcCloseHandle(codeset);
|
||||
if (res >= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svcControlMemory(&dummy, shared_addr.text_addr, 0, shared_addr.total_size << 12, MEMOP_FREE, 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result loader_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||
{
|
||||
Result res;
|
||||
u64 prog_id;
|
||||
|
||||
prog_id = title->programId;
|
||||
if (prog_id >> 32 != 0xFFFF0000)
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_id);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
res = PXIPM_RegisterProgram(prog_handle, title, update);
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
if (*prog_handle >> 32 != 0xFFFF0000)
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
|
||||
if ((title->mediaType != update->mediaType) || (prog_id != update->programId))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
res = FSREG_LoadProgram(prog_handle, title);
|
||||
if (R_SUCCEEDED(res))
|
||||
{
|
||||
if (*prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
res = FSREG_CheckHostLoadId(*prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Result loader_UnregisterProgram(u64 prog_handle)
|
||||
{
|
||||
Result res;
|
||||
|
||||
if (prog_handle >> 32 == 0xFFFF0000)
|
||||
{
|
||||
return FSREG_UnloadProgram(prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = FSREG_CheckHostLoadId(prog_handle);
|
||||
//if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32))
|
||||
if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS))
|
||||
{
|
||||
return PXIPM_UnregisterProgram(prog_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FSREG_UnloadProgram(prog_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_commands(void)
|
||||
{
|
||||
FS_ProgramInfo title;
|
||||
FS_ProgramInfo update;
|
||||
u32* cmdbuf;
|
||||
u16 cmdid;
|
||||
int res;
|
||||
Handle handle;
|
||||
u64 prog_handle;
|
||||
|
||||
cmdbuf = getThreadCommandBuffer();
|
||||
cmdid = cmdbuf[0] >> 16;
|
||||
res = 0;
|
||||
switch (cmdid)
|
||||
{
|
||||
case 1: // LoadProcess
|
||||
{
|
||||
res = loader_LoadProcess(&handle, *(u64 *)&cmdbuf[1]);
|
||||
cmdbuf[0] = 0x10042;
|
||||
cmdbuf[1] = res;
|
||||
cmdbuf[2] = 16;
|
||||
cmdbuf[3] = handle;
|
||||
break;
|
||||
}
|
||||
case 2: // RegisterProgram
|
||||
{
|
||||
memcpy(&title, &cmdbuf[1], sizeof(FS_ProgramInfo));
|
||||
memcpy(&update, &cmdbuf[5], sizeof(FS_ProgramInfo));
|
||||
res = loader_RegisterProgram(&prog_handle, &title, &update);
|
||||
cmdbuf[0] = 0x200C0;
|
||||
cmdbuf[1] = res;
|
||||
*(u64 *)&cmdbuf[2] = prog_handle;
|
||||
break;
|
||||
}
|
||||
case 3: // UnregisterProgram
|
||||
{
|
||||
if (g_cached_prog_handle == prog_handle)
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
}
|
||||
cmdbuf[0] = 0x30040;
|
||||
cmdbuf[1] = loader_UnregisterProgram(*(u64 *)&cmdbuf[1]);
|
||||
break;
|
||||
}
|
||||
case 4: // GetProgramInfo
|
||||
{
|
||||
prog_handle = *(u64 *)&cmdbuf[1];
|
||||
if (prog_handle != g_cached_prog_handle)
|
||||
{
|
||||
res = loader_GetProgramInfo(&g_exheader, prog_handle);
|
||||
if (res >= 0)
|
||||
{
|
||||
g_cached_prog_handle = prog_handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_cached_prog_handle = 0;
|
||||
}
|
||||
}
|
||||
memcpy(&g_ret_buf, &g_exheader, 1024);
|
||||
cmdbuf[0] = 0x40042;
|
||||
cmdbuf[1] = res;
|
||||
cmdbuf[2] = 0x1000002;
|
||||
cmdbuf[3] = (u32) &g_ret_buf;
|
||||
break;
|
||||
}
|
||||
default: // error
|
||||
{
|
||||
cmdbuf[0] = 0x40;
|
||||
cmdbuf[1] = 0xD900182F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result should_terminate(int *term_request)
|
||||
{
|
||||
u32 notid;
|
||||
Result ret;
|
||||
|
||||
ret = srvSysReceiveNotification(¬id);
|
||||
if (R_FAILED(ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (notid == 0x100) // term request
|
||||
{
|
||||
*term_request = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this is called before main
|
||||
void __appInit()
|
||||
{
|
||||
srvSysInit();
|
||||
fsregInit();
|
||||
fsldrInit();
|
||||
pxipmInit();
|
||||
}
|
||||
|
||||
// this is called after main exits
|
||||
void __appExit()
|
||||
{
|
||||
pxipmExit();
|
||||
fsldrExit();
|
||||
fsregExit();
|
||||
srvSysExit();
|
||||
}
|
||||
|
||||
// stubs for non-needed pre-main functions
|
||||
void __sync_init();
|
||||
void __sync_fini();
|
||||
void __system_initSyscalls();
|
||||
|
||||
void __ctru_exit()
|
||||
{
|
||||
__appExit();
|
||||
__sync_fini();
|
||||
svcExitProcess();
|
||||
}
|
||||
|
||||
void initSystem()
|
||||
{
|
||||
__sync_init();
|
||||
__system_initSyscalls();
|
||||
__appInit();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Result ret;
|
||||
Handle handle;
|
||||
Handle reply_target;
|
||||
Handle *srv_handle;
|
||||
Handle *notification_handle;
|
||||
s32 index;
|
||||
int i;
|
||||
int term_request;
|
||||
u32* cmdbuf;
|
||||
|
||||
ret = 0;
|
||||
|
||||
srv_handle = &g_handles[1];
|
||||
notification_handle = &g_handles[0];
|
||||
|
||||
if (R_FAILED(srvSysRegisterService(srv_handle, "Loader", MAX_SESSIONS)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
if (R_FAILED(srvSysEnableNotification(notification_handle)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
|
||||
g_active_handles = 2;
|
||||
g_cached_prog_handle = 0;
|
||||
index = 1;
|
||||
|
||||
reply_target = 0;
|
||||
term_request = 0;
|
||||
do
|
||||
{
|
||||
if (reply_target == 0)
|
||||
{
|
||||
cmdbuf = getThreadCommandBuffer();
|
||||
cmdbuf[0] = 0xFFFF0000;
|
||||
}
|
||||
ret = svcReplyAndReceive(&index, g_handles, g_active_handles, reply_target);
|
||||
|
||||
if (R_FAILED(ret))
|
||||
{
|
||||
// check if any handle has been closed
|
||||
if (ret == (int)0xC920181A)
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
for (i = 2; i < MAX_SESSIONS+2; i++)
|
||||
{
|
||||
if (g_handles[i] == reply_target)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
svcCloseHandle(g_handles[index]);
|
||||
g_handles[index] = g_handles[g_active_handles-1];
|
||||
g_active_handles--;
|
||||
reply_target = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// process responses
|
||||
reply_target = 0;
|
||||
switch (index)
|
||||
{
|
||||
case 0: // notification
|
||||
{
|
||||
if (R_FAILED(should_terminate(&term_request)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: // new session
|
||||
{
|
||||
if (R_FAILED(svcAcceptSession(&handle, *srv_handle)))
|
||||
{
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
if (g_active_handles < MAX_SESSIONS+2)
|
||||
{
|
||||
g_handles[g_active_handles] = handle;
|
||||
g_active_handles++;
|
||||
}
|
||||
else
|
||||
{
|
||||
svcCloseHandle(handle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // session
|
||||
{
|
||||
handle_commands();
|
||||
reply_target = g_handles[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!term_request || g_active_handles != 2);
|
||||
|
||||
srvSysUnregisterService("Loader");
|
||||
svcCloseHandle(*srv_handle);
|
||||
svcCloseHandle(*notification_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
injector/source/memory.c
Normal file
10
injector/source/memory.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "memory.h"
|
||||
|
||||
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];
|
||||
}
|
||||
5
injector/source/memory.h
Normal file
5
injector/source/memory.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
516
injector/source/patcher.c
Normal file
516
injector/source/patcher.c
Normal file
@@ -0,0 +1,516 @@
|
||||
#include <3ds.h>
|
||||
#include "memory.h"
|
||||
#include "patcher.h"
|
||||
#include "ifile.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 255
|
||||
#define CONFIG(a) ((loadConfig() >> (a + 16)) & 1)
|
||||
#define MULTICONFIG(a) ((loadConfig() >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((loadConfig() >> a) & b)
|
||||
#endif
|
||||
|
||||
static int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1;
|
||||
const u8 *buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if(cmp) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
u32 table[256];
|
||||
|
||||
for(u32 i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(u32 i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
u8 *found = memsearch(start, pattern, size, patSize);
|
||||
|
||||
if(found == NULL) break;
|
||||
|
||||
memcpy(found + offset, replace, repSize);
|
||||
|
||||
u32 at = (u32)(found - start);
|
||||
|
||||
if(at + patSize > size) break;
|
||||
|
||||
size -= at + patSize;
|
||||
start = found + patSize;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline size_t strnlen(const char *string, size_t maxlen)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
for(size = 0; *string && size < maxlen; string++, size++);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int fileOpen(IFile *file, FS_ArchiveID id, const char *path, int flags)
|
||||
{
|
||||
FS_Archive archive;
|
||||
FS_Path ppath;
|
||||
|
||||
size_t len = strnlen(path, PATH_MAX);
|
||||
archive.id = id;
|
||||
archive.lowPath.type = PATH_EMPTY;
|
||||
archive.lowPath.size = 1;
|
||||
archive.lowPath.data = (u8 *)"";
|
||||
ppath.type = PATH_ASCII;
|
||||
ppath.data = path;
|
||||
ppath.size = len + 1;
|
||||
|
||||
return IFile_Open(file, archive, ppath, flags);
|
||||
}
|
||||
|
||||
static u32 secureInfoExists(void)
|
||||
{
|
||||
static u32 secureInfoExists = 0;
|
||||
|
||||
if(!secureInfoExists)
|
||||
{
|
||||
IFile file;
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
||||
{
|
||||
secureInfoExists = 1;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
return secureInfoExists;
|
||||
}
|
||||
|
||||
static u32 loadConfig(void)
|
||||
{
|
||||
static u32 config = 0;
|
||||
|
||||
if(!config)
|
||||
{
|
||||
IFile file;
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/luma/config.bin", FS_OPEN_READ)))
|
||||
{
|
||||
u64 total;
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, &config, 4))) config |= 1 << 4;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
{
|
||||
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
|
||||
char path[] = "/luma/locales/0000000000000000.txt";
|
||||
|
||||
u32 i = 29;
|
||||
|
||||
while(progId)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
path[i--] = hexDigits[(u32)(progId & 0xF)];
|
||||
progId >>= 4;
|
||||
}
|
||||
|
||||
IFile file;
|
||||
Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
if(R_SUCCEEDED(ret))
|
||||
{
|
||||
char buf[6];
|
||||
u64 total;
|
||||
|
||||
ret = IFile_Read(&file, &total, buf, 6);
|
||||
IFile_Close(&file);
|
||||
|
||||
if(!R_SUCCEEDED(ret) || total < 6) return -1;
|
||||
|
||||
for(u32 i = 0; i < 7; ++i)
|
||||
{
|
||||
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
|
||||
|
||||
if(memcmp(buf, regions[i], 3) == 0)
|
||||
{
|
||||
*regionId = (u8)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 12; ++i)
|
||||
{
|
||||
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
|
||||
|
||||
if(memcmp(buf + 4, languages[i], 2) == 0)
|
||||
{
|
||||
*languageId = (u8)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
{
|
||||
/* HANS:
|
||||
Look for error code which is known to be stored near cfg:u handle
|
||||
this way we can find the right candidate
|
||||
(handle should also be stored right after end of candidate function) */
|
||||
|
||||
u32 n = 0,
|
||||
possible[24];
|
||||
|
||||
for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
|
||||
{
|
||||
if(*(u32 *)pos == 0xD8A103F9)
|
||||
{
|
||||
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
||||
if(*l <= 0x10000000) possible[n++] = *l;
|
||||
}
|
||||
}
|
||||
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
{
|
||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
|
||||
{
|
||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
|
||||
|
||||
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
||||
CFGU_GetConfigInfoBlk2_startPos -= 2);
|
||||
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
||||
{
|
||||
if(*(u32 *)languageBlkIdPos == 0xA0002)
|
||||
{
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
{
|
||||
if(instr[3] == 0xEB) //We're looking for BL
|
||||
{
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0,
|
||||
found;
|
||||
|
||||
do
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
|
||||
calledFunction += offset;
|
||||
|
||||
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||
|
||||
if(found)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; // mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; // bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; // (1 or 2 instructions) => movs r0, 0 (result code)
|
||||
|
||||
//We're done
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
{
|
||||
for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
|
||||
{
|
||||
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D4F70, 0xE3A00802, 0xE5A40080};
|
||||
|
||||
u32 *cmp = (u32 *)cmdPos;
|
||||
|
||||
if(cmp[0] == cfgSecureInfoGetRegionCmdPattern[0] && cmp[1] == cfgSecureInfoGetRegionCmdPattern[1] &&
|
||||
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
|
||||
{
|
||||
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; // mov r0, =regionId
|
||||
*((u32 *)cmdPos + 5) = 0xE5C40008; // strb r0, [r4, 8]
|
||||
*((u32 *)cmdPos + 6) = 0xE3B00000; // movs r0, 0 (result code) ('s' not needed but nvm)
|
||||
*((u32 *)cmdPos + 7) = 0xE5840004; // str r0, [r4, 4]
|
||||
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patchCode(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
switch(progId)
|
||||
{
|
||||
case 0x0004003000008F02LL: // USA Menu
|
||||
case 0x0004003000008202LL: // EUR Menu
|
||||
case 0x0004003000009802LL: // JPN Menu
|
||||
case 0x000400300000A102LL: // CHN Menu
|
||||
case 0x000400300000A902LL: // KOR Menu
|
||||
case 0x000400300000B102LL: // TWN Menu
|
||||
{
|
||||
static const u8 regionFreePattern[] = {
|
||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3
|
||||
};
|
||||
static const u8 regionFreePatch[] = {
|
||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
};
|
||||
|
||||
//Patch SMDH region checks
|
||||
patchMemory(code, size,
|
||||
regionFreePattern,
|
||||
sizeof(regionFreePattern), -16,
|
||||
regionFreePatch,
|
||||
sizeof(regionFreePatch), 1
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004013000002C02LL: // NIM
|
||||
{
|
||||
static const u8 blockAutoUpdatesPattern[] = {
|
||||
0x25, 0x79, 0x0B, 0x99
|
||||
};
|
||||
static const u8 blockAutoUpdatesPatch[] = {
|
||||
0xE3, 0xA0
|
||||
};
|
||||
|
||||
//Block silent auto-updates
|
||||
patchMemory(code, size,
|
||||
blockAutoUpdatesPattern,
|
||||
sizeof(blockAutoUpdatesPattern), 0,
|
||||
blockAutoUpdatesPatch,
|
||||
sizeof(blockAutoUpdatesPatch), 1
|
||||
);
|
||||
|
||||
//Apply only if the updated NAND hasn't been booted
|
||||
if((BOOTCONFIG(0, 3) != 0) == (BOOTCONFIG(3, 1) && CONFIG(1)))
|
||||
{
|
||||
static const u8 skipEshopUpdateCheckPattern[] = {
|
||||
0x30, 0xB5, 0xF1, 0xB0
|
||||
};
|
||||
static const u8 skipEshopUpdateCheckPatch[] = {
|
||||
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
|
||||
};
|
||||
|
||||
//Skip update checks to access the EShop
|
||||
patchMemory(code, size,
|
||||
skipEshopUpdateCheckPattern,
|
||||
sizeof(skipEshopUpdateCheckPattern), 0,
|
||||
skipEshopUpdateCheckPatch,
|
||||
sizeof(skipEshopUpdateCheckPatch), 1
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004013000003202LL: // FRIENDS
|
||||
{
|
||||
static const u8 fpdVerPattern[] = {
|
||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01
|
||||
};
|
||||
|
||||
static const u8 fpdVerPatch = 0x05;
|
||||
|
||||
//Allow online access to work with old friends modules
|
||||
patchMemory(code, size,
|
||||
fpdVerPattern,
|
||||
sizeof(fpdVerPattern), 9,
|
||||
&fpdVerPatch,
|
||||
sizeof(fpdVerPatch), 1
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004001000021000LL: // USA MSET
|
||||
case 0x0004001000020000LL: // JPN MSET
|
||||
case 0x0004001000022000LL: // EUR MSET
|
||||
case 0x0004001000026000LL: // CHN MSET
|
||||
case 0x0004001000027000LL: // KOR MSET
|
||||
case 0x0004001000028000LL: // TWN MSET
|
||||
{
|
||||
if(CONFIG(5))
|
||||
{
|
||||
static const u16 verPattern[] = u"Ver.";
|
||||
const u32 currentNand = BOOTCONFIG(0, 3);
|
||||
const u32 matchingFirm = BOOTCONFIG(2, 1) == (currentNand != 0);
|
||||
|
||||
//Patch Ver. string
|
||||
patchMemory(code, size,
|
||||
verPattern,
|
||||
sizeof(verPattern) - sizeof(u16), 0,
|
||||
!currentNand ? ((matchingFirm) ? u" Sys" : u"SysE") :
|
||||
((currentNand == 1) ? (matchingFirm ? u" Emu" : u"EmuS") : ((matchingFirm) ? u"Emu2" : u"Em2S")),
|
||||
sizeof(verPattern) - sizeof(u16), 1
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004013000008002LL: // NS
|
||||
{
|
||||
static const u8 stopCartUpdatesPattern[] = {
|
||||
0x0C, 0x18, 0xE1, 0xD8
|
||||
};
|
||||
static const u8 stopCartUpdatesPatch[] = {
|
||||
0x0B, 0x18, 0x21, 0xC8
|
||||
};
|
||||
|
||||
//Disable updates from foreign carts (makes carts region-free)
|
||||
patchMemory(code, size,
|
||||
stopCartUpdatesPattern,
|
||||
sizeof(stopCartUpdatesPattern), 0,
|
||||
stopCartUpdatesPatch,
|
||||
sizeof(stopCartUpdatesPatch), 2
|
||||
);
|
||||
|
||||
u32 cpuSetting = MULTICONFIG(1);
|
||||
|
||||
if(cpuSetting)
|
||||
{
|
||||
static const u8 cfgN3dsCpuPattern[] = {
|
||||
0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00
|
||||
};
|
||||
|
||||
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
||||
|
||||
//Patch N3DS CPU Clock and L2 cache setting
|
||||
if(cfgN3dsCpuLoc != NULL)
|
||||
{
|
||||
*(cfgN3dsCpuLoc + 1) = 0xE1A00000;
|
||||
*(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004013000001702LL: // CFG
|
||||
{
|
||||
static const u8 secureinfoSigCheckPattern[] = {
|
||||
0x06, 0x46, 0x10, 0x48, 0xFC
|
||||
};
|
||||
static const u8 secureinfoSigCheckPatch[] = {
|
||||
0x00, 0x26
|
||||
};
|
||||
|
||||
//Disable SecureInfo signature check
|
||||
patchMemory(code, size,
|
||||
secureinfoSigCheckPattern,
|
||||
sizeof(secureinfoSigCheckPattern), 0,
|
||||
secureinfoSigCheckPatch,
|
||||
sizeof(secureinfoSigCheckPatch), 1
|
||||
);
|
||||
|
||||
if(secureInfoExists())
|
||||
{
|
||||
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
|
||||
static const u16 secureinfoFilenamePatch[] = u"C";
|
||||
|
||||
//Use SecureInfo_C
|
||||
patchMemory(code, size,
|
||||
secureinfoFilenamePattern,
|
||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||
secureinfoFilenamePatch,
|
||||
sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if(CONFIG(4))
|
||||
{
|
||||
u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;
|
||||
|
||||
if(tidHigh == 0x0004000)
|
||||
{
|
||||
//Language emulation
|
||||
u8 regionId = 0xFF,
|
||||
languageId = 0xFF;
|
||||
|
||||
if(R_SUCCEEDED(loadTitleLocaleConfig(progId, ®ionId, &languageId)))
|
||||
{
|
||||
u32 CFGUHandleOffset;
|
||||
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
|
||||
{
|
||||
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
5
injector/source/patcher.h
Normal file
5
injector/source/patcher.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
void patchCode(u64 progId, u8 *code, u32 size);
|
||||
74
injector/source/pxipm.c
Normal file
74
injector/source/pxipm.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "pxipm.h"
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle pxipmHandle;
|
||||
static int pxipmRefCount;
|
||||
|
||||
Result pxipmInit(void)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
if (AtomicPostIncrement(&pxipmRefCount)) return 0;
|
||||
|
||||
ret = srvSysGetServiceHandle(&pxipmHandle, "PxiPM");
|
||||
|
||||
if (R_FAILED(ret)) AtomicDecrement(&pxipmRefCount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pxipmExit(void)
|
||||
{
|
||||
if (AtomicDecrement(&pxipmRefCount)) return;
|
||||
svcCloseHandle(pxipmHandle);
|
||||
}
|
||||
|
||||
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x2,8,0); // 0x20200
|
||||
memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[3] = title->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[3])+1, &title->padding, 7);
|
||||
memcpy(&cmdbuf[5], &update->programId, sizeof(u64));
|
||||
*(u8 *)&cmdbuf[7] = update->mediaType;
|
||||
memcpy(((u8 *)&cmdbuf[7])+1, &update->padding, 7);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
*prog_handle = *(u64*)&cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x1,2,2); // 0x10082
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
cmdbuf[3] = (0x400 << 8) | 0x4;
|
||||
cmdbuf[4] = (u32) exheader;
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result PXIPM_UnregisterProgram(u64 prog_handle)
|
||||
{
|
||||
u32 *cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3,2,0); // 0x30080
|
||||
cmdbuf[1] = (u32) (prog_handle);
|
||||
cmdbuf[2] = (u32) (prog_handle >> 32);
|
||||
|
||||
Result ret = 0;
|
||||
if(R_FAILED(ret = svcSendSyncRequest(pxipmHandle))) return ret;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
10
injector/source/pxipm.h
Normal file
10
injector/source/pxipm.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "exheader.h"
|
||||
|
||||
Result pxipmInit(void);
|
||||
void pxipmExit(void);
|
||||
Result PXIPM_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update);
|
||||
Result PXIPM_GetProgramInfo(exheader_header *exheader, u64 prog_handle);
|
||||
Result PXIPM_UnregisterProgram(u64 prog_handle);
|
||||
154
injector/source/srvsys.c
Normal file
154
injector/source/srvsys.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
#include "srvsys.h"
|
||||
|
||||
static Handle srvHandle;
|
||||
static int srvRefCount;
|
||||
static RecursiveLock initLock;
|
||||
static int initLockinit = 0;
|
||||
|
||||
Result srvSysInit()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if (!initLockinit)
|
||||
{
|
||||
RecursiveLock_Init(&initLock);
|
||||
}
|
||||
|
||||
RecursiveLock_Lock(&initLock);
|
||||
|
||||
if (srvRefCount > 0)
|
||||
{
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_ALREADY_INITIALIZED);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
rc = svcConnectToPort(&srvHandle, "srv:");
|
||||
if (R_LEVEL(rc) != RL_PERMANENT ||
|
||||
R_SUMMARY(rc) != RS_NOTFOUND ||
|
||||
R_DESCRIPTION(rc) != RD_NOT_FOUND
|
||||
) break;
|
||||
svcSleepThread(500000);
|
||||
}
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = srvSysRegisterClient();
|
||||
srvRefCount++;
|
||||
}
|
||||
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result srvSysRegisterClient(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x1,0,2); // 0x10002
|
||||
cmdbuf[1] = IPC_Desc_CurProcessHandle();
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysExit()
|
||||
{
|
||||
Result rc;
|
||||
RecursiveLock_Lock(&initLock);
|
||||
|
||||
if (srvRefCount > 1)
|
||||
{
|
||||
srvRefCount--;
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return MAKERESULT(RL_INFO, RS_NOP, 25, RD_BUSY);
|
||||
}
|
||||
|
||||
if (srvHandle != 0) svcCloseHandle(srvHandle);
|
||||
else svcBreak(USERBREAK_ASSERT);
|
||||
rc = (Result)srvHandle; // yeah, I think this is a benign bug
|
||||
srvHandle = 0;
|
||||
srvRefCount--;
|
||||
RecursiveLock_Unlock(&initLock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result srvSysGetServiceHandle(Handle* out, const char* name)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
cmdbuf[4] = 0x0;
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysEnableNotification(Handle* semaphoreOut)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x2,0,0);
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(semaphoreOut) *semaphoreOut = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysReceiveNotification(u32* notificationIdOut)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0xB,0,0); // 0xB0000
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(notificationIdOut) *notificationIdOut = cmdbuf[2];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x3,4,0); // 0x30100
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
cmdbuf[4] = maxSessions;
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
if(out) *out = cmdbuf[3];
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
|
||||
Result srvSysUnregisterService(const char* name)
|
||||
{
|
||||
Result rc = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x4,3,0); // 0x400C0
|
||||
strncpy((char*) &cmdbuf[1], name,8);
|
||||
cmdbuf[3] = strlen(name);
|
||||
|
||||
if(R_FAILED(rc = svcSendSyncRequest(srvHandle)))return rc;
|
||||
|
||||
return cmdbuf[1];
|
||||
}
|
||||
47
injector/source/srvsys.h
Normal file
47
injector/source/srvsys.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file srv.h
|
||||
* @brief Service API.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/// Initializes the service API.
|
||||
Result srvSysInit(void);
|
||||
|
||||
/// Exits the service API.
|
||||
Result srvSysExit(void);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a service handle, retrieving from the environment handle list if possible.
|
||||
* @param out Pointer to write the handle to.
|
||||
* @param name Name of the service.
|
||||
*/
|
||||
Result srvSysGetServiceHandle(Handle* out, const char* name);
|
||||
|
||||
/// Registers the current process as a client to the service API.
|
||||
Result srvSysRegisterClient(void);
|
||||
|
||||
/**
|
||||
* @brief Enables service notificatios, returning a notification semaphore.
|
||||
* @param semaphoreOut Pointer to output the notification semaphore to.
|
||||
*/
|
||||
Result srvSysEnableNotification(Handle* semaphoreOut);
|
||||
|
||||
/**
|
||||
* @brief Receives a notification.
|
||||
* @param notificationIdOut Pointer to output the ID of the received notification to.
|
||||
*/
|
||||
Result srvSysReceiveNotification(u32* notificationIdOut);
|
||||
|
||||
/**
|
||||
* @brief Registers the current process as a service.
|
||||
* @param out Pointer to write the service handle to.
|
||||
* @param name Name of the service.
|
||||
* @param maxSessions Maximum number of sessions the service can handle.
|
||||
*/
|
||||
Result srvSysRegisterService(Handle* out, const char* name, int maxSessions);
|
||||
|
||||
/**
|
||||
* @brief Unregisters the current process as a service.
|
||||
* @param name Name of the service.
|
||||
*/
|
||||
Result srvSysUnregisterService(const char* name);
|
||||
55
loader/Makefile
Normal file
55
loader/Makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
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
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostartfiles
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
11
loader/linker.ld
Normal file
11
loader/linker.ld
Normal file
@@ -0,0 +1,11 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x24F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
14
loader/source/buttons.h
Normal file
14
loader/source/buttons.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define HID_PAD (*(vu32 *)0x10146000 ^ 0xFFF)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
#define BUTTON_A 1
|
||||
#define BUTTON_X (1 << 10)
|
||||
#define BUTTON_Y (1 << 11)
|
||||
#define BUTTON_R1 (1 << 8)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
@@ -1,180 +1,254 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
Prototype.
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
First stable version.
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
First stable version.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support of exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD and removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
21
loader/source/fatfs/00readme.txt
Normal file
21
loader/source/fatfs/00readme.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
FatFs Module Source Files R0.12
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
ff.c FatFs module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
|
||||
101
loader/source/fatfs/diskio.c
Normal file
101
loader/source/fatfs/diskio.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
sdmmc_sdcard_init();
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if(sdmmc_sdcard_readsectors(sector, count, buff))
|
||||
return RES_PARERR;
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if _USE_WRITE
|
||||
DRESULT disk_write (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
__attribute__((unused))
|
||||
const BYTE *buff, /* Data to be written */
|
||||
__attribute__((unused))
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
__attribute__((unused))
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
return RES_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if _USE_IOCTL
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
80
loader/source/fatfs/diskio.h
Normal file
80
loader/source/fatfs/diskio.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define _USE_WRITE 1 /* 1: Enable disk_write function */
|
||||
#define _USE_IOCTL 1 /* 1: Enable disk_ioctl fucntion */
|
||||
|
||||
#include "integer.h"
|
||||
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
5787
loader/source/fatfs/ff.c
Normal file
5787
loader/source/fatfs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
366
loader/source/fatfs/ff.h
Normal file
366
loader/source/fatfs/ff.h
Normal file
@@ -0,0 +1,366 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module include R0.12 (C)ChaN, 2016
|
||||
/----------------------------------------------------------------------------/
|
||||
/ FatFs module is a free software that opened under license policy of
|
||||
/ following conditions.
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ 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 88100 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
#if _FATFS != _FFCONF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode string */
|
||||
#if _USE_LFN == 0
|
||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||
#endif
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#endif
|
||||
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* File system type (0:N/A) */
|
||||
BYTE drv; /* 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 n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if _MAX_SS != _MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH != 0
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if _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) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
DWORD volbase; /* Volume base sector */
|
||||
DWORD fatbase; /* FAT base sector */
|
||||
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) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Type of file size variables and object identifier */
|
||||
|
||||
#if _FS_EXFAT
|
||||
#if _USE_LFN == 0
|
||||
#error LFN must be enabled when enable exFAT
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#else
|
||||
typedef DWORD FSIZE_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (_FDID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if _FS_EXFAT
|
||||
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
|
||||
DWORD 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) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} _FDID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
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 (not valid when fprt 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[] */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE* fn; /* Pointer to the SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if _USE_LFN != 0
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||
#endif
|
||||
#if _USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if _USE_LFN != 0
|
||||
TCHAR altname[13]; /* Altenative file name */
|
||||
TCHAR fname[_MAX_LFN + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[13]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||
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_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of a file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of the file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||
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 */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY && !_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 */
|
||||
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 */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access control and file status flags (FIL.flag) */
|
||||
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define _FA_MODIFIED 0x20
|
||||
#define _FA_DIRTY 0x40
|
||||
|
||||
|
||||
/* FAT sub type (FATFS.fs_type) */
|
||||
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
|
||||
/* File attribute bits for directory entry */
|
||||
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_VOL 0x08 /* Volume label */
|
||||
#define AM_LFN 0x0F /* LFN entry */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||
|
||||
|
||||
/* Fast seek controls */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
266
loader/source/fatfs/ffconf.h
Normal file
266
loader/source/fatfs/ffconf.h
Normal file
@@ -0,0 +1,266 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.12 (C)ChaN, 2016
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 88100 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_READONLY 1
|
||||
/* 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 1
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 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().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _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
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _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. */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_TINY need to be 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 1
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 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
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 0
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN switches the support of long file name (LFN).
|
||||
/
|
||||
/ 0: Disable support of LFN. _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.
|
||||
/ 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:Unicode)
|
||||
/ 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 _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().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ This option has no effect when _LFN_UNICODE == 0. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures support of relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","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
|
||||
/ 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.
|
||||
/ 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
|
||||
/ 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, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches support of 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
|
||||
/* 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.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _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. */
|
||||
|
||||
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system in addition to the traditional
|
||||
/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
|
||||
/ Note that enabling exFAT discards C89 compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 3
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2016
|
||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
/ 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). */
|
||||
|
||||
|
||||
#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
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ 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
|
||||
/ 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.
|
||||
/ 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*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
38
loader/source/fatfs/integer.h
Normal file
38
loader/source/fatfs/integer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*-------------------------------------------*/
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
|
||||
|
||||
#else /* Embedded platform */
|
||||
|
||||
/* These types MUST be 16-bit or 32-bit */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* This type MUST be 8-bit */
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types MUST be 16-bit */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types MUST be 32-bit */
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
4
loader/source/fatfs/sdmmc/common.h
Normal file
4
loader/source/fatfs/sdmmc/common.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../../types.h"
|
||||
9
loader/source/fatfs/sdmmc/delay.h
Normal file
9
loader/source/fatfs/sdmmc/delay.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void ioDelay(u32 us);
|
||||
17
loader/source/fatfs/sdmmc/delay.s
Normal file
17
loader/source/fatfs/sdmmc/delay.s
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
.arm
|
||||
.global ioDelay
|
||||
.type ioDelay STT_FUNC
|
||||
|
||||
@ioDelay ( u32 us )
|
||||
ioDelay:
|
||||
ldr r1, =0x18000000 @ VRAM
|
||||
1:
|
||||
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
||||
ldr r2, [r1]
|
||||
subs r0, #1
|
||||
bgt 1b
|
||||
bx lr
|
||||
290
loader/source/fatfs/sdmmc/sdmmc.c
Normal file
290
loader/source/fatfs/sdmmc/sdmmc.c
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "sdmmc.h"
|
||||
#include "delay.h"
|
||||
|
||||
static struct mmcdevice handleSD;
|
||||
|
||||
static inline u16 sdmmc_read16(u16 reg) {
|
||||
return *(vu16*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write16(u16 reg, u16 val) {
|
||||
*(vu16*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline u32 sdmmc_read32(u16 reg) {
|
||||
return *(vu32*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write32(u16 reg, u32 val) {
|
||||
*(vu32*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) {
|
||||
u16 val = sdmmc_read16(reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
sdmmc_write16(reg, val);
|
||||
}
|
||||
|
||||
static inline void setckl(u32 data)
|
||||
{
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx)
|
||||
{
|
||||
return (ctx->error << 29) >> 31;
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx)
|
||||
{
|
||||
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
|
||||
setckl(ctx->clk);
|
||||
if (ctx->SDOPT == 0) {
|
||||
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||
} else {
|
||||
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args)
|
||||
{
|
||||
bool getSDRESP = (cmd << 15) >> 31;
|
||||
u16 flags = (cmd << 15) >> 31;
|
||||
const bool readdata = cmd & 0x20000;
|
||||
const bool writedata = cmd & 0x40000;
|
||||
|
||||
if (readdata || writedata)
|
||||
flags |= TMIO_STAT0_DATAEND;
|
||||
|
||||
ctx->error = 0;
|
||||
while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0,0);
|
||||
sdmmc_write16(REG_SDIRMASK1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
sdmmc_mask16(REG_SDDATACTL32,0x1800,0);
|
||||
|
||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
||||
|
||||
u32 size = ctx->size;
|
||||
vu8 *dataPtr = ctx->data;
|
||||
|
||||
bool useBuf = ( NULL != dataPtr );
|
||||
|
||||
u16 status0 = 0;
|
||||
while(true) {
|
||||
u16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
if (status1 & TMIO_STAT1_RXRDY) {
|
||||
if (readdata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
||||
if (size > 0x1FF) {
|
||||
for(int i = 0; i<0x200; i+=2) {
|
||||
u16 data = sdmmc_read16(REG_SDFIFO);
|
||||
*dataPtr++ = data & 0xFF;
|
||||
*dataPtr++ = data >> 8;
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status1 & TMIO_STAT1_TXRQ) {
|
||||
if (writedata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
|
||||
if (size > 0x1FF) {
|
||||
for (int i = 0; i<0x200; i+=2) {
|
||||
u16 data = *dataPtr++;
|
||||
data |= *dataPtr++ << 8;
|
||||
sdmmc_write16(REG_SDFIFO, data);
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status1 & TMIO_MASK_GW) {
|
||||
ctx->error |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(status1 & TMIO_STAT1_CMD_BUSY)) {
|
||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
ctx->error |= 0x1;
|
||||
if (status0 & TMIO_STAT0_DATAEND)
|
||||
ctx->error |= 0x2;
|
||||
|
||||
if ((status0 & flags) == flags)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
|
||||
if (getSDRESP != 0) {
|
||||
ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16);
|
||||
ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16);
|
||||
ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16);
|
||||
ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
{
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = out;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x33C12,sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
static u32 calcSDSize(u8* csd, int type)
|
||||
{
|
||||
u32 result = 0;
|
||||
if (type == -1) type = csd[14] >> 6;
|
||||
switch (type) {
|
||||
case 0:
|
||||
{
|
||||
u32 block_len = csd[9] & 0xf;
|
||||
block_len = 1u << block_len;
|
||||
u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1);
|
||||
mult = 1u << (mult + 2);
|
||||
result = csd[8] & 3;
|
||||
result = (result << 8) | csd[7];
|
||||
result = (result << 2) | (csd[6] >> 6);
|
||||
result = (result + 1) * mult * block_len / 512;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result = csd[7] & 0x3f;
|
||||
result = (result << 8) | csd[6];
|
||||
result = (result << 8) | csd[5];
|
||||
result = (result + 1) * 1024;
|
||||
break;
|
||||
default:
|
||||
break; //Do nothing otherwise
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void InitSD()
|
||||
{
|
||||
//SD
|
||||
handleSD.isSDHC = 0;
|
||||
handleSD.SDOPT = 0;
|
||||
handleSD.res = 0;
|
||||
handleSD.initarg = 0;
|
||||
handleSD.clk = 0x80;
|
||||
handleSD.devicenumber = 0;
|
||||
|
||||
*(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2;
|
||||
*(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL
|
||||
*(vu16*)0x10006104 = 0; //SDBLKLEN32
|
||||
*(vu16*)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(vu16*)0x100060E0 |= 1u; //SDRESET
|
||||
*(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
*(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20
|
||||
*(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(vu16*)0x10006026 = 512; //SDBLKLEN
|
||||
*(vu16*)0x10006008 = 0; //SDSTOP
|
||||
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
static int SD_Init()
|
||||
{
|
||||
inittarget(&handleSD);
|
||||
|
||||
ioDelay(1u << 18); //Card needs a little bit of time to be detected, it seems
|
||||
|
||||
//If not inserted
|
||||
if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0,0);
|
||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
||||
//u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E;
|
||||
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
||||
|
||||
//int count = 0;
|
||||
u32 temp2 = 0;
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp);
|
||||
temp2 = 1;
|
||||
} while ( !(handleSD.error & 1) );
|
||||
|
||||
} while((handleSD.ret[0] & 0x80000000) == 0);
|
||||
|
||||
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
||||
temp2 = 0;
|
||||
|
||||
handleSD.isSDHC = temp2;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10602,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10403,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1);
|
||||
handleSD.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.SDOPT = 1;
|
||||
sdmmc_send_command(&handleSD,0x10446,0x2);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10410,0x200);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.clk |= 0x200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sdmmc_sdcard_init()
|
||||
{
|
||||
InitSD();
|
||||
SD_Init();
|
||||
}
|
||||
122
loader/source/fatfs/sdmmc/sdmmc.h
Normal file
122
loader/source/fatfs/sdmmc/sdmmc.h
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define SDMMC_BASE 0x10006000u
|
||||
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
#define REG_SDCMDARG 0x04
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
#define REG_SDCLKCTL 0x24
|
||||
|
||||
#define REG_SDBLKLEN 0x26
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
|
||||
#define REG_SDDATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
|
||||
#define REG_SDDATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
|
||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||
#define TMIO_STAT0_DATAEND 0x0004
|
||||
#define TMIO_STAT0_CARD_REMOVE 0x0008
|
||||
#define TMIO_STAT0_CARD_INSERT 0x0010
|
||||
#define TMIO_STAT0_SIGSTATE 0x0020
|
||||
#define TMIO_STAT0_WRPROTECT 0x0080
|
||||
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
|
||||
#define TMIO_STAT0_CARD_INSERT_A 0x0200
|
||||
#define TMIO_STAT0_SIGSTATE_A 0x0400
|
||||
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
|
||||
#define TMIO_STAT1_CRCFAIL 0x0002
|
||||
#define TMIO_STAT1_STOPBIT_ERR 0x0004
|
||||
#define TMIO_STAT1_DATATIMEOUT 0x0008
|
||||
#define TMIO_STAT1_RXOVERFLOW 0x0010
|
||||
#define TMIO_STAT1_TXUNDERRUN 0x0020
|
||||
#define TMIO_STAT1_CMDTIMEOUT 0x0040
|
||||
#define TMIO_STAT1_RXRDY 0x0100
|
||||
#define TMIO_STAT1_TXRQ 0x0200
|
||||
#define TMIO_STAT1_ILL_FUNC 0x2000
|
||||
#define TMIO_STAT1_CMD_BUSY 0x4000
|
||||
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
||||
|
||||
//Comes from TWLSDK mongoose.tef DWARF info
|
||||
#define SDMC_NORMAL 0x00000000
|
||||
#define SDMC_ERR_COMMAND 0x00000001
|
||||
#define SDMC_ERR_CRC 0x00000002
|
||||
#define SDMC_ERR_END 0x00000004
|
||||
#define SDMC_ERR_TIMEOUT 0x00000008
|
||||
#define SDMC_ERR_FIFO_OVF 0x00000010
|
||||
#define SDMC_ERR_FIFO_UDF 0x00000020
|
||||
#define SDMC_ERR_WP 0x00000040
|
||||
#define SDMC_ERR_ABORT 0x00000080
|
||||
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
|
||||
#define SDMC_ERR_PARAM 0x00000200
|
||||
#define SDMC_ERR_R1_STATUS 0x00000800
|
||||
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
|
||||
#define SDMC_ERR_RESET 0x00002000
|
||||
#define SDMC_ERR_ILA 0x00004000
|
||||
#define SDMC_ERR_INFO_DETECT 0x00008000
|
||||
|
||||
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
|
||||
#define SDMC_STAT_ERR_CC 0x00100000
|
||||
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
|
||||
#define SDMC_STAT_ERR_CRC 0x00800000
|
||||
#define SDMC_STAT_ERR_OTHER 0xf9c70008
|
||||
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
|
||||
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
|
||||
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||
|
||||
typedef struct mmcdevice {
|
||||
vu8* data;
|
||||
u32 size;
|
||||
u32 error;
|
||||
u16 stat0;
|
||||
u16 stat1;
|
||||
u32 ret[4];
|
||||
u32 initarg;
|
||||
u32 isSDHC;
|
||||
u32 clk;
|
||||
u32 SDOPT;
|
||||
u32 devicenumber;
|
||||
u32 total_size; //size in sectors of the device
|
||||
u32 res;
|
||||
} mmcdevice;
|
||||
|
||||
void sdmmc_sdcard_init();
|
||||
u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
58
loader/source/main.c
Normal file
58
loader/source/main.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "types.h"
|
||||
#include "buttons.h"
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
#define PAYLOAD_ADDRESS 0x23F00000
|
||||
#define LOAD_PAYLOAD(a) loadPayload(a "_*.bin")
|
||||
|
||||
static u32 loadPayload(const char *pattern)
|
||||
{
|
||||
char path[30] = "/luma/payloads";
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
|
||||
FRESULT result = f_findfirst(&dir, &info, path, pattern);
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(result != FR_OK || !info.fname[0])
|
||||
return 0;
|
||||
|
||||
path[14] = '/';
|
||||
u32 i;
|
||||
for(i = 0; info.fname[i]; i++)
|
||||
path[15 + i] = info.fname[i];
|
||||
path[15 + i] = '\0';
|
||||
|
||||
FIL payload;
|
||||
unsigned int br;
|
||||
|
||||
f_open(&payload, path, FA_READ);
|
||||
f_read(&payload, (void *)PAYLOAD_ADDRESS, f_size(&payload), &br);
|
||||
f_close(&payload);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
FATFS fs;
|
||||
|
||||
f_mount(&fs, "0:", 1);
|
||||
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
if(((pressed & BUTTON_RIGHT) && LOAD_PAYLOAD("right")) ||
|
||||
((pressed & BUTTON_LEFT) && LOAD_PAYLOAD("left")) ||
|
||||
((pressed & BUTTON_UP) && LOAD_PAYLOAD("up")) ||
|
||||
((pressed & BUTTON_DOWN) && LOAD_PAYLOAD("down")) ||
|
||||
((pressed & BUTTON_X) && LOAD_PAYLOAD("x")) ||
|
||||
((pressed & BUTTON_Y) && LOAD_PAYLOAD("y")) ||
|
||||
((pressed & BUTTON_R1) && LOAD_PAYLOAD("r")) ||
|
||||
((pressed & BUTTON_A) && LOAD_PAYLOAD("a")) ||
|
||||
((pressed & BUTTON_SELECT) && LOAD_PAYLOAD("sel")) ||
|
||||
LOAD_PAYLOAD("def"))
|
||||
((void (*)())PAYLOAD_ADDRESS)();
|
||||
}
|
||||
11
loader/source/start.s
Normal file
11
loader/source/start.s
Normal file
@@ -0,0 +1,11 @@
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Flush caches
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0 @ flush I-cache
|
||||
mcr p15, 0, r0, c7, c6, 0 @ flush D-cache
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||
|
||||
b main
|
||||
14
loader/source/types.h
Normal file
14
loader/source/types.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
@@ -1,9 +1,6 @@
|
||||
.nds
|
||||
.arm.little
|
||||
|
||||
sdmmc equ 0x434D4453 ;dummy
|
||||
|
||||
.create "emunand.bin", 0x0801A5C0
|
||||
.org 0x0801A5C0
|
||||
.create "emunand.bin", 0
|
||||
.arm
|
||||
nand_sd:
|
||||
; Original code that still needs to be executed.
|
||||
@@ -15,7 +12,7 @@ nand_sd:
|
||||
|
||||
; If we're already trying to access the SD, return.
|
||||
ldr r2, [r0, #4]
|
||||
ldr r1, =sdmmc
|
||||
ldr r1, [sdmmc]
|
||||
cmp r2, r1
|
||||
beq nand_sd_ret
|
||||
|
||||
@@ -23,12 +20,10 @@ nand_sd:
|
||||
ldr r2, [r0, #8] ; Get sector to read
|
||||
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
|
||||
|
||||
ldr r3, =nand_offset
|
||||
ldr r3, [r3]
|
||||
ldr r3, [nand_offset]
|
||||
add r2, r3 ; Add the offset to the NAND in the SD.
|
||||
|
||||
ldreq r3, =ncsd_header_offset
|
||||
ldreq r3, [r3]
|
||||
ldreq r3, [ncsd_header_offset]
|
||||
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector.
|
||||
|
||||
str r2, [r0, #8] ; Store sector to read
|
||||
@@ -45,6 +40,7 @@ nand_sd:
|
||||
add r0, #4
|
||||
bx r0
|
||||
.pool
|
||||
sdmmc: .ascii "SDMC"
|
||||
nand_offset: .ascii "NAND" ; for rednand this should be 1
|
||||
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
|
||||
.close
|
||||
148
patches/reboot.s
Normal file
148
patches/reboot.s
Normal file
@@ -0,0 +1,148 @@
|
||||
.arm.little
|
||||
|
||||
payload_addr equ 0x23F00000 ; Brahma payload address.
|
||||
payload_maxsize equ 0x20000 ; Maximum size for the payload (200 KB will do).
|
||||
|
||||
.create "reboot.bin", 0
|
||||
.arm
|
||||
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||
|
||||
pxi_wait_recv:
|
||||
ldr r2, =0x44846
|
||||
ldr r0, =0x10008000
|
||||
readPxiLoop1:
|
||||
ldrh r1, [r0, #4]
|
||||
lsls r1, #0x17
|
||||
bmi readPxiLoop1
|
||||
ldr r0, [r0, #0xC]
|
||||
cmp r0, r2
|
||||
bne pxi_wait_recv
|
||||
|
||||
mov r4, #0
|
||||
adr r1, bin_fname
|
||||
b open_payload
|
||||
|
||||
fallback:
|
||||
mov r4, #1
|
||||
adr r1, dat_fname
|
||||
|
||||
open_payload:
|
||||
; Open file
|
||||
add r0, r7, #8
|
||||
mov r2, #1
|
||||
ldr r6, [fopen]
|
||||
orr r6, 1
|
||||
blx r6
|
||||
cmp r0, #0
|
||||
bne fallback ; If the .bin is not found, try the .dat.
|
||||
|
||||
read_payload:
|
||||
; Read file
|
||||
mov r0, r7
|
||||
adr r1, bytes_read
|
||||
ldr r2, =payload_addr
|
||||
cmp r4, #0
|
||||
movne r3, #0x12000 ; Skip the first 0x12000 bytes.
|
||||
moveq r3, payload_maxsize
|
||||
ldr r6, [sp, #0x3A8-0x198]
|
||||
ldr r6, [r6, #0x28]
|
||||
blx r6
|
||||
cmp r4, #0
|
||||
movne r4, #0
|
||||
bne read_payload ; Go read the real payload.
|
||||
|
||||
add r0, sp, #0x3A8 - 0x70
|
||||
ldr r0, [r0, #0x27]
|
||||
ldr r1, =payload_addr + 4
|
||||
str r0, [r1] ; Copy the last digits of the wanted firm to the 5th byte of the payload.
|
||||
|
||||
; Set kernel state
|
||||
mov r0, #0
|
||||
mov r1, #0
|
||||
mov r2, #0
|
||||
mov r3, #0
|
||||
swi 0x7C
|
||||
|
||||
goto_reboot:
|
||||
; Jump to reboot code
|
||||
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||
add r0, pc
|
||||
swi 0x7B
|
||||
|
||||
die:
|
||||
b die
|
||||
|
||||
bytes_read: .word 0
|
||||
fopen: .ascii "OPEN"
|
||||
.pool
|
||||
bin_fname: .dcw "sdmc:/arm9loaderhax.bin"
|
||||
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
dat_fname: .dcw "sdmc:/Luma3DS.dat"
|
||||
.word 0
|
||||
|
||||
.align 4
|
||||
kernelcode_start:
|
||||
; Set MPU settings
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||
orr r0, r0, #0x10 ; dcacheable region 4
|
||||
orr r2, r2, #0x30000 ; region 4 r/w
|
||||
orr r3, r3, #0x30000 ; region 4 r/w
|
||||
orr r12, r12, #0x10 ; icacheable region 4
|
||||
orr r1, r1, #0x10 ; write bufferable region 4
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
mcr p15, 0, r12, c2, c0, 1
|
||||
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
orr r0, r0, #0x20 ; dcacheable region 5
|
||||
orr r1, r1, #0x20 ; icacheable region 5
|
||||
orr r2, r2, #0x20 ; write bufferable region 5
|
||||
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
|
||||
; Flush cache
|
||||
mov r2, #0
|
||||
mov r1, r2
|
||||
flush_cache:
|
||||
mov r0, #0
|
||||
mov r3, r2, lsl #30
|
||||
flush_cache_inner_loop:
|
||||
orr r12, r3, r0, lsl#5
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment)
|
||||
add r0, #1
|
||||
cmp r0, #0x20
|
||||
bcc flush_cache_inner_loop
|
||||
add r2, #1
|
||||
cmp r2, #4
|
||||
bcc flush_cache
|
||||
|
||||
; Enable MPU
|
||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
mcr p15, 0, r1, c7, c5, 0 ; flush dcache
|
||||
mcr p15, 0, r1, c7, c6, 0 ; flush icache
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
|
||||
; Jump to payload
|
||||
ldr r0, =payload_addr
|
||||
bx r0
|
||||
|
||||
.pool
|
||||
.close
|
||||
98
pathchanger/pathchanger.c
Normal file
98
pathchanger/pathchanger.c
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
|
||||
static u8 *memsearch(u8 *startPos, const void *pattern, int size, int patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
int table[256];
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
|
||||
//Searching
|
||||
int j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int fsize(FILE *fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int size = ftell(fp);
|
||||
rewind(fp);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void error(FILE *payload, const char *message)
|
||||
{
|
||||
fclose(payload);
|
||||
printf("%s, are you sure you're using a Luma3DS payload?\n", message);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc == 1)
|
||||
{
|
||||
printf("Usage: %s <Luma3DS payload path>\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
FILE *payload;
|
||||
size_t size;
|
||||
|
||||
payload = fopen(argv[1], "rb+");
|
||||
size = fsize(payload);
|
||||
if(size > 0x20000)
|
||||
error(payload, "The input file is too large");
|
||||
|
||||
u8 *buffer = (u8 *)malloc(size);
|
||||
fread(buffer, 1, size, payload);
|
||||
|
||||
u8 pattern[] = {'s', 0, 'd', 0, 'm', 0, 'c', 0, ':', 0, '/', 0};
|
||||
|
||||
u8 *found = memsearch(buffer, pattern, size, sizeof(pattern));
|
||||
|
||||
if(found == NULL)
|
||||
{
|
||||
free(buffer);
|
||||
error(payload, "Pattern not found");
|
||||
}
|
||||
|
||||
u8 input[38] = {0};
|
||||
u8 payloadname[2 * (sizeof(input) - 1)] = {0};
|
||||
|
||||
printf("Enter the payload's path (37 characters max): ");
|
||||
scanf("%37s", input);
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(input) - 1; i++)
|
||||
payloadname[2 * i] = input[i];
|
||||
|
||||
memcpy(found + 12, payloadname, sizeof(payloadname));
|
||||
|
||||
rewind(payload);
|
||||
fwrite(buffer, 1, size, payload);
|
||||
|
||||
free(buffer);
|
||||
fclose(payload);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
40
pathchanger/pathchanger.py
Normal file
40
pathchanger/pathchanger.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# Requires Python >= 3.2 or >= 2.7
|
||||
|
||||
# This is part of Luma3DS
|
||||
|
||||
__author__ = "TuxSH"
|
||||
__copyright__ = "Copyright (c) 2016 TuxSH"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "v1.0"
|
||||
|
||||
import argparse
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Changes the path to Luma3DS for reboot patches")
|
||||
parser.add_argument("payload", help="Path to the Luma3DS payload")
|
||||
parser.add_argument("new_path", help="New Luma3DS payload path")
|
||||
args = parser.parse_args()
|
||||
data = b""
|
||||
|
||||
if len(args.new_path) > 37:
|
||||
raise SystemExit("The new payload path is too large (37 characters max.)")
|
||||
|
||||
with open(args.payload, "rb") as f: data = bytearray(f.read())
|
||||
|
||||
if len(data) == 0: raise SystemExit("Could not read {0}".format(args.payload))
|
||||
|
||||
if len(data) > 0x20000:
|
||||
raise SystemExit("The input file is too large, are you sure you're using a Luma3DS payload?")
|
||||
|
||||
found_index = data.find("sdmc:/".encode("utf-16-le"))
|
||||
|
||||
if found_index == -1:
|
||||
raise SystemExit("The pattern was not found, are you sure you're usinga a Luma3DS payload?")
|
||||
|
||||
namebuf = args.new_path.encode("utf-16-le")
|
||||
namebuf += b'\x00' * (74 - len(namebuf))
|
||||
|
||||
data[found_index + 12 : found_index + 12 + 74] = namebuf
|
||||
|
||||
with open(args.payload, "wb+") as f: f.write(data)
|
||||
@@ -1,159 +0,0 @@
|
||||
.nds
|
||||
|
||||
firm_size equ 0x000EA000
|
||||
firm_addr equ 0x24000000
|
||||
fopen equ 0x08059D10
|
||||
fread equ 0x0804CC54
|
||||
pxi_wait_recv equ 0x08054134
|
||||
|
||||
.macro svc, num
|
||||
.if isArm()
|
||||
.word 0xEF000000 | num
|
||||
.else
|
||||
.if num > 0xFF
|
||||
.error "bitch you crazu"
|
||||
.endif
|
||||
.halfword 0xDF00 | num
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
.create "reboot1.bin", 0x080849DC
|
||||
.org 0x080849DC
|
||||
.arm
|
||||
patch005:
|
||||
ldr r0, =0x2000E000
|
||||
mov r1, #0x200
|
||||
mov r2, #0
|
||||
add r1, r1, r0
|
||||
@@memset_loop:
|
||||
str r2, [r0]
|
||||
add r0, r0, #4
|
||||
cmp r0, r1
|
||||
blt @@memset_loop
|
||||
ldr r0, =0x2000E000
|
||||
ldr r1, =firm_fname
|
||||
mov r2, #1
|
||||
blx fopen
|
||||
ldr r0, =0x2000E000
|
||||
ldr r1, =0x2000E100
|
||||
mov r2, #firm_addr
|
||||
mov r3, #firm_size
|
||||
blx fread
|
||||
|
||||
ldr r4, =0x44846
|
||||
blx pxi_wait_recv
|
||||
cmp r0, r4
|
||||
bne patch005
|
||||
mov r2, #0
|
||||
mov r3, r2
|
||||
mov r1, r2
|
||||
mov r0, r2
|
||||
svc 0x7C
|
||||
ldr r0, =0x80FF4FC
|
||||
svc 0x7B
|
||||
|
||||
@@inf_loop:
|
||||
b @@inf_loop
|
||||
.pool
|
||||
firm_fname:
|
||||
.close
|
||||
|
||||
.create "reboot2.bin", 0x080933CC
|
||||
.org 0x080933CC
|
||||
.arm
|
||||
stmfd sp!, {r4-r11,lr}
|
||||
sub sp, sp, #0x3C
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||
orr r0, r0, #0x10 ; dcacheable region 4
|
||||
orr r2, r2, #0x30000 ; region 4 r/w
|
||||
orr r3, r3, #0x30000 ; region 4 r/w
|
||||
orr r12, r12, #0x10 ; icacheable region 4
|
||||
orr r1, r1, #0x10 ; write bufferable region 4
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
mcr p15, 0, r12, c2, c0, 1
|
||||
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
orr r0, r0, #0x20 ; dcacheable region 5
|
||||
orr r1, r1, #0x20 ; icacheable region 5
|
||||
orr r2, r2, #0x20 ; write bufferable region 5
|
||||
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
|
||||
mov r4, #firm_addr
|
||||
add r3, r4, #0x40
|
||||
ldr r0, [r3] ; offset
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
add r3, r4, #0x70
|
||||
ldr r0, [r3]
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
add r3, r4, #0xA0
|
||||
ldr r0, [r3]
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
mov r2, #0
|
||||
mov r1, r2
|
||||
@flush_cache:
|
||||
mov r0, #0
|
||||
mov r3, r2, lsl#30
|
||||
@flush_cache_inner_loop:
|
||||
orr r12, r3, r0, lsl#5
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment)
|
||||
add r0, r0, #1
|
||||
cmp r0, #0x20
|
||||
bcc @flush_cache_inner_loop
|
||||
add r2, r2, #1
|
||||
cmp r2, #4
|
||||
bcc @flush_cache
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
@mpu_enable:
|
||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
mcr p15, 0, r1, c7, c5, 0 ; flush dcache
|
||||
mcr p15, 0, r1, c7, c6, 0 ; flush icache
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
mov r0, #firm_addr
|
||||
mov r1, 0X1FFFFFFC
|
||||
ldr r2, [r0,#8] ; arm11 entry
|
||||
str r2, [r1]
|
||||
ldr r0, [r0,#0xC] ; arm9 entry
|
||||
add sp, sp, #0x3C
|
||||
ldmfd sp!, {r4-r11,lr}
|
||||
bx r0
|
||||
.pool
|
||||
memcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||
mov r12, lr
|
||||
stmfd sp!, {r0-r4}
|
||||
add r2, r2, r0
|
||||
@memcpy_loop:
|
||||
ldr r3, [r0], #4
|
||||
str r3, [r1], #4
|
||||
cmp r0, r2
|
||||
blt @memcpy_loop
|
||||
ldmfd sp!, {r0-r4}
|
||||
mov lr, r12
|
||||
bx lr
|
||||
.pool
|
||||
.close
|
||||
@@ -1,159 +0,0 @@
|
||||
.nds
|
||||
|
||||
firm_size equ 0x000EB000
|
||||
firm_addr equ 0x24000000
|
||||
fopen equ 0x0805B180
|
||||
fread equ 0x0804D9B0
|
||||
pxi_wait_recv equ 0x08055178
|
||||
|
||||
.macro svc, num
|
||||
.if isArm()
|
||||
.word 0xEF000000 | num
|
||||
.else
|
||||
.if num > 0xFF
|
||||
.error "bitch you crazu"
|
||||
.endif
|
||||
.halfword 0xDF00 | num
|
||||
.endif
|
||||
.endmacro
|
||||
|
||||
.create "reboot1.bin", 0x080859C8
|
||||
.org 0x080859C8
|
||||
.arm
|
||||
patch005:
|
||||
ldr r0, =0x2000E000
|
||||
mov r1, #0x200
|
||||
mov r2, #0
|
||||
add r1, r1, r0
|
||||
@@memset_loop:
|
||||
str r2, [r0]
|
||||
add r0, r0, #4
|
||||
cmp r0, r1
|
||||
blt @@memset_loop
|
||||
ldr r0, =0x2000E000
|
||||
ldr r1, =firm_fname
|
||||
mov r2, #1
|
||||
blx fopen
|
||||
ldr r0, =0x2000E000
|
||||
ldr r1, =0x2000E100
|
||||
mov r2, #firm_addr
|
||||
mov r3, #firm_size
|
||||
blx fread
|
||||
|
||||
ldr r4, =0x44846
|
||||
blx pxi_wait_recv
|
||||
cmp r0, r4
|
||||
bne patch005
|
||||
mov r2, #0
|
||||
mov r3, r2
|
||||
mov r1, r2
|
||||
mov r0, r2
|
||||
svc 0x7C
|
||||
ldr r0, =0x80FF4FC
|
||||
svc 0x7B
|
||||
|
||||
@@inf_loop:
|
||||
b @@inf_loop
|
||||
.pool
|
||||
firm_fname:
|
||||
.close
|
||||
|
||||
.create "reboot2.bin", 0x08094454
|
||||
.org 0x08094454
|
||||
.arm
|
||||
stmfd sp!, {r4-r11,lr}
|
||||
sub sp, sp, #0x3C
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||
orr r0, r0, #0x10 ; dcacheable region 4
|
||||
orr r2, r2, #0x30000 ; region 4 r/w
|
||||
orr r3, r3, #0x30000 ; region 4 r/w
|
||||
orr r12, r12, #0x10 ; icacheable region 4
|
||||
orr r1, r1, #0x10 ; write bufferable region 4
|
||||
mcr p15, 0, r0, c2, c0, 0
|
||||
mcr p15, 0, r12, c2, c0, 1
|
||||
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||
|
||||
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
orr r0, r0, #0x20 ; dcacheable region 5
|
||||
orr r1, r1, #0x20 ; icacheable region 5
|
||||
orr r2, r2, #0x20 ; write bufferable region 5
|
||||
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||
|
||||
mov r4, #firm_addr
|
||||
add r3, r4, #0x40
|
||||
ldr r0, [r3] ; offset
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
add r3, r4, #0x70
|
||||
ldr r0, [r3]
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
add r3, r4, #0xA0
|
||||
ldr r0, [r3]
|
||||
add r0, r0, r4 ; src
|
||||
ldr r1, [r3,#4] ; dst
|
||||
ldr r2, [r3,#8] ; size
|
||||
bl memcpy32
|
||||
mov r2, #0
|
||||
mov r1, r2
|
||||
@flush_cache:
|
||||
mov r0, #0
|
||||
mov r3, r2, lsl#30
|
||||
@flush_cache_inner_loop:
|
||||
orr r12, r3, r0, lsl#5
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment)
|
||||
add r0, r0, #1
|
||||
cmp r0, #0x20
|
||||
bcc @flush_cache_inner_loop
|
||||
add r2, r2, #1
|
||||
cmp r2, #4
|
||||
bcc @flush_cache
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
@mpu_enable:
|
||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||
mcr p15, 0, r0, c1, c0, 0
|
||||
mcr p15, 0, r1, c7, c5, 0 ; flush dcache
|
||||
mcr p15, 0, r1, c7, c6, 0 ; flush icache
|
||||
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||
mov r0, #firm_addr
|
||||
mov r1, 0X1FFFFFFC
|
||||
ldr r2, [r0,#8] ; arm11 entry
|
||||
str r2, [r1]
|
||||
ldr r0, [r0,#0xC] ; arm9 entry
|
||||
add sp, sp, #0x3C
|
||||
ldmfd sp!, {r4-r11,lr}
|
||||
bx r0
|
||||
.pool
|
||||
memcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||
mov r12, lr
|
||||
stmfd sp!, {r0-r4}
|
||||
add r2, r2, r0
|
||||
@memcpy_loop:
|
||||
ldr r3, [r0], #4
|
||||
str r3, [r1], #4
|
||||
cmp r0, r2
|
||||
blt @memcpy_loop
|
||||
ldmfd sp!, {r0-r4}
|
||||
mov lr, r12
|
||||
bx lr
|
||||
.pool
|
||||
.close
|
||||
47
screeninit/Makefile
Executable file
47
screeninit/Makefile
Executable 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
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
12
screeninit/linker.ld
Executable file
12
screeninit/linker.ld
Executable file
@@ -0,0 +1,12 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x24F03000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
105
screeninit/source/main.c
Executable file
105
screeninit/source/main.c
Executable file
@@ -0,0 +1,105 @@
|
||||
#include "types.h"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26};
|
||||
u32 brightnessLevel = *(vu32 *)0x24F03008;
|
||||
vu32 *const arm11 = (u32 *)0x1FFFFFF8;
|
||||
|
||||
*(vu32 *)0x10141200 = 0x1007F;
|
||||
*(vu32 *)0x10202014 = 0x00000001;
|
||||
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
||||
*(vu32 *)0x10202240 = brightness[brightnessLevel];
|
||||
*(vu32 *)0x10202A40 = brightness[brightnessLevel];
|
||||
*(vu32 *)0x10202244 = 0x1023E;
|
||||
*(vu32 *)0x10202A44 = 0x1023E;
|
||||
|
||||
// Top screen
|
||||
*(vu32 *)0x10400400 = 0x000001c2;
|
||||
*(vu32 *)0x10400404 = 0x000000d1;
|
||||
*(vu32 *)0x10400408 = 0x000001c1;
|
||||
*(vu32 *)0x1040040c = 0x000001c1;
|
||||
*(vu32 *)0x10400410 = 0x00000000;
|
||||
*(vu32 *)0x10400414 = 0x000000cf;
|
||||
*(vu32 *)0x10400418 = 0x000000d1;
|
||||
*(vu32 *)0x1040041c = 0x01c501c1;
|
||||
*(vu32 *)0x10400420 = 0x00010000;
|
||||
*(vu32 *)0x10400424 = 0x0000019d;
|
||||
*(vu32 *)0x10400428 = 0x00000002;
|
||||
*(vu32 *)0x1040042c = 0x00000192;
|
||||
*(vu32 *)0x10400430 = 0x00000192;
|
||||
*(vu32 *)0x10400434 = 0x00000192;
|
||||
*(vu32 *)0x10400438 = 0x00000001;
|
||||
*(vu32 *)0x1040043c = 0x00000002;
|
||||
*(vu32 *)0x10400440 = 0x01960192;
|
||||
*(vu32 *)0x10400444 = 0x00000000;
|
||||
*(vu32 *)0x10400448 = 0x00000000;
|
||||
*(vu32 *)0x1040045C = 0x00f00190;
|
||||
*(vu32 *)0x10400460 = 0x01c100d1;
|
||||
*(vu32 *)0x10400464 = 0x01920002;
|
||||
*(vu32 *)0x10400468 = 0x18300000;
|
||||
*(vu32 *)0x10400470 = 0x80341;
|
||||
*(vu32 *)0x10400474 = 0x00010501;
|
||||
*(vu32 *)0x10400478 = 0;
|
||||
*(vu32 *)0x10400490 = 0x000002D0;
|
||||
*(vu32 *)0x1040049C = 0x00000000;
|
||||
|
||||
// Disco register
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
*(vu32 *)0x10400484 = 0x10101 * i;
|
||||
|
||||
// Bottom screen
|
||||
*(vu32 *)0x10400500 = 0x000001c2;
|
||||
*(vu32 *)0x10400504 = 0x000000d1;
|
||||
*(vu32 *)0x10400508 = 0x000001c1;
|
||||
*(vu32 *)0x1040050c = 0x000001c1;
|
||||
*(vu32 *)0x10400510 = 0x000000cd;
|
||||
*(vu32 *)0x10400514 = 0x000000cf;
|
||||
*(vu32 *)0x10400518 = 0x000000d1;
|
||||
*(vu32 *)0x1040051c = 0x01c501c1;
|
||||
*(vu32 *)0x10400520 = 0x00010000;
|
||||
*(vu32 *)0x10400524 = 0x0000019d;
|
||||
*(vu32 *)0x10400528 = 0x00000052;
|
||||
*(vu32 *)0x1040052c = 0x00000192;
|
||||
*(vu32 *)0x10400530 = 0x00000192;
|
||||
*(vu32 *)0x10400534 = 0x0000004f;
|
||||
*(vu32 *)0x10400538 = 0x00000050;
|
||||
*(vu32 *)0x1040053c = 0x00000052;
|
||||
*(vu32 *)0x10400540 = 0x01980194;
|
||||
*(vu32 *)0x10400544 = 0x00000000;
|
||||
*(vu32 *)0x10400548 = 0x00000011;
|
||||
*(vu32 *)0x1040055C = 0x00f00140;
|
||||
*(vu32 *)0x10400560 = 0x01c100d1;
|
||||
*(vu32 *)0x10400564 = 0x01920052;
|
||||
*(vu32 *)0x10400568 = 0x18300000 + 0x46500;
|
||||
*(vu32 *)0x10400570 = 0x80301;
|
||||
*(vu32 *)0x10400574 = 0x00010501;
|
||||
*(vu32 *)0x10400578 = 0;
|
||||
*(vu32 *)0x10400590 = 0x000002D0;
|
||||
*(vu32 *)0x1040059C = 0x00000000;
|
||||
|
||||
// Disco register
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
*(vu32 *)0x10400584 = 0x10101 * i;
|
||||
|
||||
*(vu32 *)0x10400468 = 0x18300000;
|
||||
*(vu32 *)0x1040046c = 0x18300000;
|
||||
*(vu32 *)0x10400494 = 0x18300000;
|
||||
*(vu32 *)0x10400498 = 0x18300000;
|
||||
*(vu32 *)0x10400568 = 0x18346500;
|
||||
*(vu32 *)0x1040056c = 0x18346500;
|
||||
|
||||
//Set CakeBrah framebuffers
|
||||
*((vu32 *)0x23FFFE00) = 0x18300000;
|
||||
*((vu32 *)0x23FFFE04) = 0x18300000;
|
||||
*((vu32 *)0x23FFFE08) = 0x18346500;
|
||||
|
||||
//Clear ARM11 entry offset
|
||||
*arm11 = 0;
|
||||
|
||||
//Wait for the entry to be set
|
||||
while(!*arm11);
|
||||
|
||||
//Jump to it
|
||||
((void (*)())*arm11)();
|
||||
}
|
||||
10
screeninit/source/start.s
Normal file
10
screeninit/source/start.s
Normal file
@@ -0,0 +1,10 @@
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Disable interrupts
|
||||
CPSID aif
|
||||
|
||||
b main
|
||||
|
||||
.word 0
|
||||
13
screeninit/source/types.h
Executable file
13
screeninit/source/types.h
Executable file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
26
source/buttons.h
Normal file
26
source/buttons.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* buttons.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define HID_PAD (*(vu32 *)0x10146000 ^ 0xFFF)
|
||||
#define BUTTON_R1 (1 << 8)
|
||||
#define BUTTON_L1 (1 << 9)
|
||||
#define BUTTON_A 1
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_X (1 << 10)
|
||||
#define BUTTON_Y (1 << 11)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define OVERRIDE_BUTTONS (BUTTON_B ^ 0xFFF)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y)
|
||||
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT)
|
||||
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
||||
181
source/config.c
Normal file
181
source/config.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* config.c
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "screeninit.h"
|
||||
#include "draw.h"
|
||||
#include "fs.h"
|
||||
#include "buttons.h"
|
||||
|
||||
void configureCFW(const char *configPath)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to save and reboot", 10, 30, COLOR_WHITE);
|
||||
|
||||
const char *multiOptionsText[] = { "Screen-init brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )" };
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
|
||||
"( ) SysNAND is updated (A9LH-only)",
|
||||
"( ) Force A9LH detection",
|
||||
"( ) Use second EmuNAND as default",
|
||||
"( ) Enable region/language emulation",
|
||||
"( ) Show current NAND in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Enable splash screen with no screen-init" };
|
||||
|
||||
struct multiOption {
|
||||
int posXs[4];
|
||||
int posY;
|
||||
u32 enabled;
|
||||
} multiOptions[] = {
|
||||
{ .posXs = {26, 31, 36, 41} },
|
||||
{ .posXs = {17, 26, 32, 44} }
|
||||
};
|
||||
|
||||
//Calculate the amount of the various kinds of options and pre-select the first single one
|
||||
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
|
||||
singleOptionsAmount = sizeof(singleOptionsText) / sizeof(char *),
|
||||
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
|
||||
selectedOption = multiOptionsAmount;
|
||||
|
||||
struct singleOption {
|
||||
int posY;
|
||||
u32 enabled;
|
||||
} singleOptions[singleOptionsAmount];
|
||||
|
||||
//Parse the existing options
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
multiOptions[i].enabled = MULTICONFIG(i);
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
singleOptions[i].enabled = CONFIG(i);
|
||||
|
||||
//Character to display a selected option
|
||||
char selected = 'x';
|
||||
|
||||
int endPos = 42;
|
||||
|
||||
//Display all the multiple choice options in white
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
{
|
||||
multiOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(multiOptionsText[i], 10, multiOptions[i].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
||||
}
|
||||
|
||||
endPos += SPACING_Y / 2;
|
||||
u32 color = COLOR_RED;
|
||||
|
||||
//Display all the normal options in white except for the first one
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
{
|
||||
singleOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(singleOptionsText[i], 10, singleOptions[i].posY, color);
|
||||
if(singleOptions[i].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||
color = COLOR_WHITE;
|
||||
}
|
||||
|
||||
u32 pressed = 0;
|
||||
|
||||
//Boring configuration menu
|
||||
while(pressed != BUTTON_START)
|
||||
{
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
}
|
||||
while(!(pressed & MENU_BUTTONS));
|
||||
|
||||
if(pressed != BUTTON_A)
|
||||
{
|
||||
//Remember the previously selected option
|
||||
u32 oldSelectedOption = selectedOption;
|
||||
|
||||
switch(pressed)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
selectedOption = 0;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
selectedOption = totalIndexes;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if(selectedOption == oldSelectedOption) continue;
|
||||
|
||||
//The user moved to a different option, print the old option in white and the new one in red. Only print 'x's if necessary
|
||||
if(oldSelectedOption < multiOptionsAmount)
|
||||
{
|
||||
drawString(multiOptionsText[oldSelectedOption], 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
|
||||
drawString(singleOptionsText[singleOldSelected], 10, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
}
|
||||
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawString(multiOptionsText[selectedOption], 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
drawString(singleOptionsText[singleSelected], 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//The selected option's status changed, print the 'x's accordingly
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
{
|
||||
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
||||
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
|
||||
multiOptions[selectedOption].enabled = oldEnabled == 3 ? 0 : oldEnabled + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
|
||||
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
|
||||
if(oldEnabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
if(singleOptions[singleSelected].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (last 12 bits)
|
||||
config &= 0x3F;
|
||||
|
||||
//Parse and write the new configuration
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
config |= multiOptions[i].enabled << (i * 2 + 6);
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
config |= singleOptions[i].enabled << (i + 16);
|
||||
|
||||
fileWrite(&config, configPath, 4);
|
||||
|
||||
//Zero the last booted FIRM flag
|
||||
CFG_BOOTENV = 0;
|
||||
|
||||
mcuReboot();
|
||||
}
|
||||
17
source/config.h
Normal file
17
source/config.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* config.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||
|
||||
#define CONFIG(a) ((config >> (a + 16)) & 1)
|
||||
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((config >> a) & b)
|
||||
|
||||
extern u32 config;
|
||||
|
||||
void configureCFW(const char *configPath);
|
||||
299
source/crypto.c
299
source/crypto.c
@@ -1,14 +1,15 @@
|
||||
// From http://github.com/b1l1s/ctr
|
||||
/*
|
||||
* crypto.c
|
||||
*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
*/
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include "memory.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
/****************************************************************
|
||||
* Crypto Libs
|
||||
* Crypto libs
|
||||
****************************************************************/
|
||||
|
||||
/* original version by megazig */
|
||||
@@ -57,10 +58,10 @@ __asm__\
|
||||
}
|
||||
#endif /*__thumb__*/
|
||||
|
||||
void aes_setkey(u8 keyslot, const void* key, u32 keyType, u32 mode)
|
||||
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
||||
{
|
||||
if(keyslot <= 0x03) return; // Ignore TWL keys for now
|
||||
u32* key32 = (u32*)key;
|
||||
u32 *key32 = (u32 *)key;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
||||
|
||||
@@ -70,7 +71,7 @@ void aes_setkey(u8 keyslot, const void* key, u32 keyType, u32 mode)
|
||||
REG_AESKEYFIFO[keyType] = key32[3];
|
||||
}
|
||||
|
||||
void aes_use_keyslot(u8 keyslot)
|
||||
static void aes_use_keyslot(u8 keyslot)
|
||||
{
|
||||
if(keyslot > 0x3F)
|
||||
return;
|
||||
@@ -79,9 +80,9 @@ void aes_use_keyslot(u8 keyslot)
|
||||
*REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */
|
||||
}
|
||||
|
||||
void aes_setiv(const void* iv, u32 mode)
|
||||
static void aes_setiv(const void *iv, u32 mode)
|
||||
{
|
||||
const u32* iv32 = (const u32*)iv;
|
||||
const u32 *iv32 = (const u32 *)iv;
|
||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||
|
||||
// Word order for IV can't be changed in REG_AESCNT and always default to reversed
|
||||
@@ -101,9 +102,9 @@ void aes_setiv(const void* iv, u32 mode)
|
||||
}
|
||||
}
|
||||
|
||||
void aes_advctr(void* ctr, u32 val, u32 mode)
|
||||
static void aes_advctr(void *ctr, u32 val, u32 mode)
|
||||
{
|
||||
u32* ctr32 = (u32*)ctr;
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
|
||||
int i;
|
||||
if(mode & AES_INPUT_BE)
|
||||
@@ -128,9 +129,9 @@ void aes_advctr(void* ctr, u32 val, u32 mode)
|
||||
}
|
||||
}
|
||||
|
||||
void aes_change_ctrmode(void* ctr, u32 fromMode, u32 toMode)
|
||||
static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode)
|
||||
{
|
||||
u32* ctr32 = (u32*)ctr;
|
||||
u32 *ctr32 = (u32 *)ctr;
|
||||
int i;
|
||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN)
|
||||
{
|
||||
@@ -150,13 +151,13 @@ void aes_change_ctrmode(void* ctr, u32 fromMode, u32 toMode)
|
||||
}
|
||||
}
|
||||
|
||||
void aes_batch(void* dst, const void* src, u32 blockCount)
|
||||
static void aes_batch(void *dst, const void *src, u32 blockCount)
|
||||
{
|
||||
*REG_AESBLKCNT = blockCount << 16;
|
||||
*REG_AESCNT |= AES_CNT_START;
|
||||
|
||||
const u32* src32 = (const u32*)src;
|
||||
u32* dst32 = (u32*)dst;
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
|
||||
u32 wbc = blockCount;
|
||||
u32 rbc = blockCount;
|
||||
@@ -183,7 +184,7 @@ void aes_batch(void* dst, const void* src, u32 blockCount)
|
||||
}
|
||||
}
|
||||
|
||||
void aes(void* dst, const void* src, u32 blockCount, void* iv, u32 mode, u32 ivMode)
|
||||
static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode)
|
||||
{
|
||||
*REG_AESCNT = mode |
|
||||
AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER |
|
||||
@@ -226,17 +227,17 @@ void aes(void* dst, const void* src, u32 blockCount, void* iv, u32 mode, u32 ivM
|
||||
}
|
||||
}
|
||||
|
||||
void sha_wait_idle()
|
||||
static void sha_wait_idle()
|
||||
{
|
||||
while(*REG_SHA_CNT & 1);
|
||||
}
|
||||
|
||||
void sha(void* res, const void* src, u32 size, u32 mode)
|
||||
static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
{
|
||||
sha_wait_idle();
|
||||
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
||||
|
||||
const u32* src32 = (const u32*)src;
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
int i;
|
||||
while(size >= 0x40)
|
||||
{
|
||||
@@ -253,7 +254,7 @@ void sha(void* res, const void* src, u32 size, u32 mode)
|
||||
}
|
||||
|
||||
sha_wait_idle();
|
||||
memcpy((void*)REG_SHA_INFIFO, src32, size);
|
||||
memcpy((void *)REG_SHA_INFIFO, src32, size);
|
||||
|
||||
*REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
|
||||
|
||||
@@ -266,157 +267,133 @@ void sha(void* res, const void* src, u32 size, u32 mode)
|
||||
else if(mode == SHA_1_MODE)
|
||||
hashSize = SHA_1_HASH_SIZE;
|
||||
|
||||
memcpy(res, (void*)REG_SHA_HASH, hashSize);
|
||||
}
|
||||
|
||||
void rsa_wait_idle()
|
||||
{
|
||||
while(*REG_RSA_CNT & 1);
|
||||
}
|
||||
|
||||
void rsa_use_keyslot(u32 keyslot)
|
||||
{
|
||||
*REG_RSA_CNT = (*REG_RSA_CNT & ~RSA_CNT_KEYSLOTS) | (keyslot << 4);
|
||||
}
|
||||
|
||||
void rsa_setkey(u32 keyslot, const void* mod, const void* exp, u32 mode)
|
||||
{
|
||||
rsa_wait_idle();
|
||||
*REG_RSA_CNT = (*REG_RSA_CNT & ~RSA_CNT_KEYSLOTS) | (keyslot << 4) | RSA_IO_BE | RSA_IO_NORMAL;
|
||||
|
||||
u32 size = mode * 4;
|
||||
|
||||
volatile u32* keyslotCnt = REG_RSA_SLOT0 + (keyslot << 4);
|
||||
keyslotCnt[0] &= ~(RSA_SLOTCNT_KEY_SET | RSA_SLOTCNT_WPROTECT);
|
||||
keyslotCnt[1] = mode;
|
||||
|
||||
memcpy((void*)REG_RSA_MOD_END - size, mod, size);
|
||||
|
||||
if(exp == NULL)
|
||||
{
|
||||
size -= 4;
|
||||
while(size)
|
||||
{
|
||||
*REG_RSA_EXPFIFO = 0;
|
||||
size -= 4;
|
||||
}
|
||||
*REG_RSA_EXPFIFO = 0x01000100; // 0x00010001 byteswapped
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32* exp32 = (const u32*)exp;
|
||||
while(size)
|
||||
{
|
||||
*REG_RSA_EXPFIFO = *exp32++;
|
||||
size -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rsa_iskeyset(u32 keyslot)
|
||||
{
|
||||
return *(REG_RSA_SLOT0 + (keyslot << 4)) & 1;
|
||||
}
|
||||
|
||||
void rsa(void* dst, const void* src, u32 size)
|
||||
{
|
||||
u32 keyslot = (*REG_RSA_CNT & RSA_CNT_KEYSLOTS) >> 4;
|
||||
if(rsa_iskeyset(keyslot) == 0)
|
||||
return;
|
||||
|
||||
rsa_wait_idle();
|
||||
*REG_RSA_CNT |= RSA_IO_BE | RSA_IO_NORMAL;
|
||||
|
||||
// Pad the message with zeroes so that it's a multiple of 8
|
||||
// and write the message with the end aligned with the register
|
||||
u32 padSize = ((size + 7) & ~7) - size;
|
||||
memset((void*)REG_RSA_TXT_END - (size + padSize), 0, padSize);
|
||||
memcpy((void*)REG_RSA_TXT_END - size, src, size);
|
||||
|
||||
// Start
|
||||
*REG_RSA_CNT |= RSA_CNT_START;
|
||||
|
||||
rsa_wait_idle();
|
||||
memcpy(dst, (void*)REG_RSA_TXT_END - size, size);
|
||||
}
|
||||
|
||||
int rsa_verify(const void* data, u32 size, const void* sig, u32 mode)
|
||||
{
|
||||
u8 dataHash[SHA_256_HASH_SIZE];
|
||||
sha(dataHash, data, size, SHA_256_MODE);
|
||||
|
||||
u8 decSig[0x100]; // Way too big, need to request a work area
|
||||
|
||||
u32 sigSize = mode * 4;
|
||||
rsa(decSig, sig, sigSize);
|
||||
|
||||
return memcmp(dataHash, decSig + (sigSize - SHA_256_HASH_SIZE), SHA_256_HASH_SIZE) == 0;
|
||||
memcpy(res, (void *)REG_SHA_HASH, hashSize);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Nand/FIRM Crypto stuff
|
||||
* NAND/FIRM crypto
|
||||
****************************************************************/
|
||||
|
||||
//Get Nand CTR key
|
||||
void getNandCTR(u8 *buf, u8 console) {
|
||||
u8 *addr = console ? (u8*)0x080D8BBC : (u8*)0x080D797C;
|
||||
u8 keyLen = 0x10; //CTR length
|
||||
addr += 0x0F;
|
||||
while (keyLen --) { *(buf++) = *(addr--); }
|
||||
static u8 nandCTR[0x10],
|
||||
nandSlot;
|
||||
|
||||
static u32 fatStart;
|
||||
|
||||
//Initialize the CTRNAND crypto
|
||||
void ctrNandInit(void)
|
||||
{
|
||||
u8 cid[0x10];
|
||||
u8 shaSum[0x20];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
sha(shaSum, cid, 0x10, SHA_256_MODE);
|
||||
memcpy(nandCTR, shaSum, 0x10);
|
||||
|
||||
if(console)
|
||||
{
|
||||
u8 keyY0x5[0x10] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
nandSlot = 0x05;
|
||||
fatStart = 0x5CAD7;
|
||||
}
|
||||
else
|
||||
{
|
||||
nandSlot = 0x04;
|
||||
fatStart = 0x5CAE5;
|
||||
}
|
||||
}
|
||||
|
||||
//Read firm0 from NAND and write to buffer
|
||||
void nandFirm0(u8 *outbuf, const u32 size, u8 console){
|
||||
u8 CTR[0x10];
|
||||
getNandCTR(CTR, console);
|
||||
aes_advctr(CTR, 0x0B130000/0x10, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf);
|
||||
aes_use_keyslot(0x06);
|
||||
aes(outbuf, outbuf, size / AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
//Read and decrypt from the selected CTRNAND
|
||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||
{
|
||||
u8 tmpCTR[0x10];
|
||||
memcpy(tmpCTR, nandCTR, 0x10);
|
||||
aes_advctr(tmpCTR, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Emulates the Arm9loader process
|
||||
void arm9loader(void *armHdr, u8 mode){
|
||||
//Nand key#2 (0x12C10)
|
||||
u8 key2[0x10] = {
|
||||
0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0
|
||||
};
|
||||
|
||||
//Firm keys
|
||||
u8 keyX[0x10];
|
||||
u8 keyY[0x10];
|
||||
u8 CTR[0x10];
|
||||
u32 slot = mode ? 0x16 : 0x15;
|
||||
|
||||
//Setup keys needed for arm9bin decryption
|
||||
memcpy((u8*)keyY, (void *)((uintptr_t)armHdr+0x10), 0x10);
|
||||
memcpy((u8*)CTR, (void *)((uintptr_t)armHdr+0x20), 0x10);
|
||||
u32 size = atoi((void *)((uintptr_t)armHdr+0x30));
|
||||
|
||||
if(mode){
|
||||
//Set 0x11 to key2 for the arm9bin and misc keys
|
||||
aes_setkey(0x11, (u8*)key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x11);
|
||||
aes((u8*)keyX, (void *)((uintptr_t)armHdr+0x60), 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, (u8*)keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
//Read
|
||||
u32 result;
|
||||
if(!firmSource)
|
||||
result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||
else
|
||||
{
|
||||
sector += emuOffset;
|
||||
result = sdmmc_sdcard_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||
}
|
||||
|
||||
aes_setkey(slot, (u8*)keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setiv((u8*)CTR, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(slot);
|
||||
|
||||
//Decrypt arm9bin
|
||||
aes((void *)(armHdr+0x800), (void *)(armHdr+0x800), size/AES_BLOCK_SIZE, CTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
//Decrypt
|
||||
aes_use_keyslot(nandSlot);
|
||||
aes(outbuf, outbuf, sectorCount * 0x200 / AES_BLOCK_SIZE, tmpCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
if(mode){
|
||||
//Set keys 0x19..0x1F keyXs
|
||||
u8* decKey = (void *)((uintptr_t)armHdr+0x89824);
|
||||
return result;
|
||||
}
|
||||
|
||||
//Decrypt a FIRM ExeFS
|
||||
void decryptExeFs(u8 *inbuf)
|
||||
{
|
||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
|
||||
u8 ncchCTR[0x10] = {0};
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCTR[7 - i] = *(inbuf + 0x108 + i);
|
||||
ncchCTR[8] = 2;
|
||||
|
||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setiv(ncchCTR, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
//ARM9Loader replacement
|
||||
void arm9Loader(u8 *arm9Section, u32 mode)
|
||||
{
|
||||
//Firm keys
|
||||
u8 keyY[0x10],
|
||||
arm9BinCTR[0x10],
|
||||
arm9BinSlot = mode ? 0x16 : 0x15;
|
||||
|
||||
//Setup keys needed for arm9bin decryption
|
||||
memcpy(keyY, arm9Section + 0x10, 0x10);
|
||||
memcpy(arm9BinCTR, arm9Section + 0x20, 0x10);
|
||||
|
||||
//Calculate the size of the ARM9 binary
|
||||
u32 arm9BinSize = 0;
|
||||
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
|
||||
for(u8 *tmp = arm9Section + 0x30; *tmp; tmp++)
|
||||
arm9BinSize = (arm9BinSize << 3) + (arm9BinSize << 1) + *tmp - '0';
|
||||
|
||||
if(mode)
|
||||
{
|
||||
const u8 key2[0x10] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0};
|
||||
u8 keyX[0x10];
|
||||
|
||||
//Set 0x11 to key2 for the arm9bin and misc keys
|
||||
aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x11);
|
||||
for(slot = 0x19; slot < 0x20; slot++) {
|
||||
aes_setkey(0x11, (u8*)key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes(decKey, (void *)((uintptr_t)armHdr+0x89814), 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, (u8*)decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
*(u8 *)((void *)((uintptr_t)armHdr+0x89814+0xF)) += 1;
|
||||
aes(keyX, arm9Section + 0x60, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(arm9BinSlot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setiv(arm9BinCTR, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(arm9BinSlot);
|
||||
|
||||
//Decrypt arm9bin
|
||||
aes(arm9Section + 0x800, arm9Section + 0x800, arm9BinSize / AES_BLOCK_SIZE, arm9BinCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Set >=9.6 KeyXs
|
||||
if(mode)
|
||||
{
|
||||
u8 keyData[0x10] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98},
|
||||
decKey[0x10];
|
||||
|
||||
//Set keys 0x19..0x1F keyXs
|
||||
aes_use_keyslot(0x11);
|
||||
for(u8 slot = 0x19; slot < 0x20; slot++)
|
||||
{
|
||||
aes(decKey, keyData, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
keyData[0xF] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
165
source/crypto.h
165
source/crypto.h
@@ -1,138 +1,87 @@
|
||||
// From http://github.com/b1l1s/ctr
|
||||
/*
|
||||
* crypto.h
|
||||
*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
*/
|
||||
|
||||
#ifndef __CRYPTO_H
|
||||
#define __CRYPTO_H
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
#define FIRM_TYPE_ARM9 0
|
||||
#define FIRM_TYPE_ARM11 1
|
||||
|
||||
#define MEDIA_UNITS 0x200
|
||||
|
||||
#define NCCH_MAGIC (0x4843434E)
|
||||
#define NCSD_MAGIC (0x4453434E)
|
||||
#define FIRM_MAGIC (0x4D524946)
|
||||
#define ARM9BIN_MAGIC (0x47704770)
|
||||
|
||||
/**************************AES****************************/
|
||||
#define REG_AESCNT ((volatile u32*)0x10009000)
|
||||
#define REG_AESBLKCNT ((volatile u32*)0x10009004)
|
||||
#define REG_AESWRFIFO ((volatile u32*)0x10009008)
|
||||
#define REG_AESRDFIFO ((volatile u32*)0x1000900C)
|
||||
#define REG_AESKEYSEL ((volatile u8 *)0x10009010)
|
||||
#define REG_AESKEYCNT ((volatile u8 *)0x10009011)
|
||||
#define REG_AESCTR ((volatile u32*)0x10009020)
|
||||
#define REG_AESCNT ((vu32 *)0x10009000)
|
||||
#define REG_AESBLKCNT ((vu32 *)0x10009004)
|
||||
#define REG_AESWRFIFO ((vu32 *)0x10009008)
|
||||
#define REG_AESRDFIFO ((vu32 *)0x1000900C)
|
||||
#define REG_AESKEYSEL ((vu8 *)0x10009010)
|
||||
#define REG_AESKEYCNT ((vu8 *)0x10009011)
|
||||
#define REG_AESCTR ((vu32 *)0x10009020)
|
||||
|
||||
#define REG_AESKEYFIFO ((volatile u32*)0x10009100)
|
||||
#define REG_AESKEYXFIFO ((volatile u32*)0x10009104)
|
||||
#define REG_AESKEYYFIFO ((volatile u32*)0x10009108)
|
||||
#define REG_AESKEYFIFO ((vu32 *)0x10009100)
|
||||
#define REG_AESKEYXFIFO ((vu32 *)0x10009104)
|
||||
#define REG_AESKEYYFIFO ((vu32 *)0x10009108)
|
||||
|
||||
#define AES_CCM_DECRYPT_MODE (0u << 27)
|
||||
#define AES_CCM_ENCRYPT_MODE (1u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CTR_MODE (2u << 27)
|
||||
#define AES_CBC_DECRYPT_MODE (4u << 27)
|
||||
#define AES_CBC_ENCRYPT_MODE (5u << 27)
|
||||
#define AES_ECB_DECRYPT_MODE (6u << 27)
|
||||
#define AES_ECB_ENCRYPT_MODE (7u << 27)
|
||||
#define AES_ALL_MODES (7u << 27)
|
||||
#define AES_ALL_MODES (7u << 27)
|
||||
|
||||
#define AES_CNT_START 0x80000000
|
||||
#define AES_CNT_INPUT_ORDER 0x02000000
|
||||
#define AES_CNT_START 0x80000000
|
||||
#define AES_CNT_INPUT_ORDER 0x02000000
|
||||
#define AES_CNT_OUTPUT_ORDER 0x01000000
|
||||
#define AES_CNT_INPUT_ENDIAN 0x00800000
|
||||
#define AES_CNT_OUTPUT_ENDIAN 0x00400000
|
||||
#define AES_CNT_FLUSH_READ 0x00000800
|
||||
#define AES_CNT_FLUSH_WRITE 0x00000400
|
||||
#define AES_CNT_FLUSH_READ 0x00000800
|
||||
#define AES_CNT_FLUSH_WRITE 0x00000400
|
||||
|
||||
#define AES_INPUT_BE (AES_CNT_INPUT_ENDIAN)
|
||||
#define AES_INPUT_LE 0
|
||||
#define AES_INPUT_NORMAL (AES_CNT_INPUT_ORDER)
|
||||
#define AES_INPUT_REVERSED 0
|
||||
#define AES_INPUT_BE (AES_CNT_INPUT_ENDIAN)
|
||||
#define AES_INPUT_LE 0
|
||||
#define AES_INPUT_NORMAL (AES_CNT_INPUT_ORDER)
|
||||
#define AES_INPUT_REVERSED 0
|
||||
|
||||
#define AES_TEMP_KEYSLOT 0x11
|
||||
#define AES_BLOCK_SIZE 0x10
|
||||
|
||||
#define AES_BLOCK_SIZE 0x10
|
||||
|
||||
#define AES_KEYCNT_WRITE (1 << 0x7)
|
||||
#define AES_KEYNORMAL 0
|
||||
#define AES_KEYX 1
|
||||
#define AES_KEYY 2
|
||||
#define AES_KEYCNT_WRITE (1 << 0x7)
|
||||
#define AES_KEYNORMAL 0
|
||||
#define AES_KEYX 1
|
||||
#define AES_KEYY 2
|
||||
|
||||
/**************************SHA****************************/
|
||||
#define REG_SHA_CNT ((volatile u32*)0x1000A000)
|
||||
#define REG_SHA_BLKCNT ((volatile u32*)0x1000A004)
|
||||
#define REG_SHA_HASH ((volatile u32*)0x1000A040)
|
||||
#define REG_SHA_INFIFO ((volatile u32*)0x1000A080)
|
||||
#define REG_SHA_CNT ((vu32 *)0x1000A000)
|
||||
#define REG_SHA_BLKCNT ((vu32 *)0x1000A004)
|
||||
#define REG_SHA_HASH ((vu32 *)0x1000A040)
|
||||
#define REG_SHA_INFIFO ((vu32 *)0x1000A080)
|
||||
|
||||
#define SHA_CNT_STATE 0x00000003
|
||||
#define SHA_CNT_UNK2 0x00000004
|
||||
#define SHA_CNT_STATE 0x00000003
|
||||
#define SHA_CNT_UNK2 0x00000004
|
||||
#define SHA_CNT_OUTPUT_ENDIAN 0x00000008
|
||||
#define SHA_CNT_MODE 0x00000030
|
||||
#define SHA_CNT_ENABLE 0x00010000
|
||||
#define SHA_CNT_ACTIVE 0x00020000
|
||||
#define SHA_CNT_MODE 0x00000030
|
||||
#define SHA_CNT_ENABLE 0x00010000
|
||||
#define SHA_CNT_ACTIVE 0x00020000
|
||||
|
||||
#define SHA_HASH_READY 0x00000000
|
||||
#define SHA_NORMAL_ROUND 0x00000001
|
||||
#define SHA_FINAL_ROUND 0x00000002
|
||||
#define SHA_HASH_READY 0x00000000
|
||||
#define SHA_NORMAL_ROUND 0x00000001
|
||||
#define SHA_FINAL_ROUND 0x00000002
|
||||
|
||||
#define SHA_OUTPUT_BE SHA_CNT_OUTPUT_ENDIAN
|
||||
#define SHA_OUTPUT_LE 0
|
||||
#define SHA_OUTPUT_BE SHA_CNT_OUTPUT_ENDIAN
|
||||
#define SHA_OUTPUT_LE 0
|
||||
|
||||
#define SHA_256_MODE 0
|
||||
#define SHA_224_MODE 0x00000010
|
||||
#define SHA_1_MODE 0x00000020
|
||||
#define SHA_256_MODE 0
|
||||
#define SHA_224_MODE 0x00000010
|
||||
#define SHA_1_MODE 0x00000020
|
||||
|
||||
#define SHA_256_HASH_SIZE (256 / 8)
|
||||
#define SHA_224_HASH_SIZE (224 / 8)
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
#define SHA_256_HASH_SIZE (256 / 8)
|
||||
#define SHA_224_HASH_SIZE (224 / 8)
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
/**************************RSA****************************/
|
||||
#define REG_RSA_CNT ((volatile u32*)0x1000B000)
|
||||
#define REG_RSA_SLOT0 ((volatile u32*)0x1000B100)
|
||||
#define REG_RSA_SLOT1 ((volatile u32*)0x1000B110)
|
||||
#define REG_RSA_SLOT2 ((volatile u32*)0x1000B120)
|
||||
#define REG_RSA_SLOT3 ((volatile u32*)0x1000B130)
|
||||
#define REG_RSA_EXPFIFO ((volatile u32*)0x1000B200)
|
||||
#define REG_RSA_MOD_END ((volatile u32*)0x1000B500)
|
||||
#define REG_RSA_TXT_END ((volatile u32*)0x1000B900)
|
||||
extern u32 emuOffset, console, firmSource;
|
||||
|
||||
#define RSA_CNT_START 0x00000001
|
||||
#define RSA_CNT_KEYSLOTS 0x000000F0
|
||||
#define RSA_CNT_IO_ENDIAN 0x00000100
|
||||
#define RSA_CNT_IO_ORDER 0x00000200
|
||||
|
||||
#define RSA_SLOTCNT_KEY_SET 0x00000001
|
||||
#define RSA_SLOTCNT_WPROTECT 0x00000002 // Write protect
|
||||
|
||||
#define RSA_IO_BE RSA_CNT_IO_ENDIAN
|
||||
#define RSA_IO_LE 0
|
||||
#define RSA_IO_NORMAL RSA_CNT_IO_ORDER
|
||||
#define RSA_IO_REVERSED 0
|
||||
|
||||
#define RSA_TEMP_KEYSLOT 0
|
||||
|
||||
#define RSA_1024_MODE 0x20
|
||||
#define RSA_2048_MODE 0x40
|
||||
|
||||
//Crypto Libs
|
||||
void aes_setkey(u8 keyslot, const void* key, u32 keyType, u32 mode);
|
||||
void aes_use_keyslot(u8 keyslot);
|
||||
void aes(void* dst, const void* src, u32 blockCount, void* iv, u32 mode, u32 ivMode);
|
||||
void aes_setiv(const void* iv, u32 mode);
|
||||
void aes_advctr(void* ctr, u32 val, u32 mode);
|
||||
void aes_change_ctrmode(void* ctr, u32 fromMode, u32 toMode);
|
||||
void aes_batch(void* dst, const void* src, u32 blockCount);
|
||||
void sha(void* res, const void* src, u32 size, u32 mode);
|
||||
void rsa_setkey(u32 keyslot, const void* mod, const void* exp, u32 mode);
|
||||
void rsa_use_keyslot(u32 keyslot);
|
||||
int rsa_verify(const void* data, u32 size, const void* sig, u32 mode);
|
||||
|
||||
//NAND/FIRM stuff
|
||||
void getNandCTR(u8 *buf, u8 console);
|
||||
void nandFirm0(u8 *outbuf, const u32 size, u8 console);
|
||||
void arm9loader(void *armHdr, u8 mode);
|
||||
|
||||
#endif /*__CRYPTO_H*/
|
||||
void ctrNandInit(void);
|
||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
void decryptExeFs(u8 *inbuf);
|
||||
void arm9Loader(u8 *arm9Section, u32 mode);
|
||||
@@ -1,23 +1,93 @@
|
||||
/*
|
||||
* draw.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "screeninit.h"
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "font.h"
|
||||
|
||||
static struct fb* fb = (struct fb*) 0x23FFFE00;
|
||||
#define SCREEN_TOP_WIDTH 400
|
||||
#define SCREEN_TOP_HEIGHT 240
|
||||
|
||||
void clearScreen(void){
|
||||
memset(fb->top_left, 0, 0x38400);
|
||||
memset(fb->top_right, 0, 0x38400);
|
||||
memset(fb->bottom, 0, 0x38400);
|
||||
static const struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
} *const fb = (struct fb *)0x23FFFE00;
|
||||
|
||||
static inline int strlen(const char *string)
|
||||
{
|
||||
char *stringEnd = (char *)string;
|
||||
|
||||
while(*stringEnd) stringEnd++;
|
||||
|
||||
return stringEnd - string;
|
||||
}
|
||||
|
||||
void loadSplash(void){
|
||||
clearScreen();
|
||||
if(fileRead(fb->top_left, "/rei/splash.bin", 0x46500) != 0) return;
|
||||
unsigned i,t; for(t=220;t>0;t--){for(i=0xFFFF;i>0;i--);}; //Ghetto sleep func
|
||||
void clearScreens(void)
|
||||
{
|
||||
memset32(fb->top_left, 0, 0x46500);
|
||||
memset32(fb->top_right, 0, 0x46500);
|
||||
memset32(fb->bottom, 0, 0x38400);
|
||||
}
|
||||
|
||||
void loadSplash(void)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
//Don't delay boot if no splash image is on the SD
|
||||
if(fileRead(fb->top_left, "/luma/splash.bin", 0x46500) +
|
||||
fileRead(fb->bottom, "/luma/splashbottom.bin", 0x38400))
|
||||
{
|
||||
u64 i = 0x1400000;
|
||||
while(i--) __asm("mov r0, r0"); //Less Ghetto sleep func
|
||||
}
|
||||
}
|
||||
|
||||
void drawCharacter(char character, int posX, int posY, u32 color)
|
||||
{
|
||||
u8 *const select = fb->top_left;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
{
|
||||
char charPos = font[character * 8 + y];
|
||||
|
||||
for(int x = 7; x >= 0; x--)
|
||||
if ((charPos >> x) & 1)
|
||||
{
|
||||
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||
|
||||
select[screenPos] = color >> 16;
|
||||
select[screenPos + 1] = color >> 8;
|
||||
select[screenPos + 2] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int drawString(const char *string, int posX, int posY, u32 color)
|
||||
{
|
||||
for(int i = 0, line_i = 0; i < strlen(string); i++, line_i++)
|
||||
{
|
||||
if(string[i] == '\n')
|
||||
{
|
||||
posY += SPACING_Y;
|
||||
line_i = 0;
|
||||
i++;
|
||||
}
|
||||
else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X)
|
||||
{
|
||||
// Make sure we never get out of the screen.
|
||||
posY += SPACING_Y;
|
||||
line_i = 2; //Little offset so we know the same string continues.
|
||||
if(string[i] == ' ') i++; //Spaces at the start look weird
|
||||
}
|
||||
|
||||
drawCharacter(string[i], posX + line_i * SPACING_X, posY, color);
|
||||
}
|
||||
|
||||
return posY;
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
/*
|
||||
* draw.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct fb {
|
||||
u8 *top_left;
|
||||
u8 *top_right;
|
||||
u8 *bottom;
|
||||
};
|
||||
#define SPACING_Y 10
|
||||
#define SPACING_X 8
|
||||
#define COLOR_TITLE 0xFF9900
|
||||
#define COLOR_WHITE 0xFFFFFF
|
||||
#define COLOR_RED 0x0000FF
|
||||
#define COLOR_BLACK 0x000000
|
||||
|
||||
void clearScreen(void);
|
||||
void loadSplash(void);
|
||||
void loadSplash(void);
|
||||
void clearScreens(void);
|
||||
void drawCharacter(char character, int posX, int posY, u32 color);
|
||||
int drawString(const char *string, int posX, int posY, u32 color);
|
||||
@@ -1,64 +1,78 @@
|
||||
/*
|
||||
* emunand.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
|
||||
#include "emunand.h"
|
||||
#include "memory.h"
|
||||
#include "fatfs/ff.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
|
||||
static u8 *temp = (u8*)0x24300000;
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND)
|
||||
{
|
||||
static u8 *const temp = (u8 *)0x24300000;
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head){
|
||||
u32 nandSize = getMMCDevice(0)->total_size;
|
||||
if (sdmmc_sdcard_readsectors(nandSize, 1, temp) == 0) {
|
||||
if (*(u32*)(temp + 0x100) == NCSD_MAGIC) {
|
||||
*off = 0;
|
||||
*head = nandSize;
|
||||
const u32 nandSize = getMMCDevice(0)->total_size;
|
||||
u32 nandOffset = *emuNAND == 1 ? 0 :
|
||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||
|
||||
//Check for RedNAND
|
||||
if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0)
|
||||
{
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
{
|
||||
*off = nandOffset + 1;
|
||||
*head = nandOffset + 1;
|
||||
}
|
||||
|
||||
//Check for Gateway emuNAND
|
||||
else if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0)
|
||||
{
|
||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
{
|
||||
*off = nandOffset;
|
||||
*head = nandOffset + nandSize;
|
||||
}
|
||||
|
||||
/* Fallback to the first emuNAND if there's no second one,
|
||||
or to SysNAND if there isn't any */
|
||||
else
|
||||
{
|
||||
(*emuNAND)--;
|
||||
if(*emuNAND) getEmunandSect(off, head, emuNAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getSDMMC(void *pos, u32 *off, u32 size){
|
||||
u32 getSDMMC(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for struct code
|
||||
unsigned char pattern[] = {0x01, 0x21, 0x20, 0x18, 0x20, 0x30};
|
||||
*off = (u32)memsearch(pos, pattern, size, 6);
|
||||
|
||||
//Get DCD values
|
||||
unsigned char buf[4];
|
||||
int p;
|
||||
u32 addr = 0;
|
||||
u32 additive = 0;
|
||||
memcpy((void*)buf, (void*)(*off+0x0A), 4);
|
||||
for (p = 0; p < 4; p++) addr |= ((u32) buf[p]) << (8 * p);
|
||||
memcpy((void*)buf, (void*)(*off+0x0E), 4);
|
||||
for (p = 0; p < 4; p++) additive |= ((u32) buf[p]) << (8 * p);
|
||||
|
||||
//Return result
|
||||
*off = addr + additive;
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
const u8 *off = memsearch(pos, pattern, size, 4) - 1;
|
||||
|
||||
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
||||
}
|
||||
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff){
|
||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff)
|
||||
{
|
||||
//Look for read/write code
|
||||
unsigned char pattern[] = {0x04, 0x00, 0x0D, 0x00, 0x17, 0x00, 0x1E, 0x00, 0xC8, 0x05};
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
*writeOff = (u32)memsearch(pos, pattern, size, 10);
|
||||
*readOff = (u32)memsearch((void *)(*writeOff - 0x1000), pattern, 0x1000, 10);
|
||||
*readOff = (u32)memsearch(pos, pattern, size, 4) - 6;
|
||||
*writeOff = (u32)memsearch((u8 *)(*readOff + 0xA), pattern, 0x100, 4) - 6;
|
||||
}
|
||||
|
||||
void getMPU(void *pos, u32 *off, u32 size){
|
||||
//Look for MPU code
|
||||
unsigned char pattern[] = {0x03, 0x00, 0x24, 0x00, 0x00, 0x00, 0x10};
|
||||
u32 *getMPU(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
*off = (u32)memsearch(pos, pattern, size, 7);
|
||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||
}
|
||||
|
||||
void getEmuCode(void *pos, u32 *off, u32 size){
|
||||
void *proc9 = memsearch(pos, "Process9", size, 8);
|
||||
unsigned char pattern[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF};
|
||||
void *getEmuCode(u8 *proc9Offset)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
|
||||
//We're looking for the last spot before Process9
|
||||
*off = (u32)memsearch(pos, pattern, size - (size - (u32)(proc9 - pos)), 6) + 0xF;
|
||||
//Looking for the last free space before Process9
|
||||
return memsearch(proc9Offset - 0x3000, pattern, 0x3000, 6) + 0x455;
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
/*
|
||||
* emunand.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
|
||||
#ifndef EMU_INC
|
||||
#define EMU_INC
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define NCSD_MAGIC (0x4453434E)
|
||||
#define NCSD_MAGIC 0x4453434E
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head);
|
||||
void getSDMMC(void *pos, u32 *off, u32 size);
|
||||
void getEmuRW(void *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||
void getMPU(void *pos, u32 *off, u32 size);
|
||||
void getEmuCode(void *pos, u32 *off, u32 size);
|
||||
|
||||
#endif
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND);
|
||||
u32 getSDMMC(u8 *pos, u32 size);
|
||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||
u32 *getMPU(u8 *pos, u32 size);
|
||||
void *getEmuCode(u8 *proc9Offset);
|
||||
254
source/fatfs/00history.txt
Normal file
254
source/fatfs/00history.txt
Normal file
@@ -0,0 +1,254 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
First stable version.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support of exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD and removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
FatFs Module Source Files R0.11
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
ff.c FatFs module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
|
||||
FatFs Module Source Files R0.12
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
ff.c FatFs module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
/* Definitions of physical drive number for each media */
|
||||
#define SDCARD 0
|
||||
#define CTRNAND 1
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
@@ -30,11 +33,19 @@ DSTATUS disk_status (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
sdmmc_sdcard_init();
|
||||
switch(pdrv)
|
||||
{
|
||||
case SDCARD:
|
||||
sdmmc_sdcard_init();
|
||||
break;
|
||||
case CTRNAND:
|
||||
ctrNandInit();
|
||||
break;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
@@ -45,18 +56,25 @@ DSTATUS disk_initialize (
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_readsectors(sector, count, buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
switch(pdrv)
|
||||
{
|
||||
case SDCARD:
|
||||
if(sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff))
|
||||
return RES_PARERR;
|
||||
break;
|
||||
case CTRNAND:
|
||||
if(ctrNandRead(sector, count, (BYTE *)buff))
|
||||
return RES_PARERR;
|
||||
break;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,18 +85,16 @@ DRESULT disk_read (
|
||||
|
||||
#if _USE_WRITE
|
||||
DRESULT disk_write (
|
||||
__attribute__((unused))
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Sector address in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if(pdrv == SDCARD && sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff))
|
||||
return RES_PARERR;
|
||||
|
||||
return RES_OK;
|
||||
return RES_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
10481
source/fatfs/ff.c
10481
source/fatfs/ff.c
File diff suppressed because it is too large
Load Diff
@@ -1,350 +1,366 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module include R0.11 (C)ChaN, 2015
|
||||
/----------------------------------------------------------------------------/
|
||||
/ FatFs module is a free software that opened under license policy of
|
||||
/ following conditions.
|
||||
/
|
||||
/ Copyright (C) 2015, ChaN, all right reserved.
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ 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 32020 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
#if _FATFS != _FFCONF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode string */
|
||||
#if !_USE_LFN
|
||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||
#endif
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#endif
|
||||
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
|
||||
BYTE drv; /* Physical drive number */
|
||||
BYTE csize; /* Sectors per cluster (1,2,4...128) */
|
||||
BYTE n_fats; /* Number of FAT copies (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 n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
#if _MAX_SS != _MIN_SS
|
||||
WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
DWORD last_clust; /* Last allocated cluster */
|
||||
DWORD free_clust; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */
|
||||
DWORD fsize; /* Sectors per FAT */
|
||||
DWORD volbase; /* Volume start sector */
|
||||
DWORD fatbase; /* FAT start sector */
|
||||
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
|
||||
DWORD database; /* Data start 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) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
BYTE flag; /* Status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD fsize; /* File size */
|
||||
DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
|
||||
DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */
|
||||
DWORD dsect; /* 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[] */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||
#endif
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */
|
||||
WORD id; /* Owner file system mount ID (**do not change order**) */
|
||||
WORD index; /* Current read/write index number */
|
||||
DWORD sclust; /* Table start cluster (0:Root dir) */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
|
||||
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
|
||||
#if _FS_LOCK
|
||||
UINT lockid; /* File lock ID (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
#if _USE_LFN
|
||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
|
||||
#endif
|
||||
#if _USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
DWORD fsize; /* File size */
|
||||
WORD fdate; /* Last modified date */
|
||||
WORD ftime; /* Last modified time */
|
||||
BYTE fattrib; /* Attribute */
|
||||
TCHAR fname[13]; /* Short file name (8.3 format) */
|
||||
#if _USE_LFN
|
||||
TCHAR* lfname; /* Pointer to the LFN buffer */
|
||||
UINT lfsize; /* Size of LFN buffer in TCHAR */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||
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_SHARE */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||
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 */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->fsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY && !_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* Unicode support functions */
|
||||
#if _USE_LFN /* 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 */
|
||||
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 */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access control and file status flags (FIL.flag) */
|
||||
|
||||
#define FA_READ 0x01
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
|
||||
#if !_FS_READONLY
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA__WRITTEN 0x20
|
||||
#define FA__DIRTY 0x40
|
||||
#endif
|
||||
|
||||
|
||||
/* FAT sub type (FATFS.fs_type) */
|
||||
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
|
||||
|
||||
/* File attribute bits for directory entry */
|
||||
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_VOL 0x08 /* Volume label */
|
||||
#define AM_LFN 0x0F /* LFN entry */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||
|
||||
|
||||
/* Fast seek feature */
|
||||
#define CREATE_LINKMAP 0xFFFFFFFF
|
||||
|
||||
|
||||
|
||||
/*--------------------------------*/
|
||||
/* Multi-byte word access macros */
|
||||
|
||||
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
|
||||
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
|
||||
#else /* Use byte-by-byte access to the FAT structure */
|
||||
#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
|
||||
#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
|
||||
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
|
||||
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module include R0.12 (C)ChaN, 2016
|
||||
/----------------------------------------------------------------------------/
|
||||
/ FatFs module is a free software that opened under license policy of
|
||||
/ following conditions.
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ 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 88100 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "integer.h" /* Basic integer types */
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
#if _FATFS != _FFCONF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if _MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#if _LFN_UNICODE /* Unicode string */
|
||||
#if _USE_LFN == 0
|
||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||
#endif
|
||||
#ifndef _INC_TCHAR
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#endif
|
||||
|
||||
#else /* ANSI/OEM string */
|
||||
#ifndef _INC_TCHAR
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* File system object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* File system type (0:N/A) */
|
||||
BYTE drv; /* 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 n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if _MAX_SS != _MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if _FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
|
||||
#endif
|
||||
#if _FS_REENTRANT
|
||||
_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if _FS_RPATH != 0
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if _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) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
DWORD volbase; /* Volume base sector */
|
||||
DWORD fatbase; /* FAT base sector */
|
||||
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) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Type of file size variables and object identifier */
|
||||
|
||||
#if _FS_EXFAT
|
||||
#if _USE_LFN == 0
|
||||
#error LFN must be enabled when enable exFAT
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#else
|
||||
typedef DWORD FSIZE_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (_FDID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if _FS_EXFAT
|
||||
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
|
||||
DWORD 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) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} _FDID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
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 (not valid when fprt 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[] */
|
||||
#endif
|
||||
#if _USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
|
||||
#endif
|
||||
#if !_FS_TINY
|
||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE* fn; /* Pointer to the SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if _USE_LFN != 0
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
||||
#endif
|
||||
#if _USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if _USE_LFN != 0
|
||||
TCHAR altname[13]; /* Altenative file name */
|
||||
TCHAR fname[_MAX_LFN + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[13]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
|
||||
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_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of a file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of the file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */
|
||||
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 */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !_FS_READONLY && !_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 */
|
||||
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 */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access control and file status flags (FIL.flag) */
|
||||
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define _FA_MODIFIED 0x20
|
||||
#define _FA_DIRTY 0x40
|
||||
|
||||
|
||||
/* FAT sub type (FATFS.fs_type) */
|
||||
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
|
||||
/* File attribute bits for directory entry */
|
||||
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_VOL 0x08 /* Volume label */
|
||||
#define AM_LFN 0x0F /* LFN entry */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
#define AM_MASK 0x3F /* Mask of defined bits */
|
||||
|
||||
|
||||
/* Fast seek controls */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FATFS */
|
||||
|
||||
@@ -1,271 +1,266 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.11 (C)ChaN, 2015
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 32020 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Functions and Buffer Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _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. */
|
||||
|
||||
|
||||
#define _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
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||
/ f_truncate() and f_rename() function are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 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().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _USE_FIND 0
|
||||
/* This option switches filtered directory read feature and related functions,
|
||||
/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_TINY need to be set to 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _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.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 775 - Baltic
|
||||
/ 850 - Multilingual Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 858 - Multilingual Latin 1 + Euro
|
||||
/ 862 - Hebrew
|
||||
/ 866 - Russian
|
||||
/ 874 - Thai
|
||||
/ 932 - Japanese Shift_JIS (DBCS)
|
||||
/ 936 - Simplified Chinese GBK (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese Big5 (DBCS)
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 2
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN option switches the LFN feature.
|
||||
/
|
||||
/ 0: Disable LFN feature. _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.
|
||||
/
|
||||
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
|
||||
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
|
||||
/ 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:Unicode)
|
||||
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
|
||||
/ to 1. This option also affects behavior of string I/O functions. */
|
||||
|
||||
|
||||
#define _STRF_ENCODE 0
|
||||
/* When _LFN_UNICODE is 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().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ When _LFN_UNICODE is 0, this option has no effect. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures relative path feature.
|
||||
/
|
||||
/ 0: Disable relative path feature and remove related functions.
|
||||
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/
|
||||
/ Note that directory items read via f_readdir() are affected by this option. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
|
||||
/* _STR_VOLUME_ID option switches string volume ID feature.
|
||||
/ 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
|
||||
/ the drive ID strings are: A-Z and 0-9. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0
|
||||
/* This option switches multi-partition feature. 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 feature is enabled (1),
|
||||
/ each logical drive number is 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, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
|
||||
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _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.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 2
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2015
|
||||
/* The _FS_NORTC option switches timestamp feature. If the system does not have
|
||||
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
|
||||
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
|
||||
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
|
||||
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
|
||||
/ to be added to the project to read current time form RTC. _NORTC_MON,
|
||||
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
|
||||
|
||||
|
||||
#define _FS_LOCK 0
|
||||
/* The _FS_LOCK option switches file lock feature to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock feature. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock feature. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock feature is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define _FS_REENTRANT 0
|
||||
#define _FS_TIMEOUT 1000
|
||||
#define _SYNC_t HANDLE
|
||||
/* The _FS_REENTRANT option 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 feature.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _FS_TIMEOUT and _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*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
|
||||
|
||||
#define _WORD_ACCESS 0
|
||||
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||
/ which access method is used to the word data on the FAT volume.
|
||||
/
|
||||
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||
/
|
||||
/ * Address misaligned memory access is always allowed to ALL instructions.
|
||||
/ * Byte order on the memory is little-endian.
|
||||
/
|
||||
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
|
||||
/ Following table shows allowable settings of some processor types.
|
||||
/
|
||||
/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
|
||||
/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
|
||||
/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
|
||||
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
|
||||
/ AVR32 0 *1 RL78 0 *2 R32C 0 *2
|
||||
/ PIC18 0/1 SH-2 0 *1 M16C 0/1
|
||||
/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2
|
||||
/ PIC32 0 *1 H8/300H 0 *1 8051 0/1
|
||||
/
|
||||
/ *1:Big-endian.
|
||||
/ *2:Unaligned memory access is not supported.
|
||||
/ *3:Some compilers generate LDM/STM for mem_cpy function.
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.12 (C)ChaN, 2016
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 88100 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _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 1
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 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().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _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
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _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. */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_TINY need to be 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _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.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 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
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 2
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN switches the support of long file name (LFN).
|
||||
/
|
||||
/ 0: Disable support of LFN. _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.
|
||||
/ 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:Unicode)
|
||||
/ 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 _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().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ This option has no effect when _LFN_UNICODE == 0. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures support of relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","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
|
||||
/ 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.
|
||||
/ 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
|
||||
/ 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, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches support of 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
|
||||
/* 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.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _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. */
|
||||
|
||||
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system in addition to the traditional
|
||||
/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
|
||||
/ Note that enabling exFAT discards C89 compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 3
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2016
|
||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
/ 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). */
|
||||
|
||||
|
||||
#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
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ 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
|
||||
/ 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.
|
||||
/ 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*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
/*-------------------------------------------*/
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#else /* Embedded platform */
|
||||
|
||||
/* This type MUST be 8 bit */
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types MUST be 16 bit */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types MUST be 16 bit or 32 bit */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* These types MUST be 32 bit */
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*-------------------------------------------*/
|
||||
/* Integer type definitions for FatFs module */
|
||||
/*-------------------------------------------*/
|
||||
|
||||
#ifndef _FF_INTEGER
|
||||
#define _FF_INTEGER
|
||||
|
||||
#ifdef _WIN32 /* FatFs development platform */
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
|
||||
|
||||
#else /* Embedded platform */
|
||||
|
||||
/* These types MUST be 16-bit or 32-bit */
|
||||
typedef int INT;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
/* This type MUST be 8-bit */
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
/* These types MUST be 16-bit */
|
||||
typedef short SHORT;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned short WCHAR;
|
||||
|
||||
/* These types MUST be 32-bit */
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
388
source/fatfs/option/ccsbcs.c
Normal file
388
source/fatfs/option/ccsbcs.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample code of OS dependent controls for FatFs */
|
||||
/* (C)ChaN, 2014 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "../ff.h"
|
||||
|
||||
|
||||
#if _FS_REENTRANT
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object, such as semaphore and mutex. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */
|
||||
BYTE vol, /* Corresponding logical drive being processed */
|
||||
_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */
|
||||
ret = (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
// *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */
|
||||
// ret = 1; /* The initial value of the semaphore must be 1. */
|
||||
|
||||
// *sobj = OSMutexCreate(0, &err); /* uC/OS-II */
|
||||
// ret = (int)(err == OS_NO_ERR);
|
||||
|
||||
// *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */
|
||||
// ret = (int)(*sobj != NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* 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 ( /* !=0:Function succeeded, ==0:Could not delete due to any error */
|
||||
_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
ret = CloseHandle(sobj); /* Win32 */
|
||||
|
||||
// ret = 1; /* uITRON (nothing to do) */
|
||||
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */
|
||||
// ret = (int)(err == OS_NO_ERR);
|
||||
|
||||
// vSemaphoreDelete(sobj); /* FreeRTOS */
|
||||
// ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* 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 */
|
||||
_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */
|
||||
|
||||
// ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */
|
||||
|
||||
// OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */
|
||||
// ret = (int)(err == OS_NO_ERR);
|
||||
|
||||
// ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
ReleaseMutex(sobj); /* Win32 */
|
||||
|
||||
// sig_sem(sobj); /* uITRON */
|
||||
|
||||
// OSMutexPost(sobj); /* uC/OS-II */
|
||||
|
||||
// xSemaphoreGive(sobj); /* FreeRTOS */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if _USE_LFN == 3 /* LFN with a working buffer on the heap */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE.
|
||||
*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block */
|
||||
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); /* Discard the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define u8 uint8_t
|
||||
#define u16 uint16_t
|
||||
#define u32 uint32_t
|
||||
#define u64 uint64_t
|
||||
|
||||
#define vu8 volatile u8
|
||||
#define vu16 volatile u16
|
||||
#define vu32 volatile u32
|
||||
#define vu64 volatile u64
|
||||
#include "../../types.h"
|
||||
#include "../../crypto.h"
|
||||
@@ -1,15 +1,17 @@
|
||||
.arm
|
||||
.global waitcycles
|
||||
.type waitcycles STT_FUNC
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@waitcycles ( u32 us )
|
||||
waitcycles:
|
||||
PUSH {R0-R2,LR}
|
||||
STR R0, [SP,#4]
|
||||
waitcycles_loop:
|
||||
LDR R3, [SP,#4]
|
||||
SUBS R2, R3, #1
|
||||
STR R2, [SP,#4]
|
||||
CMP R3, #0
|
||||
BNE waitcycles_loop
|
||||
POP {R0-R2,PC}
|
||||
.arm
|
||||
.global ioDelay
|
||||
.type ioDelay STT_FUNC
|
||||
|
||||
@ioDelay ( u32 us )
|
||||
ioDelay:
|
||||
ldr r1, =0x18000000 @ VRAM
|
||||
1:
|
||||
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
||||
ldr r2, [r1]
|
||||
subs r0, #1
|
||||
bgt 1b
|
||||
bx lr
|
||||
|
||||
@@ -1,616 +1,429 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright (c) 2014, Normmatt
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms
|
||||
* of the GNU General Public License Version 2, as described below:
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file 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/.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "sdmmc.h"
|
||||
//#include "DrawCharacter.h"
|
||||
#include "delay.h"
|
||||
|
||||
//Uncomment to enable 32bit fifo support?
|
||||
//not currently working
|
||||
#define DATA32_SUPPORT
|
||||
struct mmcdevice handleNAND;
|
||||
struct mmcdevice handleSD;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
static inline u16 sdmmc_read16(u16 reg) {
|
||||
return *(vu16*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
#define bool int
|
||||
static inline void sdmmc_write16(u16 reg, u16 val) {
|
||||
*(vu16*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
#define NO_INLINE __attribute__ ((noinline))
|
||||
static inline u32 sdmmc_read32(u16 reg) {
|
||||
return *(vu32*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
#define RGB(r,g,b) (r<<24|b<<16|g<<8|r)
|
||||
static inline void sdmmc_write32(u16 reg, u32 val) {
|
||||
*(vu32*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void waitcycles(uint32_t val);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) {
|
||||
u16 val = sdmmc_read16(reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
sdmmc_write16(reg, val);
|
||||
}
|
||||
|
||||
//#define DEBUG_SDMMC
|
||||
static inline void setckl(u32 data)
|
||||
{
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SDMMC
|
||||
extern uint8_t* topScreen;
|
||||
extern void DrawHexWithName(unsigned char *screen, const char *str, unsigned int hex, int x, int y, int color, int bgcolor);
|
||||
#define DEBUGPRINT(scr,str,hex,x,y,color,bg) DrawHexWithName(scr,str,hex,x,y,color,bg)
|
||||
#else
|
||||
#define DEBUGPRINT(...)
|
||||
#endif
|
||||
|
||||
//extern "C" void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args);
|
||||
//extern "C" void inittarget(struct mmcdevice *ctx);
|
||||
//extern "C" int SD_Init();
|
||||
//extern "C" int SD_Init2();
|
||||
//extern "C" int Nand_Init2();
|
||||
//extern "C" void InitSD();
|
||||
|
||||
struct mmcdevice handelNAND;
|
||||
struct mmcdevice handelSD;
|
||||
|
||||
mmcdevice *getMMCDevice(int drive)
|
||||
{
|
||||
if(drive==0) return &handelNAND;
|
||||
return &handelSD;
|
||||
if(drive==0) return &handleNAND;
|
||||
return &handleSD;
|
||||
}
|
||||
|
||||
int geterror(struct mmcdevice *ctx)
|
||||
static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx)
|
||||
{
|
||||
return (ctx->error << 29) >> 31;
|
||||
return (ctx->error << 29) >> 31;
|
||||
}
|
||||
|
||||
|
||||
void inittarget(struct mmcdevice *ctx)
|
||||
static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx)
|
||||
{
|
||||
sdmmc_mask16(REG_SDPORTSEL,0x3,(uint16_t)ctx->devicenumber);
|
||||
setckl(ctx->clk);
|
||||
if(ctx->SDOPT == 0)
|
||||
{
|
||||
sdmmc_mask16(REG_SDOPT,0,0x8000);
|
||||
}
|
||||
else
|
||||
{
|
||||
sdmmc_mask16(REG_SDOPT,0x8000,0);
|
||||
}
|
||||
|
||||
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
|
||||
setckl(ctx->clk);
|
||||
if (ctx->SDOPT == 0) {
|
||||
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||
} else {
|
||||
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void NO_INLINE sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args)
|
||||
static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args)
|
||||
{
|
||||
bool getSDRESP = (cmd << 15) >> 31;
|
||||
uint16_t flags = (cmd << 15) >> 31;
|
||||
const bool readdata = cmd & 0x20000;
|
||||
const bool writedata = cmd & 0x40000;
|
||||
|
||||
if(readdata || writedata)
|
||||
{
|
||||
flags |= TMIO_STAT0_DATAEND;
|
||||
}
|
||||
|
||||
ctx->error = 0;
|
||||
while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0,0);
|
||||
sdmmc_write16(REG_SDIRMASK1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
#ifdef DATA32_SUPPORT
|
||||
// if(readdata)sdmmc_mask16(REG_DATACTL32, 0x1000, 0x800);
|
||||
// if(writedata)sdmmc_mask16(REG_DATACTL32, 0x800, 0x1000);
|
||||
// sdmmc_mask16(REG_DATACTL32,0x1800,2);
|
||||
#else
|
||||
sdmmc_mask16(REG_DATACTL32,0x1800,0);
|
||||
#endif
|
||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
||||
|
||||
uint32_t size = ctx->size;
|
||||
uint16_t *dataPtr = (uint16_t*)ctx->data;
|
||||
uint32_t *dataPtr32 = (uint32_t*)ctx->data;
|
||||
|
||||
bool useBuf = ( NULL != dataPtr );
|
||||
bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr))));
|
||||
|
||||
uint16_t status0 = 0;
|
||||
while(1)
|
||||
{
|
||||
volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
#ifdef DATA32_SUPPORT
|
||||
volatile uint16_t ctl32 = sdmmc_read16(REG_DATACTL32);
|
||||
if((ctl32 & 0x100))
|
||||
#else
|
||||
if((status1 & TMIO_STAT1_RXRDY))
|
||||
#endif
|
||||
{
|
||||
if(readdata)
|
||||
{
|
||||
if(useBuf)
|
||||
{
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
||||
if(size > 0x1FF)
|
||||
{
|
||||
#ifdef DATA32_SUPPORT
|
||||
if(useBuf32)
|
||||
{
|
||||
for(int i = 0; i<0x200; i+=4)
|
||||
{
|
||||
*dataPtr32++ = sdmmc_read32(REG_SDFIFO32);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
for(int i = 0; i<0x200; i+=2)
|
||||
{
|
||||
*dataPtr++ = sdmmc_read16(REG_SDFIFO);
|
||||
}
|
||||
#ifdef DATA32_SUPPORT
|
||||
}
|
||||
#endif
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_mask16(REG_DATACTL32, 0x800, 0);
|
||||
}
|
||||
}
|
||||
#ifdef DATA32_SUPPORT
|
||||
if(!(ctl32 & 0x200))
|
||||
#else
|
||||
if((status1 & TMIO_STAT1_TXRQ))
|
||||
#endif
|
||||
{
|
||||
if(writedata)
|
||||
{
|
||||
if(useBuf)
|
||||
{
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
|
||||
if(size > 0x1FF)
|
||||
{
|
||||
#ifdef DATA32_SUPPORT
|
||||
for(int i = 0; i<0x200; i+=4)
|
||||
{
|
||||
sdmmc_write32(REG_SDFIFO32,*dataPtr32++);
|
||||
}
|
||||
#else
|
||||
for(int i = 0; i<0x200; i+=2)
|
||||
{
|
||||
sdmmc_write16(REG_SDFIFO,*dataPtr++);
|
||||
}
|
||||
#endif
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_mask16(REG_DATACTL32, 0x1000, 0);
|
||||
}
|
||||
}
|
||||
if(status1 & TMIO_MASK_GW)
|
||||
{
|
||||
ctx->error |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!(status1 & TMIO_STAT1_CMD_BUSY))
|
||||
{
|
||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
{
|
||||
ctx->error |= 0x1;
|
||||
}
|
||||
if(status0 & TMIO_STAT0_DATAEND)
|
||||
{
|
||||
ctx->error |= 0x2;
|
||||
}
|
||||
|
||||
if((status0 & flags) == flags)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
|
||||
if(getSDRESP != 0)
|
||||
{
|
||||
ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16);
|
||||
ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16);
|
||||
ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16);
|
||||
ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16);
|
||||
}
|
||||
bool getSDRESP = (cmd << 15) >> 31;
|
||||
u16 flags = (cmd << 15) >> 31;
|
||||
const bool readdata = cmd & 0x20000;
|
||||
const bool writedata = cmd & 0x40000;
|
||||
|
||||
if (readdata || writedata)
|
||||
flags |= TMIO_STAT0_DATAEND;
|
||||
|
||||
ctx->error = 0;
|
||||
while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0,0);
|
||||
sdmmc_write16(REG_SDIRMASK1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
sdmmc_mask16(REG_SDDATACTL32,0x1800,0);
|
||||
|
||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
||||
|
||||
u32 size = ctx->size;
|
||||
vu8 *dataPtr = ctx->data;
|
||||
|
||||
bool useBuf = ( NULL != dataPtr );
|
||||
|
||||
u16 status0 = 0;
|
||||
while(true) {
|
||||
u16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
if (status1 & TMIO_STAT1_RXRDY) {
|
||||
if (readdata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
||||
if (size > 0x1FF) {
|
||||
for(int i = 0; i<0x200; i+=2) {
|
||||
u16 data = sdmmc_read16(REG_SDFIFO);
|
||||
*dataPtr++ = data & 0xFF;
|
||||
*dataPtr++ = data >> 8;
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status1 & TMIO_STAT1_TXRQ) {
|
||||
if (writedata && useBuf) {
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
|
||||
if (size > 0x1FF) {
|
||||
for (int i = 0; i<0x200; i+=2) {
|
||||
u16 data = *dataPtr++;
|
||||
data |= *dataPtr++ << 8;
|
||||
sdmmc_write16(REG_SDFIFO, data);
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (status1 & TMIO_MASK_GW) {
|
||||
ctx->error |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(status1 & TMIO_STAT1_CMD_BUSY)) {
|
||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
ctx->error |= 0x1;
|
||||
if (status0 & TMIO_STAT0_DATAEND)
|
||||
ctx->error |= 0x2;
|
||||
|
||||
if ((status0 & flags) == flags)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
|
||||
if (getSDRESP != 0) {
|
||||
ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16);
|
||||
ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16);
|
||||
ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16);
|
||||
ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
int NO_INLINE sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in)
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in)
|
||||
{
|
||||
if(handelSD.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handelSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
#ifdef DATA32_SUPPORT
|
||||
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32,0x200);
|
||||
#endif
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handelSD.data = in;
|
||||
handelSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handelSD,0x52C19,sector_no);
|
||||
return geterror(&handelSD);
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = in;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x52C19,sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
int NO_INLINE sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out)
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
{
|
||||
if(handelSD.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handelSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
#ifdef DATA32_SUPPORT
|
||||
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32,0x200);
|
||||
#endif
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handelSD.data = out;
|
||||
handelSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handelSD,0x33C12,sector_no);
|
||||
return geterror(&handelSD);
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = out;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x33C12,sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int NO_INLINE sdmmc_nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out)
|
||||
u32 __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
{
|
||||
if(handelNAND.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handelNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
#ifdef DATA32_SUPPORT
|
||||
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32,0x200);
|
||||
#endif
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handelNAND.data = out;
|
||||
handelNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handelNAND,0x33C12,sector_no);
|
||||
inittarget(&handelSD);
|
||||
return geterror(&handelNAND);
|
||||
if (handleNAND.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
inittarget(&handleNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
|
||||
handleNAND.data = out;
|
||||
handleNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleNAND,0x33C12,sector_no);
|
||||
inittarget(&handleSD);
|
||||
return geterror(&handleNAND);
|
||||
}
|
||||
|
||||
int NO_INLINE sdmmc_nand_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in) //experimental
|
||||
static u32 calcSDSize(u8* csd, int type)
|
||||
{
|
||||
if(handelNAND.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handelNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
#ifdef DATA32_SUPPORT
|
||||
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32,0x200);
|
||||
#endif
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handelNAND.data = in;
|
||||
handelNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handelNAND,0x52C19,sector_no);
|
||||
inittarget(&handelSD);
|
||||
return geterror(&handelNAND);
|
||||
u32 result = 0;
|
||||
if (type == -1) type = csd[14] >> 6;
|
||||
switch (type) {
|
||||
case 0:
|
||||
{
|
||||
u32 block_len = csd[9] & 0xf;
|
||||
block_len = 1u << block_len;
|
||||
u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1);
|
||||
mult = 1u << (mult + 2);
|
||||
result = csd[8] & 3;
|
||||
result = (result << 8) | csd[7];
|
||||
result = (result << 2) | (csd[6] >> 6);
|
||||
result = (result + 1) * mult * block_len / 512;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result = csd[7] & 0x3f;
|
||||
result = (result << 8) | csd[6];
|
||||
result = (result << 8) | csd[5];
|
||||
result = (result + 1) * 1024;
|
||||
break;
|
||||
default:
|
||||
break; //Do nothing otherwise
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t calcSDSize(uint8_t* csd, int type)
|
||||
static void InitSD()
|
||||
{
|
||||
uint32_t result=0;
|
||||
if(type == -1) type = csd[14] >> 6;
|
||||
switch(type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
uint32_t block_len=csd[9]&0xf;
|
||||
block_len=1<<block_len;
|
||||
uint32_t mult=(csd[4]>>7)|((csd[5]&3)<<1);
|
||||
mult=1<<(mult+2);
|
||||
result=csd[8]&3;
|
||||
result=(result<<8)|csd[7];
|
||||
result=(result<<2)|(csd[6]>>6);
|
||||
result=(result+1)*mult*block_len/512;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result=csd[7]&0x3f;
|
||||
result=(result<<8)|csd[6];
|
||||
result=(result<<8)|csd[5];
|
||||
result=(result+1)*1024;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
//NAND
|
||||
handleNAND.isSDHC = 0;
|
||||
handleNAND.SDOPT = 0;
|
||||
handleNAND.res = 0;
|
||||
handleNAND.initarg = 1;
|
||||
handleNAND.clk = 0x80;
|
||||
handleNAND.devicenumber = 1;
|
||||
|
||||
//SD
|
||||
handleSD.isSDHC = 0;
|
||||
handleSD.SDOPT = 0;
|
||||
handleSD.res = 0;
|
||||
handleSD.initarg = 0;
|
||||
handleSD.clk = 0x80;
|
||||
handleSD.devicenumber = 0;
|
||||
|
||||
*(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2;
|
||||
*(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL
|
||||
*(vu16*)0x10006104 = 0; //SDBLKLEN32
|
||||
*(vu16*)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(vu16*)0x100060E0 |= 1u; //SDRESET
|
||||
*(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
*(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20
|
||||
*(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(vu16*)0x10006026 = 512; //SDBLKLEN
|
||||
*(vu16*)0x10006008 = 0; //SDSTOP
|
||||
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
void InitSD()
|
||||
static int Nand_Init()
|
||||
{
|
||||
//NAND
|
||||
handelNAND.isSDHC = 0;
|
||||
handelNAND.SDOPT = 0;
|
||||
handelNAND.res = 0;
|
||||
handelNAND.initarg = 1;
|
||||
handelNAND.clk = 0x80;
|
||||
handelNAND.devicenumber = 1;
|
||||
|
||||
//SD
|
||||
handelSD.isSDHC = 0;
|
||||
handelSD.SDOPT = 0;
|
||||
handelSD.res = 0;
|
||||
handelSD.initarg = 0;
|
||||
handelSD.clk = 0x80;
|
||||
handelSD.devicenumber = 0;
|
||||
|
||||
//sdmmc_mask16(0x100,0x800,0);
|
||||
//sdmmc_mask16(0x100,0x1000,0);
|
||||
//sdmmc_mask16(0x100,0x0,0x402);
|
||||
//sdmmc_mask16(0xD8,0x22,0x2);
|
||||
//sdmmc_mask16(0x100,0x2,0);
|
||||
//sdmmc_mask16(0xD8,0x22,0);
|
||||
//sdmmc_write16(0x104,0);
|
||||
//sdmmc_write16(0x108,1);
|
||||
//sdmmc_mask16(REG_SDRESET,1,0); //not in new Version -- nintendo's code does this
|
||||
//sdmmc_mask16(REG_SDRESET,0,1); //not in new Version -- nintendo's code does this
|
||||
//sdmmc_mask16(0x20,0,0x31D);
|
||||
//sdmmc_mask16(0x22,0,0x837F);
|
||||
//sdmmc_mask16(0xFC,0,0xDB);
|
||||
//sdmmc_mask16(0xFE,0,0xDB);
|
||||
////sdmmc_write16(REG_SDCLKCTL,0x20);
|
||||
////sdmmc_write16(REG_SDOPT,0x40EE);
|
||||
////sdmmc_mask16(0x02,0x3,0);
|
||||
//sdmmc_write16(REG_SDCLKCTL,0x40);
|
||||
//sdmmc_write16(REG_SDOPT,0x40EB);
|
||||
//sdmmc_mask16(0x02,0x3,0);
|
||||
//sdmmc_write16(REG_SDBLKLEN,0x200);
|
||||
//sdmmc_write16(REG_SDSTOP,0);
|
||||
|
||||
*(volatile uint16_t*)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(volatile uint16_t*)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
#ifdef DATA32_SUPPORT
|
||||
*(volatile uint16_t*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
#else
|
||||
*(volatile uint16_t*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
#endif
|
||||
*(volatile uint16_t*)0x100060D8 = (*(volatile uint16_t*)0x100060D8 & 0xFFDD) | 2;
|
||||
#ifdef DATA32_SUPPORT
|
||||
*(volatile uint16_t*)0x10006100 &= 0xFFFFu; //SDDATACTL32
|
||||
*(volatile uint16_t*)0x100060D8 &= 0xFFDFu; //SDDATACTL
|
||||
*(volatile uint16_t*)0x10006104 = 512; //SDBLKLEN32
|
||||
#else
|
||||
*(volatile uint16_t*)0x10006100 &= 0xFFFDu; //SDDATACTL32
|
||||
*(volatile uint16_t*)0x100060D8 &= 0xFFDDu; //SDDATACTL
|
||||
*(volatile uint16_t*)0x10006104 = 0; //SDBLKLEN32
|
||||
#endif
|
||||
*(volatile uint16_t*)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(volatile uint16_t*)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(volatile uint16_t*)0x100060E0 |= 1u; //SDRESET
|
||||
*(volatile uint16_t*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(volatile uint16_t*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(volatile uint16_t*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(volatile uint16_t*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(volatile uint16_t*)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
#ifdef DATA32_SUPPORT
|
||||
*(volatile uint16_t*)0x10006024 = 0x20;
|
||||
*(volatile uint16_t*)0x10006028 = 0x40EE;
|
||||
#else
|
||||
*(volatile uint16_t*)0x10006024 = 0x40; //Nintendo sets this to 0x20
|
||||
*(volatile uint16_t*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE
|
||||
#endif
|
||||
*(volatile uint16_t*)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(volatile uint16_t*)0x10006026 = 512; //SDBLKLEN
|
||||
*(volatile uint16_t*)0x10006008 = 0; //SDSTOP
|
||||
|
||||
inittarget(&handelSD);
|
||||
inittarget(&handleNAND);
|
||||
ioDelay(0xF000);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0,0);
|
||||
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleNAND,0x10701,0x100000);
|
||||
} while ( !(handleNAND.error & 1) );
|
||||
} while((handleNAND.ret[0] & 0x80000000) == 0);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10602,0x0);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0);
|
||||
handleNAND.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.SDOPT = 1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B70100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B90100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10410,0x200);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
|
||||
handleNAND.clk |= 0x200;
|
||||
|
||||
inittarget(&handleSD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Nand_Init()
|
||||
static int SD_Init()
|
||||
{
|
||||
inittarget(&handelNAND);
|
||||
waitcycles(0xF000);
|
||||
|
||||
DEBUGPRINT(topScreen, "0x00000 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0,0);
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10701 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
sdmmc_send_command(&handelNAND,0x10701,0x100000);
|
||||
DEBUGPRINT(topScreen, "error ", handelNAND.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
DEBUGPRINT(topScreen, "ret: ", handelNAND.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
DEBUGPRINT(topScreen, "test ", 3, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
} while ( !(handelNAND.error & 1) );
|
||||
}
|
||||
while((handelNAND.ret[0] & 0x80000000) == 0);
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10602 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10602,0x0);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10403 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10403,handelNAND.initarg << 0x10);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10609 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10609,handelNAND.initarg << 0x10);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10407 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
handelNAND.total_size = calcSDSize((uint8_t*)&handelNAND.ret[0],0);
|
||||
handelNAND.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10407,handelNAND.initarg << 0x10);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10506 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
handelNAND.SDOPT = 1;
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10506,0x3B70100);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10506 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10506,0x3B90100);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x1040D ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x1040D,handelNAND.initarg << 0x10);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10410 ", handelNAND.error, 10, 20 + 13*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelNAND,0x10410,0x200);
|
||||
if((handelNAND.error & 0x4))return -1;
|
||||
|
||||
handelNAND.clk |= 0x200;
|
||||
|
||||
inittarget(&handelSD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
inittarget(&handleSD);
|
||||
|
||||
int SD_Init()
|
||||
{
|
||||
inittarget(&handelSD);
|
||||
//waitcycles(0x3E8);
|
||||
waitcycles(0xF000);
|
||||
DEBUGPRINT(topScreen, "0x00000 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
sdmmc_send_command(&handelSD,0,0);
|
||||
DEBUGPRINT(topScreen, "0x10408 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
sdmmc_send_command(&handelSD,0x10408,0x1AA);
|
||||
//uint32_t temp = (handelSD.ret[0] == 0x1AA) << 0x1E;
|
||||
uint32_t temp = (handelSD.error & 0x1) << 0x1E;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10769 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 15*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
DEBUGPRINT(topScreen, "temp: ", temp, 10, 20 + 16*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
//int count = 0;
|
||||
uint32_t temp2 = 0;
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handelSD,0x10769,0x00FF8000 | temp);
|
||||
temp2 = 1;
|
||||
} while ( !(handelSD.error & 1) );
|
||||
|
||||
//DEBUGPRINT(topScreen, "sd error ", handelSD.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
//DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
//DEBUGPRINT(topScreen, "count: ", count++, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
}
|
||||
while((handelSD.ret[0] & 0x80000000) == 0);
|
||||
//do
|
||||
//{
|
||||
// sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10);
|
||||
// sdmmc_send_command(&handelSD,0x10769,0x00FF8000 | temp);
|
||||
//
|
||||
// DEBUGPRINT(topScreen, "sd error ", handelSD.error, 10, 20 + 17*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
// DEBUGPRINT(topScreen, "sd ret: ", handelSD.ret[0], 10, 20 + 18*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
// DEBUGPRINT(topScreen, "count: ", count++, 10, 20 + 19*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
//}
|
||||
//while(!(handelSD.ret[0] & 0x80000000));
|
||||
ioDelay(1u << 18); //Card needs a little bit of time to be detected, it seems
|
||||
|
||||
//If not inserted
|
||||
if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0,0);
|
||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
||||
//u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E;
|
||||
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
||||
|
||||
if(!((handelSD.ret[0] >> 30) & 1) || !temp)
|
||||
temp2 = 0;
|
||||
|
||||
handelSD.isSDHC = temp2;
|
||||
//handelSD.isSDHC = (handelSD.ret[0] & 0x40000000);
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10602 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10602,0);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10403 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10403,0);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
handelSD.initarg = handelSD.ret[0] >> 0x10;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10609 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10609,handelSD.initarg << 0x10);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
|
||||
handelSD.total_size = calcSDSize((uint8_t*)&handelSD.ret[0],-1);
|
||||
handelSD.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10507 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10507,handelSD.initarg << 0x10);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
//int count = 0;
|
||||
u32 temp2 = 0;
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp);
|
||||
temp2 = 1;
|
||||
} while ( !(handleSD.error & 1) );
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10437 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10437,handelSD.initarg << 0x10);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10446 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
handelSD.SDOPT = 1;
|
||||
sdmmc_send_command(&handelSD,0x10446,0x2);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x1040D ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x1040D,handelSD.initarg << 0x10);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
|
||||
DEBUGPRINT(topScreen, "0x10410 ", handelSD.error, 10, 20 + 14*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
|
||||
sdmmc_send_command(&handelSD,0x10410,0x200);
|
||||
if((handelSD.error & 0x4)) return -1;
|
||||
handelSD.clk |= 0x200;
|
||||
|
||||
return 0;
|
||||
} while((handleSD.ret[0] & 0x80000000) == 0);
|
||||
|
||||
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
||||
temp2 = 0;
|
||||
|
||||
handleSD.isSDHC = temp2;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10602,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10403,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1);
|
||||
handleSD.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
handleSD.SDOPT = 1;
|
||||
sdmmc_send_command(&handleSD,0x10446,0x2);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10410,0x200);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
handleSD.clk |= 0x200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sdmmc_sdcard_init()
|
||||
{
|
||||
DEBUGPRINT(topScreen, "sdmmc_sdcard_init ", handelSD.error, 10, 20 + 2*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
InitSD();
|
||||
//SD_Init2();
|
||||
//Nand_Init();
|
||||
Nand_Init();
|
||||
DEBUGPRINT(topScreen, "nand_res ", nand_res, 10, 20 + 3*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
SD_Init();
|
||||
DEBUGPRINT(topScreen, "sd_res ", sd_res, 10, 20 + 4*8, RGB(40, 40, 40), RGB(208, 208, 208));
|
||||
InitSD();
|
||||
Nand_Init();
|
||||
SD_Init();
|
||||
}
|
||||
|
||||
int sdmmc_get_cid(int isNand, uint32_t *info)
|
||||
{
|
||||
struct mmcdevice *device;
|
||||
if(isNand)
|
||||
device = &handleNAND;
|
||||
else
|
||||
device = &handleSD;
|
||||
|
||||
inittarget(device);
|
||||
// use cmd7 to put sd card in standby mode
|
||||
// CMD7
|
||||
{
|
||||
sdmmc_send_command(device,0x10507,0);
|
||||
//if((device->error & 0x4)) return -1;
|
||||
}
|
||||
|
||||
// get sd card info
|
||||
// use cmd10 to read CID
|
||||
{
|
||||
sdmmc_send_command(device,0x1060A,device->initarg << 0x10);
|
||||
//if((device->error & 0x4)) return -2;
|
||||
|
||||
for( int i = 0; i < 4; ++i ) {
|
||||
info[i] = device->ret[i];
|
||||
}
|
||||
}
|
||||
|
||||
// put sd card back to transfer mode
|
||||
// CMD7
|
||||
{
|
||||
sdmmc_send_command(device,0x10507,device->initarg << 0x10);
|
||||
//if((device->error & 0x4)) return -3;
|
||||
}
|
||||
|
||||
if(isNand)
|
||||
{
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,52 +1,52 @@
|
||||
#ifndef __SDMMC_H__
|
||||
#define __SDMMC_H__
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common.h"
|
||||
|
||||
#define SDMMC_BASE 0x10006000
|
||||
#define SDMMC_BASE 0x10006000u
|
||||
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
#define REG_SDCMDARG 0x04
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
#define REG_SDCMDARG 0x04
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
#define REG_SDCLKCTL 0x24
|
||||
|
||||
#define REG_SDBLKLEN 0x26
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
#define REG_SDCLKCTL 0x24
|
||||
|
||||
#define REG_DATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
#define REG_SDBLKLEN 0x26
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
|
||||
#define REG_DATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
#define REG_SDDATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
#define REG_SDDATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
|
||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||
#define TMIO_STAT0_DATAEND 0x0004
|
||||
@@ -97,88 +97,33 @@
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
|
||||
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
|
||||
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef struct mmcdevice {
|
||||
vu8* data;
|
||||
u32 size;
|
||||
u32 error;
|
||||
u16 stat0;
|
||||
u16 stat1;
|
||||
u32 ret[4];
|
||||
u32 initarg;
|
||||
u32 isSDHC;
|
||||
u32 clk;
|
||||
u32 SDOPT;
|
||||
u32 devicenumber;
|
||||
u32 total_size; //size in sectors of the device
|
||||
u32 res;
|
||||
} mmcdevice;
|
||||
|
||||
typedef struct mmcdevice {
|
||||
uint8_t* data;
|
||||
uint32_t size;
|
||||
uint32_t error;
|
||||
uint16_t stat0;
|
||||
uint16_t stat1;
|
||||
uint32_t ret[4];
|
||||
uint32_t initarg;
|
||||
uint32_t isSDHC;
|
||||
uint32_t clk;
|
||||
uint32_t SDOPT;
|
||||
uint32_t devicenumber;
|
||||
uint32_t total_size; //size in sectors of the device
|
||||
uint32_t res;
|
||||
} mmcdevice;
|
||||
|
||||
void sdmmc_sdcard_init();
|
||||
int sdmmc_sdcard_readsector(uint32_t sector_no, uint8_t *out);
|
||||
int sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out);
|
||||
int sdmmc_sdcard_writesector(uint32_t sector_no, uint8_t *in);
|
||||
int sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in);
|
||||
|
||||
int sdmmc_nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out);
|
||||
int sdmmc_nand_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *in);
|
||||
|
||||
mmcdevice *getMMCDevice(int drive);
|
||||
|
||||
void InitSD();
|
||||
int Nand_Init();
|
||||
int SD_Init();
|
||||
mmcdevice *getMMCDevice(int drive);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
void sdmmc_sdcard_init();
|
||||
u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in);
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static inline uint16_t sdmmc_read16(uint16_t reg) {
|
||||
//---------------------------------------------------------------------------------
|
||||
return *(volatile uint16_t*)(SDMMC_BASE + reg);
|
||||
}
|
||||
u32 sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static inline void sdmmc_write16(uint16_t reg, uint16_t val) {
|
||||
//---------------------------------------------------------------------------------
|
||||
*(volatile uint16_t*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static inline uint32_t sdmmc_read32(uint16_t reg) {
|
||||
//---------------------------------------------------------------------------------
|
||||
return *(volatile uint32_t*)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static inline void sdmmc_write32(uint16_t reg, uint32_t val) {
|
||||
//---------------------------------------------------------------------------------
|
||||
*(volatile uint32_t*)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
static inline void sdmmc_mask16(uint16_t reg, const uint16_t clear, const uint16_t set) {
|
||||
//---------------------------------------------------------------------------------
|
||||
uint16_t val = sdmmc_read16(reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
sdmmc_write16(reg, val);
|
||||
}
|
||||
|
||||
static inline void setckl(uint32_t data)
|
||||
{
|
||||
sdmmc_mask16(REG_SDCLKCTL,0x100,0);
|
||||
sdmmc_mask16(REG_SDCLKCTL,0x2FF,data&0x2FF);
|
||||
sdmmc_mask16(REG_SDCLKCTL,0x0,0x100);
|
||||
}
|
||||
|
||||
#endif
|
||||
int sdmmc_get_cid( int isNand, uint32_t *info);
|
||||
492
source/firm.c
492
source/firm.c
@@ -1,164 +1,402 @@
|
||||
/*
|
||||
* firm.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
|
||||
#include "firm.h"
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "fs.h"
|
||||
#include "patches.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "emunand.h"
|
||||
#include "crypto.h"
|
||||
#include "draw.h"
|
||||
#include "screeninit.h"
|
||||
#include "loader.h"
|
||||
#include "buttons.h"
|
||||
#include "../build/patches.h"
|
||||
|
||||
firmHeader *firmLocation = (firmHeader *)0x24000000;
|
||||
firmSectionHeader *section;
|
||||
u32 firmSize = 0;
|
||||
u8 mode = 1,
|
||||
console = 1;
|
||||
u16 pressed;
|
||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||
static const firmSectionHeader *section;
|
||||
|
||||
//Load firm into FCRAM
|
||||
u8 loadFirm(void){
|
||||
u32 config,
|
||||
console,
|
||||
firmSource,
|
||||
emuOffset;
|
||||
|
||||
if(PDN_MPCORE_CFG == 1) console = 0;
|
||||
pressed = HID_PAD;
|
||||
section = firmLocation->section;
|
||||
void main(void)
|
||||
{
|
||||
u32 bootType,
|
||||
firmType,
|
||||
nandType,
|
||||
a9lhInstalled,
|
||||
updatedSys,
|
||||
needConfig,
|
||||
newConfig,
|
||||
emuHeader;
|
||||
|
||||
//If L and R are pressed, boot SysNAND with the NAND FIRM
|
||||
if((pressed & BUTTON_L1R1) == BUTTON_L1R1){
|
||||
mode = 0;
|
||||
//Read FIRM from NAND and write to FCRAM
|
||||
firmSize = console ? 0xF2C00 : 0xE9000;
|
||||
nandFirm0((u8*)firmLocation, firmSize, console);
|
||||
if(memcmp((u8*)firmLocation, "FIRM", 4) != 0) return 1;
|
||||
}
|
||||
//Load FIRM from SDCard
|
||||
else{
|
||||
const char firmPath[] = "/rei/firmware.bin";
|
||||
firmSize = fileSize(firmPath);
|
||||
if (!firmSize) return 1;
|
||||
fileRead((u8*)firmLocation, firmPath, firmSize);
|
||||
if((((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68)) return 1;
|
||||
//Detect the console being used
|
||||
console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
||||
|
||||
//Mount filesystems. CTRNAND will be mounted only if/when needed
|
||||
mountFs();
|
||||
|
||||
//Attempt to read the configuration file
|
||||
const char configPath[] = "/luma/config.bin";
|
||||
if(fileRead(&config, configPath, 4)) needConfig = 1;
|
||||
else
|
||||
{
|
||||
config = 0;
|
||||
needConfig = 2;
|
||||
}
|
||||
|
||||
if(console) arm9loader((u8*)firmLocation + section[2].offset, mode);
|
||||
//Determine if this is a firmlaunch boot
|
||||
if(*(vu8 *)0x23F00005)
|
||||
{
|
||||
if(needConfig == 2) mcuReboot();
|
||||
|
||||
return 0;
|
||||
bootType = 1;
|
||||
|
||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||
firmType = *(vu8 *)0x23F00005 - '0';
|
||||
|
||||
nandType = BOOTCONFIG(0, 3);
|
||||
firmSource = BOOTCONFIG(2, 1);
|
||||
a9lhInstalled = BOOTCONFIG(3, 1);
|
||||
updatedSys = (a9lhInstalled && CONFIG(1)) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bootType = 0;
|
||||
firmType = 0;
|
||||
|
||||
//Determine if booting with A9LH
|
||||
u32 a9lhBoot = !PDN_SPI_CNT ? 1 : 0;
|
||||
|
||||
//Retrieve the last booted FIRM
|
||||
u32 previousFirm = CFG_BOOTENV;
|
||||
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
//Determine if A9LH is installed and the user has an updated sysNAND
|
||||
if(a9lhBoot || CONFIG(2))
|
||||
{
|
||||
if(pressed == SAFE_MODE)
|
||||
error("Using Safe Mode would brick you, or remove A9LH!");
|
||||
|
||||
a9lhInstalled = 1;
|
||||
|
||||
//Check setting for > 9.2 sysNAND
|
||||
updatedSys = CONFIG(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
a9lhInstalled = 0;
|
||||
updatedSys = 0;
|
||||
}
|
||||
|
||||
newConfig = a9lhInstalled << 3;
|
||||
|
||||
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
||||
try to force boot options */
|
||||
if(a9lhBoot && previousFirm && needConfig == 1)
|
||||
{
|
||||
//Always force a sysNAND boot when quitting AGB_FIRM
|
||||
if(previousFirm == 7)
|
||||
{
|
||||
nandType = 0;
|
||||
firmSource = updatedSys ? 0 : BOOTCONFIG(2, 1);
|
||||
needConfig = 0;
|
||||
|
||||
//Flag to prevent multiple boot options-forcing
|
||||
newConfig |= 1 << 4;
|
||||
}
|
||||
/* Else, force the last used boot options unless a payload button or A/L/R are pressed
|
||||
or the no-forcing flag is set */
|
||||
else if(!(pressed & OVERRIDE_BUTTONS) && !BOOTCONFIG(4, 1))
|
||||
{
|
||||
nandType = BOOTCONFIG(0, 3);
|
||||
firmSource = BOOTCONFIG(2, 1);
|
||||
needConfig = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Boot options aren't being forced
|
||||
if(needConfig)
|
||||
{
|
||||
/* If L and R/Select or one of the single payload buttons are pressed and, if not using A9LH,
|
||||
the Safe Mode combo is not pressed, chainload an external payload */
|
||||
if(((pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)))
|
||||
&& pressed != SAFE_MODE)
|
||||
loadPayload();
|
||||
|
||||
//If no configuration file exists or SELECT is held, load configuration menu
|
||||
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
||||
configureCFW(configPath);
|
||||
|
||||
//If screens are inited or the corresponding option is set, load splash screen
|
||||
if(PDN_GPU_CNT != 1 || CONFIG(7)) loadSplash();
|
||||
|
||||
u32 autoBootSys = CONFIG(0);
|
||||
|
||||
//Determine if we need to boot an emuNAND or sysNAND
|
||||
nandType = (pressed & BUTTON_L1) ? autoBootSys : ((pressed & BUTTON_R1) ? updatedSys : !autoBootSys);
|
||||
|
||||
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
|
||||
or vice-versa, boot the second emuNAND */
|
||||
if(nandType && ((!(pressed & BUTTON_B)) == CONFIG(3))) nandType++;
|
||||
|
||||
//Determine the NAND we should take the FIRM from
|
||||
firmSource = (pressed & BUTTON_R1) ? !nandType : (nandType != 0);
|
||||
}
|
||||
}
|
||||
|
||||
//If we need to boot emuNAND, make sure it exists
|
||||
if(nandType)
|
||||
{
|
||||
getEmunandSect(&emuOffset, &emuHeader, &nandType);
|
||||
if(!nandType) firmSource = 0;
|
||||
}
|
||||
|
||||
//Same if we're using emuNAND as the FIRM source
|
||||
else if(firmSource)
|
||||
getEmunandSect(&emuOffset, &emuHeader, &firmSource);
|
||||
|
||||
if(!bootType)
|
||||
{
|
||||
newConfig |= nandType | (firmSource << 2);
|
||||
|
||||
/* If the boot configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if((newConfig & 0x2F) != (config & 0x3F))
|
||||
{
|
||||
//Preserve user settings (last 26 bits)
|
||||
newConfig |= config & 0xFFFFFFC0;
|
||||
|
||||
fileWrite(&newConfig, configPath, 4);
|
||||
}
|
||||
}
|
||||
|
||||
loadFirm(firmType, !firmType && updatedSys == !firmSource);
|
||||
|
||||
if(!firmType) patchNativeFirm(nandType, emuHeader, a9lhInstalled);
|
||||
else patchTwlAgbFirm(firmType);
|
||||
|
||||
launchFirm(bootType);
|
||||
}
|
||||
|
||||
//Nand redirection
|
||||
u8 loadEmu(void){
|
||||
static inline void loadFirm(u32 firmType, u32 externalFirm)
|
||||
{
|
||||
section = firm->section;
|
||||
|
||||
u32 emuOffset = 0,
|
||||
emuHeader = 0,
|
||||
emuRead = 0,
|
||||
emuWrite = 0,
|
||||
sdmmcOffset = 0,
|
||||
mpuOffset = 0,
|
||||
emuCodeOffset = 0;
|
||||
/* If the conditions to load the external FIRM aren't met, or reading fails, or the FIRM
|
||||
doesn't match the console, load it from CTRNAND */
|
||||
if(!externalFirm || !fileRead(firm, "/luma/firmware.bin", 0) ||
|
||||
(((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
||||
{
|
||||
const char *firmFolders[3][2] = {{ "00000002", "20000002" },
|
||||
{ "00000102", "20000102" },
|
||||
{ "00000202", "20000202" }};
|
||||
|
||||
//Read emunand code from SD
|
||||
getEmuCode(firmLocation, &emuCodeOffset, firmSize);
|
||||
const char path[] = "/rei/emunand/emunand.bin";
|
||||
u32 size = fileSize(path);
|
||||
if (!size) return 1;
|
||||
fileRead((u8*)emuCodeOffset, path, size);
|
||||
firmRead(firm, firmFolders[firmType][console]);
|
||||
decryptExeFs((u8 *)firm);
|
||||
}
|
||||
}
|
||||
|
||||
//Find and patch emunand related offsets
|
||||
u32 *pos_sdmmc = memsearch((u32*)emuCodeOffset, "SDMC", size, 4);
|
||||
u32 *pos_offset = memsearch((u32*)emuCodeOffset, "NAND", size, 4);
|
||||
u32 *pos_header = memsearch((u32*)emuCodeOffset, "NCSD", size, 4);
|
||||
getSDMMC(firmLocation, &sdmmcOffset, firmSize);
|
||||
getEmunandSect(&emuOffset, &emuHeader);
|
||||
getEmuRW(firmLocation, firmSize, &emuRead, &emuWrite);
|
||||
getMPU(firmLocation, &mpuOffset, firmSize);
|
||||
*pos_sdmmc = sdmmcOffset;
|
||||
static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhInstalled)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
|
||||
u32 nativeFirmType;
|
||||
|
||||
if(console)
|
||||
{
|
||||
//Determine if we're booting the 9.0 FIRM
|
||||
nativeFirmType = (arm9Section[0x51] == 0xFF) ? 0 : 1;
|
||||
|
||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
arm9Loader(arm9Section, nativeFirmType);
|
||||
firm->arm9Entry = (u8 *)0x801B01C;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Determine if we're booting the 9.0 FIRM
|
||||
u8 firm90Hash[0x10] = {0x27, 0x2D, 0xFE, 0xEB, 0xAF, 0x3F, 0x6B, 0x3B, 0xF5, 0xDE, 0x4C, 0x41, 0xDE, 0x95, 0x27, 0x6A};
|
||||
nativeFirmType = (memcmp(section[2].hash, firm90Hash, 0x10) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
if(nativeFirmType || nandType)
|
||||
{
|
||||
//Find the Process9 NCCH location
|
||||
u8 *proc9Offset = getProc9(arm9Section, section[2].size);
|
||||
|
||||
//Apply emuNAND patches
|
||||
if(nandType) patchEmuNAND(arm9Section, proc9Offset, emuHeader);
|
||||
|
||||
//Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
||||
if(nativeFirmType) patchReboots(arm9Section, proc9Offset);
|
||||
}
|
||||
|
||||
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||
if(a9lhInstalled && !nandType)
|
||||
{
|
||||
u16 *writeOffset = getFirmWrite(arm9Section, section[2].size);
|
||||
*writeOffset = writeBlock[0];
|
||||
*(writeOffset + 1) = writeBlock[1];
|
||||
}
|
||||
|
||||
//Apply signature checks patches
|
||||
u32 sigOffset,
|
||||
sigOffset2;
|
||||
|
||||
getSigChecks(arm9Section, section[2].size, &sigOffset, &sigOffset2);
|
||||
*(u16 *)sigOffset = sigPatch[0];
|
||||
*(u16 *)sigOffset2 = sigPatch[0];
|
||||
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
||||
|
||||
//Replace the FIRM loader with the injector
|
||||
injectLoader();
|
||||
}
|
||||
|
||||
static inline void patchEmuNAND(u8 *arm9Section, u8 *proc9Offset, u32 emuHeader)
|
||||
{
|
||||
//Copy emuNAND code
|
||||
void *emuCodeOffset = getEmuCode(proc9Offset);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
|
||||
//Add the data of the found emuNAND
|
||||
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4);
|
||||
u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
||||
*pos_offset = emuOffset;
|
||||
*pos_header = emuHeader;
|
||||
|
||||
//Add emunand hooks
|
||||
if(!console) nandRedir[5] = 0xA4;
|
||||
memcpy((u8*)emuRead, nandRedir, sizeof(nandRedir));
|
||||
memcpy((u8*)emuWrite, nandRedir, sizeof(nandRedir));
|
||||
//Find and add the SDMMC struct
|
||||
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
||||
*pos_sdmmc = getSDMMC(arm9Section, section[2].size);
|
||||
|
||||
//Calculate offset for the hooks
|
||||
u32 branchOffset = (u32)emuCodeOffset - (u32)firm -
|
||||
section[2].offset + (u32)section[2].address;
|
||||
|
||||
//Add emuNAND hooks
|
||||
u32 emuRead,
|
||||
emuWrite;
|
||||
|
||||
getEmuRW(arm9Section, section[2].size, &emuRead, &emuWrite);
|
||||
*(u16 *)emuRead = nandRedir[0];
|
||||
*((u16 *)emuRead + 1) = nandRedir[1];
|
||||
*((u32 *)emuRead + 1) = branchOffset;
|
||||
*(u16 *)emuWrite = nandRedir[0];
|
||||
*((u16 *)emuWrite + 1) = nandRedir[1];
|
||||
*((u32 *)emuWrite + 1) = branchOffset;
|
||||
|
||||
//Set MPU for emu code region
|
||||
memcpy((u8*)mpuOffset, mpu, sizeof(mpu));
|
||||
|
||||
return 0;
|
||||
u32 *mpuOffset = getMPU(arm9Section, section[2].size);
|
||||
*mpuOffset = mpuPatch[0];
|
||||
*(mpuOffset + 6) = mpuPatch[1];
|
||||
*(mpuOffset + 9) = mpuPatch[2];
|
||||
}
|
||||
|
||||
//Patches
|
||||
u8 patchFirm(void){
|
||||
static inline void patchReboots(u8 *arm9Section, u8 *proc9Offset)
|
||||
{
|
||||
//Calculate offset for the firmlaunch code
|
||||
void *rebootOffset = getReboot(arm9Section, section[2].size);
|
||||
|
||||
//If L is pressed, boot SysNAND with the SDCard FIRM
|
||||
if(mode && !(pressed & BUTTON_L1)) if (loadEmu()) return 1;
|
||||
//Calculate offset for the fOpen function
|
||||
u32 fOpenOffset = getfOpen(proc9Offset, rebootOffset);
|
||||
|
||||
//Disable signature checks
|
||||
u32 sigOffset = 0,
|
||||
sigOffset2 = 0;
|
||||
getSignatures(firmLocation, firmSize, &sigOffset, &sigOffset2);
|
||||
memcpy((u8*)sigOffset, sigPat1, sizeof(sigPat1));
|
||||
memcpy((u8*)sigOffset2, sigPat2, sizeof(sigPat2));
|
||||
//Copy firmlaunch code
|
||||
memcpy(rebootOffset, reboot, reboot_size);
|
||||
|
||||
//Apply reboot patch and write patched FIRM
|
||||
if(!console && mode &&
|
||||
((fileSize("/rei/reversereboot") > 0) == (pressed & BUTTON_A))){
|
||||
u32 rebootOffset = 0,
|
||||
rebootOffset2 = 0;
|
||||
getReboot(firmLocation, firmSize, &rebootOffset, &rebootOffset2);
|
||||
char path[] = "/rei/reboot/reboot1.bin";
|
||||
u32 size = fileSize(path);
|
||||
if (!size) return 1;
|
||||
fileRead((u8*)rebootOffset, path, size);
|
||||
memcpy((u8*)rebootOffset + size, L"sdmc:", 10);
|
||||
memcpy((u8*)rebootOffset + size + 10, L"" PATCHED_FIRM_PATH, sizeof(PATCHED_FIRM_PATH) * 2);
|
||||
path[18] = '2';
|
||||
size = fileSize(path);
|
||||
if (!size) return 1;
|
||||
fileRead((u8*)rebootOffset2, path, size);
|
||||
if (fileWrite((u8*)firmLocation, PATCHED_FIRM_PATH, firmSize) != 0) return 1;
|
||||
//Put the fOpen offset in the right location
|
||||
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
||||
*pos_fopen = fOpenOffset;
|
||||
}
|
||||
|
||||
static inline void injectLoader(void)
|
||||
{
|
||||
u32 loaderSize;
|
||||
|
||||
void *loaderOffset = getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderSize);
|
||||
|
||||
//Check that the injector CXI isn't larger than the original
|
||||
if((u32)injector_size <= loaderSize)
|
||||
{
|
||||
memcpy(loaderOffset, injector, injector_size);
|
||||
|
||||
//Patch content size and ExeFS size to match the repaced loader's ones
|
||||
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
||||
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void patchTwlAgbFirm(u32 firmType)
|
||||
{
|
||||
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
if(console)
|
||||
{
|
||||
arm9Loader((u8 *)firm + section[3].offset, 0);
|
||||
firm->arm9Entry = (u8 *)0x801301C;
|
||||
}
|
||||
|
||||
return 0;
|
||||
const patchData twlPatches[] = {
|
||||
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
||||
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
||||
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
||||
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
||||
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
||||
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
||||
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
||||
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
||||
},
|
||||
agbPatches[] = {
|
||||
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
||||
};
|
||||
|
||||
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
||||
if the matching option was enabled (keep it as last) */
|
||||
u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) :
|
||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6));
|
||||
const patchData *patches = firmType == 1 ? twlPatches : agbPatches;
|
||||
|
||||
//Patch
|
||||
for(u32 i = 0; i < numPatches; i++)
|
||||
{
|
||||
switch(patches[i].type)
|
||||
{
|
||||
case 0:
|
||||
memcpy((u8 *)firm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)((u8 *)firm + patches[i].offset[console] + 2) = 0;
|
||||
case 1:
|
||||
*(u16 *)((u8 *)firm + patches[i].offset[console]) = patches[i].patch.type1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Firmlaunchhax
|
||||
void launchFirm(void){
|
||||
static inline void launchFirm(u32 bootType)
|
||||
{
|
||||
//Copy FIRM sections to respective memory locations
|
||||
for(u32 i = 0; i < 4 && section[i].size; i++)
|
||||
memcpy(section[i].address, (u8 *)firm + section[i].offset, section[i].size);
|
||||
|
||||
//Set MPU
|
||||
__asm__ (
|
||||
"msr cpsr_c, #0xDF\n\t" //Set system mode, disable interrupts
|
||||
"ldr r0, =0x10000035\n\t" //Memory area 0x10000000-0x18000000, enabled, 128MB
|
||||
"ldr r4, =0x18000035\n\t" //Memory area 0x18000000-0x20000000, enabled, 128MB
|
||||
"mcr p15, 0, r0, c6, c3, 0\n\t" //Set memory area 3 (0x10000000-0x18000000)
|
||||
"mcr p15, 0, r4, c6, c4, 0\n\t" //Set memory area 4 (0x18000000-0x20000000)
|
||||
"mrc p15, 0, r0, c2, c0, 0\n\t" //read data cacheable bit
|
||||
"mrc p15, 0, r4, c2, c0, 1\n\t" //read inst cacheable bit
|
||||
"mrc p15, 0, r1, c3, c0, 0\n\t" //read data writeable
|
||||
"mrc p15, 0, r2, c5, c0, 2\n\t" //read data access permission
|
||||
"mrc p15, 0, r3, c5, c0, 3\n\t" //read inst access permission
|
||||
"orr r0, r0, #0x30\n\t"
|
||||
"orr r4, r4, #0x30\n\t"
|
||||
"orr r1, r1, #0x30\n\t"
|
||||
"bic r2, r2, #0xF0000\n\t"
|
||||
"bic r3, r3, #0xF0000\n\t"
|
||||
"orr r2, r2, #0x30000\n\t"
|
||||
"orr r3, r3, #0x30000\n\t"
|
||||
"mcr p15, 0, r0, c2, c0, 0\n\t" //write data cacheable bit
|
||||
"mcr p15, 0, r4, c2, c0, 1\n\t" //write inst cacheable bit
|
||||
"mcr p15, 0, r1, c3, c0, 0\n\t" //write data writeable
|
||||
"mcr p15, 0, r2, c5, c0, 2\n\t" //write data access permission
|
||||
"mcr p15, 0, r3, c5, c0, 3\n\t" //write inst access permission
|
||||
::: "r0", "r1", "r2", "r3", "r4"
|
||||
);
|
||||
|
||||
//Copy firm partitions to respective memory locations
|
||||
memcpy(section[0].address, (u8*)firmLocation + section[0].offset, section[0].size);
|
||||
memcpy(section[1].address, (u8*)firmLocation + section[1].offset, section[1].size);
|
||||
memcpy(section[2].address, (u8*)firmLocation + section[2].offset, section[2].size);
|
||||
*(u32 *)0x1FFFFFF8 = (u32)firmLocation->arm11Entry;
|
||||
|
||||
//Final jump to arm9 binary
|
||||
console ? ((void (*)())0x801B01C)() : ((void (*)())firmLocation->arm9Entry)();
|
||||
//Determine the ARM11 entry to use
|
||||
vu32 *arm11;
|
||||
if(bootType) arm11 = (u32 *)0x1FFFFFFC;
|
||||
else
|
||||
{
|
||||
deinitScreens();
|
||||
arm11 = (u32 *)0x1FFFFFF8;
|
||||
}
|
||||
|
||||
//Set ARM11 kernel entrypoint
|
||||
*arm11 = (u32)firm->arm11Entry;
|
||||
|
||||
//Final jump to ARM9 kernel
|
||||
((void (*)())firm->arm9Entry)();
|
||||
}
|
||||
@@ -1,23 +1,45 @@
|
||||
/*
|
||||
* firm.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
#ifndef FIRM_INC
|
||||
#define FIRM_INC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PDN_MPCORE_CFG (*(u8*)0x10140FFC)
|
||||
#define HID_PAD ((~*(u16*)0x10146000) & 0xFFF)
|
||||
#define BUTTON_L1R1 (3 << 8)
|
||||
#define BUTTON_L1 (1 << 9)
|
||||
#define BUTTON_A 1
|
||||
#define PATCHED_FIRM_PATH "/rei/patched_firmware.bin"
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
u8 loadFirm(void);
|
||||
u8 loadEmu(void);
|
||||
u8 patchFirm(void);
|
||||
void launchFirm(void);
|
||||
//FIRM Header layout
|
||||
typedef struct firmSectionHeader {
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} firmSectionHeader;
|
||||
|
||||
#endif
|
||||
typedef struct firmHeader {
|
||||
u32 magic;
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
firmSectionHeader section[4];
|
||||
} firmHeader;
|
||||
|
||||
typedef struct patchData {
|
||||
u32 offset[2];
|
||||
union {
|
||||
u8 type0[8];
|
||||
u16 type1;
|
||||
} patch;
|
||||
u32 type;
|
||||
} patchData;
|
||||
|
||||
static inline void loadFirm(u32 firmType, u32 externalFirm);
|
||||
static inline void patchNativeFirm(u32 emuNAND, u32 emuHeader, u32 a9lhSetup);
|
||||
static inline void patchEmuNAND(u8 *arm9Section, u8 *proc9Offset, u32 emuHeader);
|
||||
static inline void patchReboots(u8 *arm9Section, u8 *proc9Offset);
|
||||
static inline void injectLoader(void);
|
||||
static inline void patchTwlAgbFirm(u32 firmType);
|
||||
static inline void launchFirm(u32 bootType);
|
||||
144
source/font.h
Normal file
144
source/font.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
This file was autogenerated by raw2c.
|
||||
Visit http://www.devkitpro.org
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
#ifndef _font_h_
|
||||
#define _font_h_
|
||||
//---------------------------------------------------------------------------------
|
||||
static const unsigned char font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
|
||||
0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
|
||||
0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x3c, 0x3c, 0x18, 0xff, 0xe7, 0x18, 0x3c, 0x00,
|
||||
0x10, 0x38, 0x7c, 0xfe, 0xee, 0x10, 0x38, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
|
||||
0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
|
||||
0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
|
||||
0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x08, 0x0c, 0x0a, 0x0a, 0x08, 0x78, 0xf0, 0x00,
|
||||
0x18, 0x14, 0x1a, 0x16, 0x72, 0xe2, 0x0e, 0x1c, 0x10, 0x54, 0x38, 0xee, 0x38, 0x54, 0x10, 0x00,
|
||||
0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
|
||||
0x18, 0x3c, 0x5a, 0x18, 0x5a, 0x3c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
|
||||
0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x00, 0x1c, 0x22, 0x38, 0x44, 0x44, 0x38, 0x88, 0x70,
|
||||
0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, 0x5a, 0x18, 0x5a, 0x3c, 0x18, 0x7e,
|
||||
0x18, 0x3c, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x5a, 0x3c, 0x18, 0x00,
|
||||
0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
|
||||
0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x24, 0x42, 0xff, 0x42, 0x24, 0x00, 0x00,
|
||||
0x00, 0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00,
|
||||
0x6c, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
|
||||
0x10, 0x7c, 0xd0, 0x7c, 0x16, 0xfc, 0x10, 0x00, 0x00, 0x66, 0xac, 0xd8, 0x36, 0x6a, 0xcc, 0x00,
|
||||
0x38, 0x4c, 0x38, 0x78, 0xce, 0xcc, 0x7a, 0x00, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
|
||||
0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00,
|
||||
0x7c, 0xce, 0xde, 0xf6, 0xe6, 0xe6, 0x7c, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7e, 0x00,
|
||||
0x7c, 0xc6, 0x06, 0x1c, 0x70, 0xc6, 0xfe, 0x00, 0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, 0xfe, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
|
||||
0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,
|
||||
0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||
0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
|
||||
0x7c, 0x82, 0x9e, 0xa6, 0x9e, 0x80, 0x7c, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00,
|
||||
0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00,
|
||||
0xfc, 0x66, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
|
||||
0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xce, 0xc6, 0x7e, 0x00,
|
||||
0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
|
||||
0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x82, 0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
|
||||
0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06,
|
||||
0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xe6, 0x00, 0x7c, 0xc6, 0xc0, 0x7c, 0x06, 0xc6, 0x7c, 0x00,
|
||||
0x7e, 0x5a, 0x5a, 0x18, 0x18, 0x18, 0x3c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x82, 0x00,
|
||||
0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00,
|
||||
0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
|
||||
0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
|
||||
0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
|
||||
0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00,
|
||||
0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00,
|
||||
0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x78,
|
||||
0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0x00, 0x0c, 0x00, 0x1c, 0x0c, 0x0c, 0xcc, 0x78, 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
|
||||
0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xd6, 0xd6, 0x00,
|
||||
0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x7c, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
|
||||
0x00, 0x00, 0xde, 0x76, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0x7c, 0x00,
|
||||
0x10, 0x30, 0xfc, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00,
|
||||
0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
|
||||
0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, 0x0e, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0e, 0x00,
|
||||
0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xe0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xe0, 0x00,
|
||||
0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
|
||||
0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x70, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
|
||||
0x0e, 0x10, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0x7c, 0x82, 0x38, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
|
||||
0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0xe0, 0x10, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
|
||||
0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x7c, 0xc0, 0xc0, 0x7c, 0x18, 0x70,
|
||||
0x7c, 0x82, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00,
|
||||
0xe0, 0x10, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0x7c, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, 0xe0, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0xc6, 0x00, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x38, 0x38, 0x7c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
|
||||
0x0e, 0x10, 0xfe, 0x60, 0x78, 0x60, 0xfe, 0x00, 0x00, 0x00, 0x7c, 0x12, 0x7e, 0xd0, 0x7e, 0x00,
|
||||
0x7e, 0xc8, 0xc8, 0xfe, 0xc8, 0xc8, 0xce, 0x00, 0x7c, 0x82, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0xe0, 0x10, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0x7c, 0x82, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0xe0, 0x10, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
|
||||
0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x18, 0x7c, 0xd6, 0xd0, 0xd6, 0x7c, 0x18, 0x00,
|
||||
0x38, 0x6c, 0x60, 0xf0, 0x60, 0xf2, 0xdc, 0x00, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x00,
|
||||
0xf8, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0x06, 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
|
||||
0x0e, 0x10, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, 0x0e, 0x10, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0x0e, 0x10, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x0e, 0x10, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
|
||||
0x66, 0x98, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x98, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0x00,
|
||||
0x38, 0x0c, 0x3c, 0x34, 0x00, 0x7e, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
|
||||
0x30, 0x00, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, 0xc0, 0xc8, 0xd0, 0xfe, 0x46, 0x8c, 0x1e, 0x00,
|
||||
0xc0, 0xc8, 0xd0, 0xec, 0x5c, 0xbe, 0x0c, 0x00, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x18, 0x00,
|
||||
0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00,
|
||||
0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
|
||||
0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
|
||||
0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
|
||||
0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
|
||||
0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
|
||||
0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
|
||||
0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
|
||||
0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
|
||||
0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
|
||||
0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
|
||||
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x74, 0xcc, 0xc8, 0xdc, 0x76, 0x00, 0x78, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xdc, 0x40,
|
||||
0xfe, 0x62, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x02, 0x7e, 0xec, 0x6c, 0x6c, 0x48, 0x00,
|
||||
0xfe, 0x62, 0x30, 0x18, 0x30, 0x62, 0xfe, 0x00, 0x00, 0x00, 0x7e, 0xd0, 0xc8, 0xc8, 0x70, 0x00,
|
||||
0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xf8, 0x80, 0x00, 0x00, 0x7e, 0xd8, 0x18, 0x18, 0x10, 0x00,
|
||||
0x38, 0x10, 0x7c, 0xd6, 0xd6, 0x7c, 0x10, 0x38, 0x7c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x7c, 0x00,
|
||||
0x7c, 0xc6, 0xc6, 0xc6, 0x6c, 0x28, 0xee, 0x00, 0x3c, 0x22, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
|
||||
0x00, 0x00, 0x66, 0x99, 0x99, 0x66, 0x00, 0x00, 0x00, 0x06, 0x7c, 0x9e, 0xf2, 0x7c, 0xc0, 0x00,
|
||||
0x00, 0x00, 0x7c, 0xc0, 0xf8, 0xc0, 0x7c, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00,
|
||||
0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00,
|
||||
0x30, 0x18, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x00,
|
||||
0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
|
||||
0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
|
||||
0x38, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x00,
|
||||
0xd8, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0xc0, 0xf0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
|
||||
};
|
||||
const int font_size = sizeof(font);
|
||||
//---------------------------------------------------------------------------------
|
||||
#endif //_font_h_
|
||||
//---------------------------------------------------------------------------------
|
||||
137
source/fs.c
137
source/fs.c
@@ -2,85 +2,110 @@
|
||||
* fs.c
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "fatfs/ff.h"
|
||||
|
||||
static FATFS fs;
|
||||
static FATFS sdFs,
|
||||
nandFs;
|
||||
|
||||
int mountSD()
|
||||
u32 mountFs(void)
|
||||
{
|
||||
if (f_mount(&fs, "0:", 1) != FR_OK) {
|
||||
//printF("Failed to mount SD card!");
|
||||
return 1;
|
||||
}
|
||||
//printF("Mounted SD card");
|
||||
return 0;
|
||||
if(f_mount(&sdFs, "0:", 1) != FR_OK) return 0;
|
||||
f_mount(&nandFs, "1:", 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unmountSD()
|
||||
u32 fileRead(void *dest, const char *path, u32 size)
|
||||
{
|
||||
if (f_mount(NULL, "0:", 1) != FR_OK) {
|
||||
//printF("Failed to mount SD card!");
|
||||
return 1;
|
||||
}
|
||||
//printF("Unmounted SD card");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fileReadOffset(u8 *dest, const char *path, u32 size, u32 offset){
|
||||
FRESULT fr;
|
||||
FIL fp;
|
||||
unsigned int br = 0;
|
||||
|
||||
fr = f_open(&fp, path, FA_READ);
|
||||
if (fr != FR_OK)goto error;
|
||||
|
||||
if (!size) size = f_size(&fp);
|
||||
if (offset) {
|
||||
fr = f_lseek(&fp, offset);
|
||||
if (fr != FR_OK) goto error;
|
||||
if(fr == FR_OK)
|
||||
{
|
||||
if(!size) size = f_size(&fp);
|
||||
fr = f_read(&fp, dest, size, &br);
|
||||
}
|
||||
|
||||
fr = f_read(&fp, dest, size, &br);
|
||||
if (fr != FR_OK) goto error;
|
||||
|
||||
fr = f_close(&fp);
|
||||
if (fr != FR_OK) goto error;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
f_close(&fp);
|
||||
return fr;
|
||||
|
||||
return fr ? 0 : 1;
|
||||
}
|
||||
|
||||
int fileRead(u8 *dest, const char *path, u32 size){
|
||||
return fileReadOffset(dest, path, size, 0);
|
||||
}
|
||||
|
||||
int fileWrite(const u8 *buffer, const char *path, u32 size){
|
||||
u32 fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
FRESULT fr;
|
||||
FIL fp;
|
||||
unsigned int br = 0;
|
||||
|
||||
if(f_open(&fp, path, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK){
|
||||
fr = f_write(&fp, buffer, size, &br);
|
||||
f_close(&fp);
|
||||
if (fr == FR_OK && br == size) return 0;
|
||||
}
|
||||
return fr;
|
||||
fr = f_open(&fp, path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
if(fr == FR_OK) fr = f_write(&fp, buffer, size, &br);
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
return fr ? 0 : 1;
|
||||
}
|
||||
|
||||
int fileSize(const char* path){
|
||||
FRESULT fr;
|
||||
FIL fp;
|
||||
int size = 0;
|
||||
u32 defPayloadExists(void)
|
||||
{
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
|
||||
fr = f_open(&fp, path, FA_READ);
|
||||
if (fr != FR_OK)goto error;
|
||||
|
||||
size = f_size(&fp);
|
||||
error:
|
||||
f_close(&fp);
|
||||
return size;
|
||||
FRESULT result = f_findfirst(&dir, &info, "/luma/payloads", "def_*.bin");
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
return (result == FR_OK && info.fname[0]);
|
||||
}
|
||||
|
||||
void firmRead(void *dest, const char *firmFolder)
|
||||
{
|
||||
char path[48] = "1:/title/00040138/00000000/content";
|
||||
memcpy(&path[18], firmFolder, 8);
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
|
||||
f_opendir(&dir, path);
|
||||
|
||||
u32 id = 0xFFFFFFFF;
|
||||
|
||||
//Parse the target directory
|
||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0])
|
||||
{
|
||||
//Not a cxi
|
||||
if(info.altname[9] != 'A') continue;
|
||||
|
||||
//Convert the .app name to an integer
|
||||
u32 tempId = 0;
|
||||
for(char *tmp = info.altname; *tmp != '.'; tmp++)
|
||||
{
|
||||
tempId <<= 4;
|
||||
tempId += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
|
||||
}
|
||||
|
||||
//Found an older cxi
|
||||
if(tempId < id) id = tempId;
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
//Complete the string with the .app name
|
||||
memcpy(&path[34], "/00000000.app", 14);
|
||||
|
||||
//Last digit of the .app
|
||||
u32 i = 42;
|
||||
|
||||
//Convert back the .app name from integer to array
|
||||
while(id)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
path[i--] = hexDigits[id & 0xF];
|
||||
id >>= 4;
|
||||
}
|
||||
|
||||
fileRead(dest, path, 0);
|
||||
}
|
||||
16
source/fs.h
16
source/fs.h
@@ -2,16 +2,12 @@
|
||||
* fs.h
|
||||
*/
|
||||
|
||||
#ifndef __fs_h__
|
||||
#define __fs_h__
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
int mountSD();
|
||||
int unmountSD();
|
||||
int fileReadOffset(u8 *dest, const char *path, u32 size, u32 offset);
|
||||
int fileRead(u8 *dest, const char *path, u32 size);
|
||||
int fileWrite(const u8 *buffer, const char *path, u32 size);
|
||||
int fileSize(const char* path);
|
||||
|
||||
#endif
|
||||
u32 mountFs(void);
|
||||
u32 fileRead(void *dest, const char *path, u32 size);
|
||||
u32 fileWrite(const void *buffer, const char *path, u32 size);
|
||||
u32 defPayloadExists(void);
|
||||
void firmRead(void *dest, const char *firmFolder);
|
||||
114
source/i2c.c
Normal file
114
source/i2c.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "i2c.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
||||
{0, 0x4A}, {0, 0x7A}, {0, 0x78},
|
||||
{1, 0x4A}, {1, 0x78}, {1, 0x2C},
|
||||
{1, 0x2E}, {1, 0x40}, {1, 0x44},
|
||||
{2, 0xD6}, {2, 0xD0}, {2, 0xD2},
|
||||
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
||||
};
|
||||
|
||||
static inline u8 i2cGetDeviceBusId(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].bus_id;
|
||||
}
|
||||
|
||||
static inline u8 i2cGetDeviceRegAddr(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].reg_addr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_data_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_DATA),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetDataReg(u8 bus_id)
|
||||
{
|
||||
return reg_data_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_cnt_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_CNT),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetCntReg(u8 bus_id)
|
||||
{
|
||||
return reg_cnt_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline void i2cWaitBusy(u8 bus_id)
|
||||
{
|
||||
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||
}
|
||||
|
||||
static inline u32 i2cGetResult(u8 bus_id)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
|
||||
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
||||
}
|
||||
|
||||
static void i2cStop(u8 bus_id, u8 arg0)
|
||||
{
|
||||
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = dev_reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC2;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
static u32 i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC0;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = data;
|
||||
*i2cGetCntReg(bus_id) = 0xC1;
|
||||
i2cStop(bus_id, 0);
|
||||
|
||||
if(i2cGetResult(bus_id))
|
||||
return 1;
|
||||
}
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
i2cWaitBusy(bus_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
source/i2c.h
Normal file
18
source/i2c.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define I2C1_REG_OFF 0x10161000
|
||||
#define I2C2_REG_OFF 0x10144000
|
||||
#define I2C3_REG_OFF 0x10148000
|
||||
|
||||
#define I2C_REG_DATA 0
|
||||
#define I2C_REG_CNT 1
|
||||
#define I2C_REG_CNTEX 2
|
||||
#define I2C_REG_SCL 4
|
||||
|
||||
#define I2C_DEV_MCU 3
|
||||
#define I2C_DEV_GYRO 10
|
||||
#define I2C_DEV_IR 13
|
||||
|
||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
21
source/loader.c
Normal file
21
source/loader.c
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* loader.c
|
||||
*/
|
||||
|
||||
#include "loader.h"
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "screeninit.h"
|
||||
#include "../build/loader.h"
|
||||
|
||||
#define PAYLOAD_ADDRESS 0x24F00000
|
||||
|
||||
void loadPayload(void)
|
||||
{
|
||||
if(defPayloadExists())
|
||||
{
|
||||
initScreens();
|
||||
memcpy((void *)PAYLOAD_ADDRESS, loader, loader_size);
|
||||
((void (*)())PAYLOAD_ADDRESS)();
|
||||
}
|
||||
}
|
||||
9
source/loader.h
Normal file
9
source/loader.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* loader.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void loadPayload(void);
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* main.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*
|
||||
* Minimalist CFW for N3DS
|
||||
*/
|
||||
|
||||
#include "fs.h"
|
||||
#include "firm.h"
|
||||
#include "draw.h"
|
||||
|
||||
u8 main(){
|
||||
mountSD();
|
||||
loadSplash();
|
||||
if (loadFirm()) return 1;
|
||||
if (patchFirm()) return 1;
|
||||
launchFirm();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,42 +1,63 @@
|
||||
/*
|
||||
* memory.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
void memcpy32(u32 *dest, u32 *src, u32 size){
|
||||
for (u32 i = 0; i < size; i++) dest[i] = src[i];
|
||||
}
|
||||
void memcpy(void *dest, const void *src, u32 size)
|
||||
{
|
||||
u8 *destc = (u8 *)dest;
|
||||
const u8 *srcc = (const u8 *)src;
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size){
|
||||
char *destc = (char *)dest;
|
||||
const char *srcc = (const char *)src;
|
||||
u32 i; for (i = 0; i < size; i++) {
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
}
|
||||
|
||||
void memset(void *dest, int filler, u32 size){
|
||||
char *destc = (char *)dest;
|
||||
u32 i; for (i = 0; i < size; i++) {
|
||||
destc[i] = filler;
|
||||
}
|
||||
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 char *buf1c = (const char *)buf1;
|
||||
const char *buf2c = (const char *)buf2;
|
||||
u32 i; for (i = 0; i < size; i++) {
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1;
|
||||
const u8 *buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if (cmp) return cmp;
|
||||
if(cmp) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *memsearch(void *start_pos, void *search, u32 size, u32 size_search){
|
||||
for (void *pos = start_pos + size - size_search; pos >= start_pos; pos--) {
|
||||
if (memcmp(pos, search, size_search) == 0) return pos;
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
u32 table[256];
|
||||
|
||||
for(u32 i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(u32 i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
/*
|
||||
* memory.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
*/
|
||||
#ifndef MEM_INC
|
||||
#define MEM_INC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
void memcpy32(u32 *dest, u32 *src, u32 size);
|
||||
void memset(void *dest, int filler, u32 size);
|
||||
void memset32(void *dest, u32 filler, u32 size);
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||
void *memsearch(void *start_pos, void *search, u32 size, u32 size_search);
|
||||
|
||||
#endif
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
@@ -1,7 +1,5 @@
|
||||
/*
|
||||
* patches.c
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
|
||||
#include "patches.h"
|
||||
@@ -11,41 +9,65 @@
|
||||
* Patches
|
||||
**************************************************/
|
||||
|
||||
/*
|
||||
* MPU
|
||||
*/
|
||||
u8 mpu[0x2C] = { //MPU shit
|
||||
0x03, 0x00, 0x36, 0x00, 0x00, 0x00, 0x10, 0x10, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x36, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x01, 0x01, 0x01, 0x01, 0x03, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08,
|
||||
0x01, 0x01, 0x01, 0x01, 0x03, 0x06, 0x1C, 0x00, 0x00, 0x00, 0x02, 0x08
|
||||
};
|
||||
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
|
||||
|
||||
u8 nandRedir[0x08] = {0x00, 0x4C, 0xA0, 0x47, 0xC0, 0xA5, 0x01, 0x08}; //Branch to emunand function
|
||||
|
||||
/*
|
||||
* Sig checks
|
||||
*/
|
||||
u8 sigPat1[2] = {0x00, 0x20};
|
||||
u8 sigPat2[4] = {0x00, 0x20, 0x70, 0x47};
|
||||
const u16 nandRedir[2] = {0x4C00, 0x47A0},
|
||||
sigPatch[2] = {0x2000, 0x4770},
|
||||
writeBlock[2] = {0x2000, 0x46C0};
|
||||
|
||||
/**************************************************
|
||||
* Functions
|
||||
**************************************************/
|
||||
|
||||
void getSignatures(void *pos, u32 size, u32 *off, u32 *off2){
|
||||
//Look for signature checks
|
||||
unsigned char pattern[] = {0xC0, 0x1C, 0x76, 0xE7, 0x20};
|
||||
unsigned char pattern2[] = {0x70, 0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
*off = (u32)memsearch(pos, pattern, size, 5);
|
||||
*off2 = (u32)memsearch(pos, pattern2, size, 5);
|
||||
u8 *getProc9(u8 *pos, u32 size)
|
||||
{
|
||||
return memsearch(pos, "ess9", size, 4);
|
||||
}
|
||||
|
||||
void getReboot(void *pos, u32 size, u32 *off, u32 *off2){
|
||||
//Look for FIRM reboot code
|
||||
unsigned char pattern[] = {0x8D, 0xE5, 0x00, 0xC0, 0x91, 0xE5};
|
||||
unsigned char pattern2[] = {0xF0, 0x4F, 0x2D, 0xE9, 0x3C};
|
||||
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2)
|
||||
{
|
||||
//Look for signature checks
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
*off = (u32)memsearch(pos, pattern, size, 6) + 2;
|
||||
*off2 = (u32)memsearch(pos, pattern2, size, 5);
|
||||
*off = (u32)memsearch(pos, pattern, size, 4);
|
||||
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
||||
}
|
||||
|
||||
void *getReboot(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for FIRM reboot code
|
||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||
|
||||
return memsearch(pos, pattern, size, 4) - 0x10;
|
||||
}
|
||||
|
||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset)
|
||||
{
|
||||
//Offset Process9 code gets loaded to in memory (defined in ExHeader)
|
||||
u32 p9MemAddr = *(u32 *)(proc9Offset + 0xC);
|
||||
|
||||
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
|
||||
u32 p9CodeOff = (u32)(proc9Offset - 0x204) + (*(u32 *)(proc9Offset - 0x64) * 0x200) + 0x200;
|
||||
|
||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
||||
}
|
||||
|
||||
u16 *getFirmWrite(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for FIRM writing code
|
||||
u8 *const off = memsearch(pos, "exe:", size, 4);
|
||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||
|
||||
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
||||
}
|
||||
|
||||
void *getLoader(u8 *pos, u32 size, u32 *loaderSize)
|
||||
{
|
||||
u8 *const off = memsearch(pos, "loade", size, 5);
|
||||
|
||||
*loaderSize = *(u32 *)(off - 0xFC) * 0x200;
|
||||
|
||||
return off - 0x200;
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
/*
|
||||
* patches.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
#ifndef PATCHES_INC
|
||||
#define PATCHES_INC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**************************************************
|
||||
* Patches
|
||||
**************************************************/
|
||||
u8 mpu[0x2C];
|
||||
u8 nandRedir[0x08];
|
||||
u8 sigPat1[2];
|
||||
u8 sigPat2[4];
|
||||
const u32 mpuPatch[3];
|
||||
const u16 nandRedir[2],
|
||||
sigPatch[2],
|
||||
writeBlock[2];
|
||||
|
||||
/**************************************************
|
||||
* Functions
|
||||
**************************************************/
|
||||
void getSignatures(void *pos, u32 size, u32 *off, u32 *off2);
|
||||
void getReboot(void *pos, u32 size, u32 *off, u32 *off2);
|
||||
|
||||
#endif
|
||||
u8 *getProc9(u8 *pos, u32 size);
|
||||
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2);
|
||||
void *getReboot(u8 *pos, u32 size);
|
||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset);
|
||||
u16 *getFirmWrite(u8 *pos, u32 size);
|
||||
void *getLoader(u8 *pos, u32 size, u32 *loaderSize);
|
||||
65
source/screeninit.c
Normal file
65
source/screeninit.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* screeninit.c
|
||||
*
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others.
|
||||
* Screen deinit code by tiniVi.
|
||||
*/
|
||||
|
||||
#include "screeninit.h"
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "draw.h"
|
||||
#include "i2c.h"
|
||||
#include "../build/screeninit.h"
|
||||
|
||||
#define SCREENINIT_ADDRESS 0x24F03000
|
||||
|
||||
vu32 *arm11Entry = (u32 *)0x1FFFFFF8;
|
||||
|
||||
void deinitScreens(void)
|
||||
{
|
||||
void __attribute__((naked)) ARM11(void)
|
||||
{
|
||||
//Disable interrupts
|
||||
__asm(".word 0xF10C01C0");
|
||||
|
||||
//Clear ARM11 entry offset
|
||||
*arm11Entry = 0;
|
||||
|
||||
//Shutdown LCDs
|
||||
*(vu32 *)0x10202A44 = 0;
|
||||
*(vu32 *)0x10202244 = 0;
|
||||
*(vu32 *)0x10202014 = 0;
|
||||
|
||||
//Wait for the entry to be set
|
||||
while(!*arm11Entry);
|
||||
|
||||
//Jump to it
|
||||
((void (*)())*arm11Entry)();
|
||||
}
|
||||
|
||||
if(PDN_GPU_CNT != 1)
|
||||
{
|
||||
*arm11Entry = (u32)ARM11;
|
||||
while(*arm11Entry);
|
||||
}
|
||||
}
|
||||
|
||||
void initScreens(void)
|
||||
{
|
||||
if(PDN_GPU_CNT == 1)
|
||||
{
|
||||
memcpy((void *)SCREENINIT_ADDRESS, screeninit, screeninit_size);
|
||||
|
||||
//Write brightness level for the stub to pick up
|
||||
*(vu32 *)(SCREENINIT_ADDRESS + 8) = MULTICONFIG(0);
|
||||
|
||||
*arm11Entry = SCREENINIT_ADDRESS;
|
||||
while(*arm11Entry);
|
||||
|
||||
//Turn on backlight
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
||||
}
|
||||
|
||||
clearScreens();
|
||||
}
|
||||
15
source/screeninit.h
Normal file
15
source/screeninit.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* screeninit.h
|
||||
*
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others.
|
||||
* Screen deinit code by tiniVi.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
void deinitScreens(void);
|
||||
void initScreens(void);
|
||||
@@ -2,47 +2,57 @@
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
b start
|
||||
|
||||
.word 0
|
||||
|
||||
start:
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
|
||||
@ Sets MPU permissions and cache settings
|
||||
ldr r0, =0xFFFF001D @ ffff0000 32k
|
||||
ldr r1, =0x01FF801D @ 01ff8000 32k
|
||||
ldr r2, =0x08000027 @ 08000000 1M
|
||||
ldr r3, =0x10000021 @ 10000000 128k
|
||||
ldr r4, =0x10100025 @ 10100000 512k
|
||||
ldr r5, =0x20000035 @ 20000000 128M
|
||||
ldr r6, =0x2800801B @ 28008000 16k
|
||||
ldr r7, =0x1800002D @ 18000000 8M
|
||||
ldr r8, =0x33333336
|
||||
ldr r9, =0x60600666
|
||||
mov r10, #0x25
|
||||
mov r11, #0x25
|
||||
mov r12, #0x25
|
||||
mcr p15, 0, r0, c6, c0, 0
|
||||
mcr p15, 0, r1, c6, c1, 0
|
||||
mcr p15, 0, r2, c6, c2, 0
|
||||
mcr p15, 0, r3, c6, c3, 0
|
||||
mcr p15, 0, r4, c6, c4, 0
|
||||
mcr p15, 0, r5, c6, c5, 0
|
||||
mcr p15, 0, r6, c6, c6, 0
|
||||
mcr p15, 0, r7, c6, c7, 0
|
||||
mcr p15, 0, r8, c5, c0, 2 @ Enable data r/w for all regions
|
||||
mcr p15, 0, r9, c5, c0, 3 @ Enable inst read for 0, 1, 2, 5, 7
|
||||
mcr p15, 0, r10, c3, c0, 0 @ Write bufferable 0, 2, 5
|
||||
mcr p15, 0, r11, c2, c0, 0 @ Data cacheable 0, 2, 5
|
||||
mcr p15, 0, r12, c2, c0, 1 @ Inst cacheable 0, 2, 5
|
||||
@ Give read/write access to all the memory regions
|
||||
ldr r0, =0x33333333
|
||||
mcr p15, 0, r0, c5, c0, 2 @ write data access
|
||||
mcr p15, 0, r0, c5, c0, 3 @ write instruction access
|
||||
|
||||
@ Enables all the settings we specified above
|
||||
ldr r0, =0x5307D
|
||||
mcr p15, 0, r0, c1, c0, 0 @ cp15 ctl register enable mpu, enable cache and use alt vector table
|
||||
@ Set MPU permissions and cache settings
|
||||
ldr r0, =0xFFFF001D @ ffff0000 32k
|
||||
ldr r1, =0x01FF801D @ 01ff8000 32k
|
||||
ldr r2, =0x08000027 @ 08000000 1M
|
||||
ldr r3, =0x10000021 @ 10000000 128k
|
||||
ldr r4, =0x10100025 @ 10100000 512k
|
||||
ldr r5, =0x20000035 @ 20000000 128M
|
||||
ldr r6, =0x1FF00027 @ 1FF00000 1M
|
||||
ldr r7, =0x1800002D @ 18000000 8M
|
||||
mcr p15, 0, r0, c6, c0, 0
|
||||
mcr p15, 0, r1, c6, c1, 0
|
||||
mcr p15, 0, r2, c6, c2, 0
|
||||
mcr p15, 0, r3, c6, c3, 0
|
||||
mcr p15, 0, r4, c6, c4, 0
|
||||
mcr p15, 0, r5, c6, c5, 0
|
||||
mcr p15, 0, r6, c6, c6, 0
|
||||
mcr p15, 0, r7, c6, c7, 0
|
||||
mov r0, #0x25
|
||||
mcr p15, 0, r0, c3, c0, 0 @ Write bufferable 0, 2, 5
|
||||
mcr p15, 0, r0, c2, c0, 0 @ Data cacheable 0, 2, 5
|
||||
mcr p15, 0, r0, c2, c0, 1 @ Inst cacheable 0, 2, 5
|
||||
|
||||
@ Undocumented: Fixes mounting of SDMC
|
||||
ldr r0, =0x10000020
|
||||
mov r1, #0x340
|
||||
str r1, [r0]
|
||||
@ Enable caches
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||
orr r0, r0, #(1<<12) @ - instruction cache enable
|
||||
orr r0, r0, #(1<<2) @ - data cache enable
|
||||
orr r0, r0, #(1<<0) @ - mpu enable
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
||||
|
||||
bl main
|
||||
@ Flush caches
|
||||
mov r0, #0
|
||||
mcr p15, 0, r0, c7, c5, 0 @ flush I-cache
|
||||
mcr p15, 0, r0, c7, c6, 0 @ flush D-cache
|
||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||
|
||||
.die:
|
||||
b .die
|
||||
@ Fix mounting of SDMC
|
||||
ldr r0, =0x10000020
|
||||
mov r1, #0x340
|
||||
str r1, [r0]
|
||||
|
||||
b main
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
/*
|
||||
* types.h
|
||||
* by Reisyukaku
|
||||
* Copyright (c) 2015 All Rights Reserved
|
||||
*/
|
||||
#ifndef TYPES_INC
|
||||
#define TYPES_INC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -14,23 +12,7 @@ typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
//FIRM Header layout
|
||||
typedef struct firmSectionHeader {
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} firmSectionHeader;
|
||||
|
||||
typedef struct firmHeader {
|
||||
u32 magic;
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
firmSectionHeader section[4];
|
||||
} firmHeader;
|
||||
|
||||
#endif
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
61
source/utils.c
Normal file
61
source/utils.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* utils.c
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "screeninit.h"
|
||||
#include "draw.h"
|
||||
#include "i2c.h"
|
||||
#include "buttons.h"
|
||||
|
||||
u32 waitInput(void)
|
||||
{
|
||||
u32 pressedKey = 0,
|
||||
key;
|
||||
|
||||
//Wait for no keys to be pressed
|
||||
while(HID_PAD);
|
||||
|
||||
do
|
||||
{
|
||||
//Wait for a key to be pressed
|
||||
while(!HID_PAD);
|
||||
|
||||
key = HID_PAD;
|
||||
|
||||
//Make sure it's pressed
|
||||
for(u32 i = 0x13000; i; i--)
|
||||
{
|
||||
if(key != HID_PAD) break;
|
||||
if(i == 1) pressedKey = 1;
|
||||
}
|
||||
}
|
||||
while(!pressedKey);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void mcuShutDown(void)
|
||||
{
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1);
|
||||
while(1);
|
||||
}
|
||||
|
||||
void mcuReboot(void)
|
||||
{
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2);
|
||||
while(1);
|
||||
}
|
||||
|
||||
void error(const char *message)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
||||
int posY = drawString(message, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
waitInput();
|
||||
|
||||
mcuShutDown();
|
||||
}
|
||||
12
source/utils.h
Normal file
12
source/utils.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* utils.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
u32 waitInput(void);
|
||||
void mcuShutDown(void);
|
||||
void mcuReboot(void);
|
||||
void error(const char *message);
|
||||
Reference in New Issue
Block a user