commit e2b7f1e60709cc64692f8c9fee1c9f9035153160 Author: Reisyukaku Date: Tue Aug 4 21:57:37 2015 -0400 init commit take 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b99aa1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build.bat +data/firmware.bin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..843b2cb --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +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 + +dir_source := source +dir_data := data +dir_build := build +dir_mset := mset +dir_out := out +dir_emu := emunand +dir_thread := thread + +ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te +CFLAGS := -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main +FLAGS := dir_out=$(abspath $(dir_out)) + +objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + + +.PHONY: all +all: launcher + +.PHONY: launcher +launcher: $(dir_out)/ReiNand.dat + +.PHONY: clean +clean: + @$(MAKE) $(FLAGS) -C $(dir_mset) clean + rm -rf $(dir_out) $(dir_build) + +.PHONY: $(dir_out)/ReiNand.dat +$(dir_out)/ReiNand.dat: $(dir_build)/main.bin $(dir_out)/rei/ $(dir_out)/rei/thread/arm9.bin $(dir_out)/rei/emunand/emunand.bin + @$(MAKE) $(FLAGS) -C $(dir_mset) launcher + dd if=$(dir_build)/main.bin of=$@ bs=512 seek=256 + +$(dir_out)/rei/: $(dir_data)/firmware.bin $(dir_data)/splash.bin + @mkdir -p "$(dir_out)/rei" + @cp -av $(dir_data)/*bin $@ + +$(dir_out)/rei/thread/arm9.bin: $(dir_thread) + @$(MAKE) -C $(dir_thread) + @mkdir -p "$(dir_out)/rei/thread" + @mv $(dir_thread)/arm9.bin $(dir_out)/rei/thread + +$(dir_out)/rei/emunand/emunand.bin: $(dir_emu)/emuCode.s + @armips $< + @mkdir -p "$(dir_out)/rei/emunand" + @mv emunand.bin $(dir_out)/rei/emunand + +$(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)/%.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) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $< + +$(dir_build)/fatfs/%.o: $(dir_source)/fatfs/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $< +include $(call rwildcard, $(dir_build), *.d) diff --git a/data/splash.bin b/data/splash.bin new file mode 100644 index 0000000..c822561 Binary files /dev/null and b/data/splash.bin differ diff --git a/emunand/emuCode.s b/emunand/emuCode.s new file mode 100644 index 0000000..36588b4 --- /dev/null +++ b/emunand/emuCode.s @@ -0,0 +1,155 @@ +.nds + +sdmmc equ 0x80D86F0 +sdmmc_unk1 equ 0x080788C0 +aes_unk equ 0x0805F9E4 +aes_setkey equ 0x08057458 +sdmmc_unk2 equ 0x080786A0 +sdmmc_unk0 equ 0x08062890 + +.create "emunand.bin", 0x0801A4C0 +.org 0x0801A4C0 +.arm +patch000_00: + stmfd sp!, {r0-r3} + mov r3, r0 + ldr r1, =orig_sector + ldr r2, [r3,#4] + str r2, [r1,#4] + ldr r0, =sdmmc + cmp r2, r0 + ldr r2, [r3,#8] + str r2, [r1] + beq @@orig_code + ldr r1, =sdmmc + str r1, [r3,#4] + cmp r2, #0 + ldr r0, =nand_offset + ldrne r0, [r0] + addne r0, r2 + ldreq r0, [r0, #(ncsd_header_offset - nand_offset)] + str r0, [r3,#8] +@@orig_code: + ldmfd sp!, {r0-r3} + movs r4, r0 + movs r5, r1 + movs r7, r2 + movs r6, r3 + movs r0, r1, lsl#23 + beq loc_801a534 + stmfd sp!, {r4} + ldr r4, =(sdmmc_unk0 + 1) + blx r4 + ldmfd sp!, {r4} +loc_801a534: + ldr r0, [r4,#4] + ldr r1, [r0] + ldr r1, [r1,#0x18] + blx r1 + ldr r1, [r4,#4] + movs r3, r0 + ldr r0, [r1,#0x20] + movs r2, r5, lsr#9 + mov r12, r0 + ldr r0, [r4,#8] + str r7, [sp,#4] + adds r0, r0, r2 + cmp r1, #0 + str r6, [sp,#8] + str r0, [sp] + beq loc_801a578 + adds r1, r1, #8 +loc_801a578: + movs r2, r4 + adds r2, r2, #0xc + mov r0, r12 + ldr r5, =(sdmmc_unk1 + 1) ; called by the original function + blx r5 + stmfd sp!, {r0-r3} + ldr r2, =orig_sector + ldr r1, [r2] + str r1, [r4,#8] + ldr r1, [r2,#4] + str r1, [r4,#4] + ldmfd sp!, {r0-r3} + ldmfd sp!, {r1-r7,lr} + bx lr + +patch000_01: + stmfd sp!, {r0-r3} + mov r3, r0 + ldr r1, =orig_sector + ldr r2, [r3,#4] + str r2, [r1,#4] + ldr r0, =sdmmc + cmp r2, r0 + ldr r2, [r3,#8] + str r2, [r1] + beq @@orig_code + ldr r1, =sdmmc + str r1, [r3,#4] + cmp r2, #0 + ldr r0, =nand_offset + ldrne r0, [r0] + addne r0, r2 + ldreq r0, [r0, #(ncsd_header_offset - nand_offset)] + str r0, [r3,#8] +@@orig_code: + ldmfd sp!, {r0-r3} + movs r4, r0 + movs r5, r1 + movs r7, r2 + movs r6, r3 + movs r0, r1, lsl#23 + beq loc_801a624 + stmfd sp!, {r4} + ldr r4, =(sdmmc_unk0 + 1) + blx r4 + ldmfd sp!, {r4} +loc_801a624: + ldr r0, [r4,#4] + ldr r1, [r0] + ldr r1, [r1,#0x18] + blx r1 + ldr r1, [r4,#4] + movs r3, r0 + ldr r0, [r1,#0x20] + movs r2, r5, lsr#9 + mov r12, r0 + ldr r0, [r4,#8] + str r7, [sp,#4] + adds r0, r0, r2 + cmp r1, #0 + str r6, [sp,#8] + str r0, [sp] + beq loc_801a668 + adds r1, r1, #8 +loc_801a668: + movs r2, r4 + adds r2, r2, #0xC + mov r0, r12 + ldr r5, =(sdmmc_unk2 + 1) + blx r5 + stmfd sp!, {r0-r3} + ldr r2, =orig_sector + ldr r1, [r2] + str r1, [r4,#8] + ldr r1, [r2,#4] + str r1, [r4,#4] + ldmfd sp!, {r0-r3} + ldmfd sp!, {r1-r7,lr} + bx lr + +.pool +orig_sector: .word 0x00000000 +orig_ptr: .word 0x00000000 +nand_offset: .ascii "NAND" ; for rednand this should be 1 +ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED) +;ncsd_header_offset: .word 0x1D7800 +;ncsd_header_offset: .word 0x1DD000 +slot0x25keyX: + .word 0xABD8E7CE + .word 0xAE0DC030 + .word 0xE3F50E85 + .word 0xF35AAC82 +.close diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..d57403d --- /dev/null +++ b/linker.ld @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x23F00000; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} diff --git a/mset/LICENSE.txt b/mset/LICENSE.txt new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/mset/LICENSE.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/mset/Makefile b/mset/Makefile new file mode 100644 index 0000000..dbffff0 --- /dev/null +++ b/mset/Makefile @@ -0,0 +1,118 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +# This should be set externally +name ?= reiNand.dat +path ?= +dir_out ?= . + +CC := arm-none-eabi-gcc +AS := arm-none-eabi-as +LD := arm-none-eabi-ld +OC := arm-none-eabi-objcopy + +dir_source := source +dir_build := build + +ARM9FLAGS := -mcpu=arm946e-s -march=armv5te +ARM11FLAGS := -mcpu=mpcore +ASFLAGS := -mlittle-endian +CFLAGS := -marm $(ASFLAGS) -O2 -std=c11 -MMD -MP -fno-builtin -fshort-wchar -Wall -Wextra -Wno-main -DLAUNCHER_PATH='"$(path)$(name)"' + +get_objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, $1)) + +objects := $(call get_objects, $(wildcard $(dir_source)/*.s $(dir_source)/*.c)) + +objects_payload := $(call get_objects, \ + $(call rwildcard, $(dir_source)/payload, *.s *.c)) + +versions := mset_4x mset_4x_dg mset_6x + +rops := $(foreach ver, $(versions), $(dir_build)/$(ver)/rop.dat) + +.SECONDARY: + +.PHONY: all +all: launcher + +.PHONY: launcher +launcher: $(dir_out)/$(name) + +.PHONY: bigpayload +bigpayload: $(dir_build)/bigpayload.built + +.PHONY: clean +clean: + rm -rf $(dir_out)/$(name) $(dir_build) + +# Big payload +$(dir_build)/bigpayload.built: $(dir_out)/$(name) $(dir_build)/payload/main.bin + dd if=$(dir_build)/payload/main.bin of=$(dir_out)/$(name) bs=512 seek=144 + @touch $@ + +# Throw everything together +$(dir_out)/$(name): $(rops) $(dir_build)/mset/main.bin $(dir_build)/spider/main.bin + touch $@ + dd if=$(dir_build)/spider/main.bin of=$@ bs=512 seek=0 + dd if=$(dir_build)/mset_4x/rop.dat of=$@ bs=512 seek=32 + dd if=$(dir_build)/mset_4x_dg/rop.dat of=$@ bs=512 seek=34 + dd if=$(dir_build)/mset_6x/rop.dat of=$@ bs=512 seek=40 + dd if=$(dir_build)/mset/main.bin of=$@ bs=512 seek=64 + +# MSET ROPs +$(dir_build)/mset_%/rop.dat: rop_param = MSET_$(shell echo $* | tr a-z A-Z) +$(dir_build)/mset_%/rop.dat: + @make -C rop3ds LoadCodeMset.dat ASFLAGS="-D$(rop_param) -DARM_CODE_OFFSET=0x8000" + @mkdir -p "$(@D)" + @mv rop3ds/LoadCodeMset.dat $@ + +# Create bin from elf +$(dir_build)/%/main.bin: $(dir_build)/%/main.elf + $(OC) -S -O binary $< $@ + +# Different flags for different things +$(dir_build)/payload/main.elf: ASFLAGS := $(ARM9FLAGS) $(ASFLAGS) +$(dir_build)/payload/main.elf: CFLAGS := $(ARM9FLAGS) $(CFLAGS) +$(dir_build)/payload/main.elf: $(objects_payload) + # FatFs requires libgcc for __aeabi_uidiv + $(CC) -nostartfiles $(LDFLAGS) -T linker_payload.ld $(OUTPUT_OPTION) $^ + +# The arm11 payloads +$(dir_build)/%/main.elf: ASFLAGS := $(ARM11FLAGS) $(ASFLAGS) + +$(dir_build)/mset/main.elf: CFLAGS := -DENTRY_MSET $(ARM11FLAGS) $(CFLAGS) +$(dir_build)/mset/main.elf: $(patsubst $(dir_build)/%, $(dir_build)/mset/%, $(objects)) + $(LD) $(LDFLAGS) -T linker_mset.ld $(OUTPUT_OPTION) $^ + +$(dir_build)/spider/main.elf: CFLAGS := -DENTRY_SPIDER $(ARM11FLAGS) $(CFLAGS) +$(dir_build)/spider/main.elf: $(patsubst $(dir_build)/%, $(dir_build)/spider/%, $(objects)) + $(LD) $(LDFLAGS) -T linker_spider.ld $(OUTPUT_OPTION) $^ + +# Fatfs requires to be built in thumb +$(dir_build)/payload/fatfs/%.o: $(dir_source)/payload/fatfs/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) -mthumb -mthumb-interwork -Wno-unused-function $(OUTPUT_OPTION) $< + +$(dir_build)/payload/fatfs/%.o: $(dir_source)/payload/fatfs/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) -mthumb -mthumb-interwork $(OUTPUT_OPTION) $< + +$(dir_build)/payload/%.o: $(dir_source)/payload/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(dir_build)/payload/%.o: $(dir_source)/payload/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) $(OUTPUT_OPTION) $< + +.SECONDEXPANSION: +$(dir_build)/%.o: $(dir_source)/$$(notdir $$*).c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< + +.SECONDEXPANSION: +$(dir_build)/%.o: $(dir_source)/$$(notdir $$*).s + @mkdir -p "$(@D)" + $(COMPILE.s) $(OUTPUT_OPTION) $< + +include $(call rwildcard, $(dir_build), *.d) diff --git a/mset/linker_mset.ld b/mset/linker_mset.ld new file mode 100644 index 0000000..505539a --- /dev/null +++ b/mset/linker_mset.ld @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x00240000; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} diff --git a/mset/linker_payload.ld b/mset/linker_payload.ld new file mode 100644 index 0000000..d57403d --- /dev/null +++ b/mset/linker_payload.ld @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x23F00000; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} diff --git a/mset/linker_spider.ld b/mset/linker_spider.ld new file mode 100644 index 0000000..deda366 --- /dev/null +++ b/mset/linker_spider.ld @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x009D2000; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} diff --git a/mset/reiNand.dat b/mset/reiNand.dat new file mode 100644 index 0000000..06c6c22 Binary files /dev/null and b/mset/reiNand.dat differ diff --git a/mset/rop3ds/DownloadCode.S b/mset/rop3ds/DownloadCode.S new file mode 100644 index 0000000..6cb016c --- /dev/null +++ b/mset/rop3ds/DownloadCode.S @@ -0,0 +1,65 @@ + .arm + .text + +#include "rop.h" + +#ifdef ARM_CODE + #define CODE_SIZE arm_code_end-arm_code +#else + #define CODE_SIZE 0x300*15-0x460 +#endif + + .global _start +@--------------------------------------------------------------------------------- +_start: + rop_memcpy(BUFFER_LOC+0x300*0, ROP_LOC+0x304*1, 0x300) + rop_memcpy(BUFFER_LOC+0x300*1, ROP_LOC+0x304*2, 0x300) + rop_memcpy(BUFFER_LOC+0x300*2, ROP_LOC+0x304*3, 0x300) + rop_memcpy(BUFFER_LOC+0x300*3, ROP_LOC+0x304*4, 0x300) + + rop_memcpy(BUFFER_LOC+0x300*4, ROP_LOC+0x1400+0x304*0, 0x300) + rop_memcpy(BUFFER_LOC+0x300*5, ROP_LOC+0x1400+0x304*1, 0x300) + rop_memcpy(BUFFER_LOC+0x300*6, ROP_LOC+0x1400+0x304*2, 0x300) + rop_memcpy(BUFFER_LOC+0x300*7, ROP_LOC+0x1400+0x304*3, 0x300) + rop_memcpy(BUFFER_LOC+0x300*8, ROP_LOC+0x1400+0x304*4, 0x300) + + rop_memcpy(BUFFER_LOC+0x300*9, ROP_LOC+0x2800+0x304*0, 0x300) + rop_memcpy(BUFFER_LOC+0x300*10, ROP_LOC+0x2800+0x304*1, 0x300) + rop_memcpy(BUFFER_LOC+0x300*11, ROP_LOC+0x2800+0x304*2, 0x300) + rop_memcpy(BUFFER_LOC+0x300*12, ROP_LOC+0x2800+0x304*3, 0x300) + rop_memcpy(BUFFER_LOC+0x300*13, ROP_LOC+0x2800+0x304*4, 0x300) + + rop_fs_mount(DMC) + + .fill 3, 4, POP_PC + .word POP_R0_PC +@.org 0x21C + .word MAGIC + + rop_flush_data_cache(BUFFER_LOC+arm_code-0x300, CODE_SIZE) + rop_gx_texture_copy(BUFFER_LOC+arm_code-0x300, CODE_TARGET, CODE_SIZE) + rop_flush_data_cache(BUFFER_LOC, 0x38400*2) + rop_gx_texture_copy(BUFFER_LOC, 0x1F48F000, 0x38400*2) + rop_sleep(1000000000) + +@ rop_sleep(1000000000) +@ rop_flush_data_cache(BUFFER_LOC, 0x38400) +@ rop_gx_texture_copy(BUFFER_LOC, 0x1F4C7800, 0x38400) +@ rop_sleep(1000000000) + + rop_jump_arm + +.org 0x300 +.org 0x3F4 + .word ROP_LOC+_start+0x8C +.org 0x428 + .word ROP_LOC+_start, 0, POP_PC +.org 0x458 + .word POP_PC, POP_PC +arm_code: +#ifdef ARM_CODE + #define S_(x) #x + #define S(x) S_(x) + .incbin S(ARM_CODE) +#endif +arm_code_end: diff --git a/mset/rop3ds/LICENSE b/mset/rop3ds/LICENSE new file mode 100644 index 0000000..ec3ca36 --- /dev/null +++ b/mset/rop3ds/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2015 dukesrg, bilis + + 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. diff --git a/mset/rop3ds/LoadCode.S b/mset/rop3ds/LoadCode.S new file mode 100644 index 0000000..8ac278a --- /dev/null +++ b/mset/rop3ds/LoadCode.S @@ -0,0 +1,34 @@ + .arm + .text + +#include "rop.h" + +#ifndef SPIDER_ARM_CODE_SIZE + #define SPIDER_ARM_CODE_SIZE 0x00004000 +#endif + + .global _start +@--------------------------------------------------------------------------------- +_start: + rop_fs_mount(DMC) + rop_file_open(THIS, FileName, FILE_READ) + rop_store(THIS+4, SPIDER_ARM_CODE_OFFSET) + rop_file_read(THIS, THIS+0x20, BUFFER_LOC, SPIDER_ARM_CODE_SIZE) + rop_flush_data_cache(BUFFER_LOC, SPIDER_ARM_CODE_SIZE) + rop_gx_texture_copy(BUFFER_LOC, CODE_TARGET, SPIDER_ARM_CODE_SIZE) + rop_sleep(1000000000) + rop_jump_arm +@.org 0x0F4, GARBAGE + .fill 3, 4, GARBAGE + .word ROP_LOC+_start+0x8C +@.org 0x128, GARBAGE + .fill 12, 4, GARBAGE + .word ROP_LOC+_start, 0, POP_PC +@.org 0x158, GARBAGE + .fill 9, 4, GARBAGE + .fill 2, 4, POP_PC +FileName: + .string16 "dmc:/" +@.org 0x21C, GARBAGE + .fill 44, 4, GARBAGE + .word MAGIC diff --git a/mset/rop3ds/LoadCodeMset.S b/mset/rop3ds/LoadCodeMset.S new file mode 100644 index 0000000..d42d98b --- /dev/null +++ b/mset/rop3ds/LoadCodeMset.S @@ -0,0 +1,18 @@ + .arm + .text + +#include "rop.h" + +#ifndef MSET_ARM_CODE_SIZE + #define MSET_ARM_CODE_SIZE 0x00004000 +#endif + + .global _start +@--------------------------------------------------------------------------------- +_start: + rop_store(THIS+4, ARM_CODE_OFFSET) + rop_file_read(THIS, THIS+0x20, BUFFER_LOC, MSET_ARM_CODE_SIZE) + rop_flush_data_cache(BUFFER_LOC, MSET_ARM_CODE_SIZE) + rop_gx_texture_copy(BUFFER_LOC, CODE_TARGET, MSET_ARM_CODE_SIZE) + rop_sleep(1000000000) + rop_jump_arm diff --git a/mset/rop3ds/Makefile b/mset/rop3ds/Makefile new file mode 100644 index 0000000..3915d02 --- /dev/null +++ b/mset/rop3ds/Makefile @@ -0,0 +1,110 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/base_rules + +all: index.html compat.html rop.dat LoadCode.dat LoadCodeMset.dat + +%.dat: %.elf + @$(OBJCOPY) -O binary $^ $@ +%.elf: %.S + @$(CC) -c -o $@ $< $(ASFLAGS) + +bin2utf8: + @gcc *.c -o bin2utf8.exe -std=c99 + +%.utf8: %.dat bin2utf8 + @./bin2utf8.exe $< >$@ + +define makepayload + @echo "generating $(2) ROP" + @make -s LoadCode.dat ASFLAGS="-D$(2) -DSPIDER_ARM_CODE_OFFSET=$(3) -D$(4)" + @make -s LoadCode.utf8 + @sed -e "/$(1)'/{rLoadCode.utf8" -e "N}" -i $(5) + @sed "/$(1)'/s/\(.*\)\(\t\{3\}.*:'\)/\2\1/" -i $(5) + @rm LoadCode.dat + @rm LoadCode.utf8 +endef + +index.html: index.html.template bin2utf8 + @cp -f $< $@ + $(call makepayload,17498,SPIDER_4X,0,NO_SPIDER_DG,$@) + $(call makepayload,17538C45,SPIDER_45_CN,0,NO_SPIDER_DG,$@) + $(call makepayload,17538C42,SPIDER_42_CN,0,NO_SPIDER_DG,$@) + $(call makepayload,17538K,SPIDER_4X_KR,0,NO_SPIDER_DG,$@) + $(call makepayload,17538T,SPIDER_4X_TW,0,NO_SPIDER_DG,$@) + $(call makepayload,17552,SPIDER_5X,0,NO_SPIDER_DG,$@) + $(call makepayload,17552C,SPIDER_5X_CN,0,NO_SPIDER_DG,$@) + $(call makepayload,17552K,SPIDER_5X_KR,0,NO_SPIDER_DG,$@) + $(call makepayload,17552T,SPIDER_5X_TW,0,NO_SPIDER_DG,$@) + $(call makepayload,17567,SPIDER_9X,0,NO_SPIDER_DG,$@) + $(call makepayload,17567C,SPIDER_9X_CN,0,NO_SPIDER_DG,$@) + $(call makepayload,17567K,SPIDER_9X_KR,0,NO_SPIDER_DG,$@) + $(call makepayload,17567T,SPIDER_9X_TW,0,NO_SPIDER_DG,$@) + +compat.html: index.html.template bin2utf8 + @cp -f $< $@ + $(call makepayload,17498,SPIDER_4X,0,SPIDER_DG,$@) + $(call makepayload,17538C45,SPIDER_45_CN,0,SPIDER_DG,$@) + $(call makepayload,17538C42,SPIDER_42_CN,0,SPIDER_DG,$@) + $(call makepayload,17538K,SPIDER_4X_KR,0,SPIDER_DG,$@) + $(call makepayload,17538T,SPIDER_4X_TW,0,SPIDER_DG,$@) + +define makebigpayload + @echo "generating $(2) ROP" + @./bin2utf8.exe $(1).rop >rop.utf8 + @sed -e "/$(1)'/{rrop.utf8" -e "N}" -i $(5) + @sed "/$(1)'/s/\(.*\)\(\t\{3\}.*:'\)/\2\1/" -i $(5) + @rm rop.utf8 +endef + +big.html: index.html.template bin2utf8 + @cp -f $< $@ + $(call makebigpayload,17498,SPIDER_4X,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17538C45,SPIDER_45_CN,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17538C42,SPIDER_42_CN,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17538K,SPIDER_4X_KR,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17538T,SPIDER_4X_TW,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17552,SPIDER_5X,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17552C,SPIDER_5X_CN,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17552K,SPIDER_5X_KR,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17552T,SPIDER_5X_TW,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17567,SPIDER_9X,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17567C,SPIDER_9X_CN,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17567K,SPIDER_9X_KR,0,NO_SPIDER_DG,$@) + $(call makebigpayload,17567T,SPIDER_9X_TW,0,NO_SPIDER_DG,$@) + +define makedatpayload + @echo "generating $(2) ROP" + @make -s DownloadCode.dat ASFLAGS="-D$(2) -DSPIDER_ARM_CODE_OFFSET=$(3) -D$(4)" + @mv DownloadCode.dat $(1).dat +endef + +datpayload: download.html.template + $(call makedatpayload,17498,SPIDER_4X,0,NO_SPIDER_DG) + $(call makedatpayload,17538C45,SPIDER_45_CN,0,NO_SPIDER_DG) + $(call makedatpayload,17538C42,SPIDER_42_CN,0,NO_SPIDER_DG) + $(call makedatpayload,17538K,SPIDER_4X_KR,0,NO_SPIDER_DG) + $(call makedatpayload,17538T,SPIDER_4X_TW,0,NO_SPIDER_DG) + $(call makedatpayload,17552,SPIDER_5X,0,NO_SPIDER_DG) + $(call makedatpayload,17552C,SPIDER_5X_CN,0,NO_SPIDER_DG) + $(call makedatpayload,17552K,SPIDER_5X_KR,0,NO_SPIDER_DG) + $(call makedatpayload,17552T,SPIDER_5X_TW,0,NO_SPIDER_DG) + $(call makedatpayload,17567,SPIDER_9X,0,NO_SPIDER_DG) + $(call makedatpayload,17567C,SPIDER_9X_CN,0,NO_SPIDER_DG) + $(call makedatpayload,17567K,SPIDER_9X_KR,0,NO_SPIDER_DG) + $(call makedatpayload,17567T,SPIDER_9X_TW,0,NO_SPIDER_DG) + @cp -f $< download.html + +datpayloadcompat: download.html.template + $(call makedatpayload,17498,SPIDER_4X,0,SPIDER_DG) + $(call makedatpayload,17538C45,SPIDER_45_CN,0,SPIDER_DG) + $(call makedatpayload,17538C42,SPIDER_42_CN,0,SPIDER_DG) + $(call makedatpayload,17538K,SPIDER_4X_KR,0,SPIDER_DG) + $(call makedatpayload,17538T,SPIDER_4X_TW,0,SPIDER_DG) + @cp -f $< download.html + +.PHONY: clean +clean: + @rm -rf *.elf *.dat *.rop *.exe *.utf8 *.html diff --git a/mset/rop3ds/bin2utf8.c b/mset/rop3ds/bin2utf8.c new file mode 100644 index 0000000..9954ad0 --- /dev/null +++ b/mset/rop3ds/bin2utf8.c @@ -0,0 +1,43 @@ +#include +int main(int argc, const char *argv[]) +{ + FILE *file; + unsigned int state; + unsigned short data, prevdata = 1; + + if (argc != 2) + { + fprintf(stderr, "Usage: %s input-file\n", argv[0]); + return 255; + } + + if ((file = fopen(argv[1], "rb")) == NULL) + { + perror("fopen"); + return 1; + } + + while (fread(&data, 2, 1, file) == 1) + { + if (data == 0){ + printf("\\0"); + }else if (data == '\r' || data == '\n'){ + printf("\\%03o", data); + }else if (((prevdata == 0) && (data >= '0') && (data <= '7'))){ + printf("00%c", data); + }else if ((data == '\'') || /*(data == '"') ||*/ (data == '\\')){ + printf("\\%c", (char)data); + }else if (data == 0x2028 || ((data >= 0xD800) && (data <= 0xDFFF))){ + printf("\\u%04x", data); + }else if (data < 0x80){ + printf("%c", (char)data); + }else if (data < 0x800){ + printf("%c%c", ((data >> 6) & 0x1F) | 0xC0, (data & 0x3F) | 0x80); + }else{ + printf("%c%c%c", ((data >> 12) & 0x0F) | 0xE0, ((data >> 6) & 0x3F) | 0x80, ((data) & 0x3F) | 0x80); + } + prevdata = data; + } + fclose(file); + return 0; +} diff --git a/mset/rop3ds/bin2utf8.exe b/mset/rop3ds/bin2utf8.exe new file mode 100644 index 0000000..34ab11a Binary files /dev/null and b/mset/rop3ds/bin2utf8.exe differ diff --git a/mset/rop3ds/compat.html b/mset/rop3ds/compat.html new file mode 100644 index 0000000..e20891d --- /dev/null +++ b/mset/rop3ds/compat.html @@ -0,0 +1,51 @@ +

.

diff --git a/mset/rop3ds/download.html.template b/mset/rop3ds/download.html.template new file mode 100644 index 0000000..e75e639 --- /dev/null +++ b/mset/rop3ds/download.html.template @@ -0,0 +1,71 @@ +

.

diff --git a/mset/rop3ds/index.html b/mset/rop3ds/index.html new file mode 100644 index 0000000..fda32f6 --- /dev/null +++ b/mset/rop3ds/index.html @@ -0,0 +1,51 @@ +

.

diff --git a/mset/rop3ds/index.html.template b/mset/rop3ds/index.html.template new file mode 100644 index 0000000..3d4c796 --- /dev/null +++ b/mset/rop3ds/index.html.template @@ -0,0 +1,51 @@ +

.

diff --git a/mset/rop3ds/rop.S b/mset/rop3ds/rop.S new file mode 100644 index 0000000..901be91 --- /dev/null +++ b/mset/rop3ds/rop.S @@ -0,0 +1,21 @@ + .arm + .text + +#include "rop.h" + + .global _start +@--------------------------------------------------------------------------------- +_start: + rop_memcpy(BUFFER_LOC, ROP_LOC+arm_code, arm_code_end-arm_code) + rop_flush_data_cache(BUFFER_LOC, arm_code_end-arm_code) + rop_gx_texture_copy(BUFFER_LOC, CODE_TARGET, arm_code_end-arm_code) + rop_sleep(1000000000) + rop_jump_arm +.align 4, GARBAGE +arm_code: +#ifdef ARM_CODE + #define S_(x) #x + #define S(x) S_(x) + .incbin S(ARM_CODE) +#endif +arm_code_end: diff --git a/mset/rop3ds/rop.h b/mset/rop3ds/rop.h new file mode 100644 index 0000000..77cd90e --- /dev/null +++ b/mset/rop3ds/rop.h @@ -0,0 +1,541 @@ +#define DLPLAY_CODE_LOC_VA 0x00192800 +#define DLPLAY_NSSHANDLE_LOC_VA 0x001A5200 +#define KPROCESS_HANDLE 0xFFFF8001 +#define GX_SetTextureCopy 0x00000004 +#define FILE_READ 0x00000001 +#define FILE_WRITE 0x00000002 +#define FILE_CREATE 0x00000004 +#define GARBAGE 0x00230040 + +#if defined(MSET_4X) || defined(MSET_4X_DG) + #define ROP_LOC 0x002B0000 + #define HANDLE_PTR 0x0027FAC4 + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0013C5D4 + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x0027C580 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x001AC924 + #define LDR_R0_0_POP_R4_PC 0x0012FBBC + #define POP_PC 0x001002F9 + #define POP_R0_PC 0x00143D8C + #define POP_R1_PC 0x001C4FC4 +// #define POP_R1_PC 0x001549E1 + #define POP_R2_PC 0x0022952D + #define POP_R3_PC 0x0010538C + #define POP_R4_PC 0x001001ED +// #define POP_R4_PC 0x001B3AA0 + #define POP_R0_R2_PC 0x0010F2B9 + #define POP_R1_2_3_PC 0x001549B1 + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x0018D5DC + #define POP_R4_LR_BX_R2 0x001D9360 + #define STR_R1_0_POP_R4_PC 0x0010CCBC + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x001B82A8 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001B3954 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001B3B50 + #define SVC_0A_BX_LR 0x001AEA50 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x001BFA60 + #if defined(MSET_4X_DG) || defined(MSET_DG) + #define CODE_TARGET 0x17EB0000 + #else + #define CODE_TARGET 0x17FAD000 + #endif +#elif defined(MSET_6X) + #define ROP_LOC 0x00290000 + #define HANDLE_PTR 0x0028DBEC + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0013D3FC + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x0028A580 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x001B4E8C + #define LDR_R0_0_POP_R4_PC 0x00130818 + #define POP_PC 0x001002F9 + #define POP_R0_PC 0x00144CF8 + #define POP_R1_2_3_PC 0x0011BE4D + #define POP_R1_PC 0x001CD804 + #define POP_R3_PC 0x00105110 + #define POP_R4_PC 0x001001ED + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x0018B184 + #define POP_R4_LR_BX_R2 0x00192758 + #define STR_R1_0_POP_R4_PC 0x0010CF5C + #define SVC_0A_BX_LR 0x001B6C6C + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x001C08B4 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001BC188 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001BC380 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x001C814C + #define CODE_TARGET 0x17EA0000 +#elif defined(SPIDER_20) //1.7412.JP/US/EU + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x0017E63C + #define LDMFD_SP_R4_5_PC 0x00101418 + #define LDR_R0_0_POP_R4_PC 0x001CA228 + #define POP_PC 0x0010D8B4 + #define POP_R0_1_2_3_4_7_PC 0x001768FF + #define POP_R1_PC 0x0026A124 + #define POP_R4_5_6_PC 0x00100D24 + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x00103D3C + #define SP_LR_LDMFD_SP_LR_PC 0x002D5254 + #define STR_R1_0_POP_R4_PC 0x00119768 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x00332EBC + #define IFile_Open_LDMFD_SP_R4_5_6_7_PC 0x0025B8AC + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x002FA3F0 + #define DMC 0x002A497F + #define MAGIC 0x002D5240 + #define ROP_LOC 0x08CF2000 + #ifdef SPIDER_DG + #define CODE_TARGET 0x195CE000 + #else + #define CODE_TARGET 0x192CD000 + #endif +#elif defined(SPIDER_21) //1.7455.JP/US/EU + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x0017E764 + #define LDMFD_SP_R4_5_PC 0x00101418 + #define LDR_R0_0_POP_R4_PC 0x001CA350 + #define POP_PC 0x0010D954 + #define POP_R0_1_2_3_4_7_PC 0x00176A27 + #define POP_R1_PC 0x0026A528 + #define POP_R4_5_6_PC 0x00100D24 + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x00103D3C + #define SP_LR_LDMFD_SP_LR_PC 0x002D5654 + #define STR_R1_0_POP_R4_PC 0x00119864 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x00333330 + #define IFile_Open_LDMFD_SP_R4_5_6_7_PC 0x0025BC00 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x002FA864 + #define DMC 0x002A4C57 + #define MAGIC 0x002D5640 + #define ROP_LOC 0x08CF2000 + #ifdef SPIDER_DG + #define CODE_TARGET 0x195CE000 + #else + #define CODE_TARGET 0x192CD000 + #endif +#elif defined(SPIDER_4X) //1.7498.JP/US/EU + #define DLPLAY_CODE_LOC (DLPLAY_CODE_LOC_VA-0x00100000+0x03F50000+0x14000000-0x4000) + #define DLPLAY_HOOK_LOC (0x1A3500-0x00100000+0x03F50000+0x14000000-0x4000) + #define HANDLE_PTR 0x003B643C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x00344C2C + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003F54E8 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_PC 0x002CF3EC + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x00354850 + #define CALL_BX_LR 0x0025DFF0 + #define CALL_BX_LR_2 0x00344B84 + #define CALL_3 0x002C62E4 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x0018114C + #define LDMFD_SP_R4_5_PC 0x00101408 + #define LDR_R0_0_POP_R4_PC 0x001CCC64 + #define POP_LR_PC 0x002D6A34 + #define POP_PC 0x0010DB6C + #define POP_R0_PC 0x002AD574 + #define POP_R1_2_3_PC 0x00217450 + #define POP_R0_1_2_3_4_PC 0x0029C170 +// #define POP_R0_1_2_3_4_PC 0x0022B550 + #define POP_R0_1_2_3_4_7_PC 0x0017943B + #define POP_R1_PC 0x00269758 + #define POP_R2_PC 0x0012F815 + #define POP_R2_3_PC 0x00231A24 + #define POP_R2_3_4_PC 0x00101878 + #define POP_R3_PC 0x0011B064 + #define POP_R4_PC 0x0010DAA8 + #define POP_R4_5_6_PC 0x00100D24 + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x00103DA8 + #define POP_R4_LR_BX_R2 0x00100C8C + #define SP_LR_LDMFD_SP_LR_PC 0x002D6A30 + #define STR_R1_0_POP_R4_PC 0x00119B94 +// #define STR_R1_0_POP_R4_PC 0x0016F3FC + #define SVC_0A_BX_LR 0x002A513C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x00332BE8 + #define IFile_Open_LDMFD_SP_R4_5_6_7_PC 0x0025B0A4 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x002FC8E4 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00311D90 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0029BF60 + #define DMC 0x002A5F27 + #define MAGIC 0x002D6A1C + #define ROP_LOC 0x08B47400 +// #define ROP_LOC 0x08CF2000 + #ifdef SPIDER_DG + #define CODE_TARGET 0x195D1000 + #else + #define CODE_TARGET 0x192D3000 + #endif +#elif defined(SPIDER_42_CN) || defined(SPIDER_4X_KR) || defined(SPIDER_4X_TW) //1.7538.CN/KR/TW + #define CALL_3 0x0011DD48 + #define DMC 0x0010509F //CN? + #define LDMFD_SP_R4_5_PC 0x00101A44 + #define LDR_R0_0_POP_R4_PC 0x0011BADC + #define POP_PC 0x001057B4 + #define POP_R0_PC 0x0010C2F8 + #define POP_R3_PC 0x001050D4 + #define POP_R1_2_3_PC 0x00103DC8 + #define POP_R4_5_6_PC 0x0010014C //CN? + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x00106598 + #define STR_R1_0_POP_R4_PC 0x00106684 + #if defined(SPIDER_42_CN) //1.7538.CN FW4.2 + #define HANDLE_PTR 0x003D9704 + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD1C + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D6C40 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA40 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023F048 + #define SVC_0A_BX_LR 0x00104218 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019B640 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022E334 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001674BC + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00167544 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B7F18 + #define CALL_BX_LR 0x0023E4DC + #define CALL_BX_LR_2 0x00190118 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C469C + #define POP_LR_PC 0x0012FE98 + #define POP_R0_1_2_3_4_7_PC 0x001932FB + #define POP_R1_PC 0x00226B2C + #define POP_R2_3_PC 0x0014C734 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE94 + #define MAGIC 0x0012FE80 + #ifdef SPIDER_DG + #define CODE_TARGET 0x19593000 + #else + #define CODE_TARGET 0x19357000 + #endif + #elif defined(SPIDER_4X_KR) //1.7538.KR + #define HANDLE_PTR 0x003DA704 + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BCF0 + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D7C40 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA14 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023FF90 + #define SVC_0A_BX_LR 0x00104218 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019C258 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022F284 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001680F8 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00168180 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B8B68 + #define CALL_BX_LR 0x0023F424 + #define CALL_BX_LR_2 0x00190D30 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C4EC4 + #define POP_LR_PC 0x0012FE6C + #define POP_R0_1_2_3_4_7_PC 0x00193F13 + #define POP_R1_PC 0x00227A28 + #define POP_R2_3_PC 0x0014D2D8 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE68 + #define MAGIC 0x0012FE54 + #ifndef SPIDER_DG + #define CODE_TARGET 0x19255000 + #endif + #elif defined(SPIDER_4X_TW) //1.7538.TW + #define HANDLE_PTR 0x003DA704 + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD1C + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D7C40 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA40 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023FFE4 + #define SVC_0A_BX_LR 0x00104218 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019C260 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022F2D8 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001680FC + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00168184 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B8B70 + #define CALL_BX_LR 0x0023F478 + #define CALL_BX_LR_2 0x00190D34 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C4F14 + #define POP_LR_PC 0x0012FE98 + #define POP_R0_1_2_3_4_7_PC 0x00193F1B + #define POP_R1_PC 0x00227A64 + #define POP_R2_3_PC 0x0014D29C + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE94 + #define MAGIC 0x0012FE80 + #ifndef SPIDER_DG + #define CODE_TARGET 0x19355000 + #endif + #endif +#elif defined(SPIDER_45_CN) //1.7538.CN FW4.5 + #define CALL_3 0x0011DD68 + #define DMC 0x001050CF + #define LDMFD_SP_R4_5_PC 0x00101A40 + #define LDR_R0_0_POP_R4_PC 0x0011BB04 + #define POP_LR_PC 0x0012FEA8 + #define POP_PC 0x001057E4 + #define POP_R0_PC 0x0010C324 + #define POP_R1_PC 0x00226AF8 + #define POP_R1_2_3_PC 0x00103DC0 + #define POP_R2_3_PC 0x0014C26C + #define POP_R3_PC 0x00105104 +// #define POP_R4_5_6_PC 0x? + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x001065C8 + #define STR_R1_0_POP_R4_PC 0x001066B4 + #define HANDLE_PTR 0x003D9704 + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD30 + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D6C40 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA54 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023EFA0 + #define SVC_0A_BX_LR 0x0010420C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019B138 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022E2B0 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x00166FC8 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00167050 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C464C + #define CALL_BX_LR 0x0023E434 + #define CALL_BX_LR_2 0x0018FC0C + #define POP_R0_1_2_3_4_7_PC 0x00112211 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B7A10 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FEA4 + #define MAGIC 0x0012FE90 + #ifdef SPIDER_DG + #define CODE_TARGET 0x19593000 + #else + #define CODE_TARGET 0x19357000 + #endif +#elif defined(SPIDER_5X) || defined(SPIDER_5X_CN) || defined(SPIDER_5X_KR) || defined(SPIDER_5X_TW) + #define CALL_3 0x0011DD80 + #define DMC 0x001050CB + #define LDMFD_SP_R4_5_PC 0x00101A40 + #define LDR_R0_0_POP_R4_PC 0x0011BB00 + #define POP_PC 0x001057E0 + #define POP_R0_PC 0x0010C320 + #define POP_R1_2_3_PC 0x00103DC0 + #define POP_R3_PC 0x00105100 + #define POP_R4_5_6_PC 0x0010014C + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x001065C4 +// #define POP_R4_5_6_7_8_9_10_11_12_PC 0x0010CC4C + #define STR_R1_0_POP_R4_PC 0x001066B0 + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D7C40 + #if defined(SPIDER_5X_CN) //1.7552.CN + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD48 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA6C + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023F80C + #define SVC_0A_BX_LR 0x0010420C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019B7D0 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022EA5C + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x0016751C + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001675A4 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B80A8 + #define CALL_BX_LR 0x0023ECA0 + #define CALL_BX_LR_2 0x001902A8 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C4E98 + #define POP_LR_PC 0x0012FEC0 + #define POP_R0_1_2_3_4_7_PC 0x0019348B + #define POP_R1_PC 0x002272A0 + #define POP_R2_3_PC 0x0014C8AC + #define SP_LR_LDMFD_SP_LR_PC 0x0012FEBC + #define MAGIC 0x0012FEA8 + #elif defined(SPIDER_5X_KR) //1.7552.KR + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD1C + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA40 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x002407DC + #define SVC_0A_BX_LR 0x0010420C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CA78 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FAC8 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001686FC + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00168784 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B9350 + #define CALL_BX_LR 0x0023FC70 + #define CALL_BX_LR_2 0x0019154C + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C5748 + #define POP_LR_PC 0x0012FE94 + #define POP_R0_1_2_3_4_7_PC 0x00194733 + #define POP_R1_PC 0x00228274 + #define POP_R2_3_PC 0x0014D49C + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE90 + #define MAGIC 0x0012FE7C + #elif defined(SPIDER_5X_TW) //1.7552.TW + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD48 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA6C + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x00240870 + #define SVC_0A_BX_LR 0x0010420C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CAC0 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FB5C + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x00168744 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001687CC + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B9398 + #define CALL_BX_LR 0x0023FD04 + #define CALL_BX_LR_2 0x00191594 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C57D8 + #define POP_LR_PC 0x0012FEC0 + #define POP_R0_1_2_3_4_7_PC 0x0019477B + #define POP_R1_PC 0x002282F0 + #define POP_R2_3_PC 0x0014D4A4 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FEBC + #define MAGIC 0x0012FEA8 + #else //1.7552.JP/US/EU + #define DLPLAY_CODE_LOC (DLPLAY_CODE_LOC_VA-0x00100000+0x03F50000+0x14000000) + #define DLPLAY_HOOK_LOC (0x1A3500-0x00100000+0x03F50000+0x14000000) + #define HANDLE_PTR 0x003DA72C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012C228 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BF4C + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B9300 + #define CALL_BX_LR 0x0023FFEC + #define CALL_BX_LR_2 0x001914FC + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C5AC0 + #define POP_LR_PC 0x001303A4 + #define POP_R0_1_2_3_4_PC 0x0012A3D4 + #define POP_R0_1_2_3_4_7_PC 0x001946E3 + #define POP_R1_PC 0x00228B10 + #define POP_R2_3_PC 0x0014D554 + #define POP_R2_3_4_PC 0x001007B4 + #define POP_R4_PC 0x0010510C + #define SP_LR_LDMFD_SP_LR_PC 0x001303A0 + #define SVC_0A_BX_LR 0x0010420C + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CA28 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FE44 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001686C0 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00168748 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x00240B58 + #define MAGIC 0x0013038C + #define ROP_LOC 0x08B85400 +// #define ROP_LOC 0x088B5400 +// #define ROP_LOC 0x08CF208C + #endif +#elif defined(SPIDER_9X) || defined(SPIDER_9X_CN) || defined(SPIDER_9X_KR) || defined(SPIDER_9X_TW) + #define CALL_3 0x0011DD48 + #define DMC 0x001050B3 + #define LDMFD_SP_R4_5_PC 0x00101A34 + #define LDR_R0_0_POP_R4_PC 0x0011BACC + #define POP_PC 0x001057C4 + #define POP_R0_PC 0x0010C2FC + #define POP_R1_2_3_PC 0x00103DAC + #define POP_R3_PC 0x001050E8 + #define POP_R4_5_6_PC 0x0010014C + #define POP_R4_5_6_7_8_9_10_11_12_PC 0x001065A8 + #define STR_R1_0_POP_R4_PC 0x00106694 + #define nn__gxlow__CTR__detail__GetInterruptReceiver 0x003D7C40 + #if defined(SPIDER_9X_CN) //1.7567.CN + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD00 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA24 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x0023F808 + #define SVC_0A_BX_LR 0x001041F8 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019B7E0 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022EA24 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x00167540 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001675C8 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B80B8 + #define CALL_BX_LR 0x0023ECA0 + #define CALL_BX_LR_2 0x001902B8 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C4EB0 + #define POP_LR_PC 0x012FE78 + #define POP_R0_1_2_3_4_7_PC 0x0019349B + #define POP_R1_PC 0x0022728C + #define POP_R2_3_PC 0x0014C8F4 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE74 + #define MAGIC 0x0012FE60 + #elif defined(SPIDER_9X_KR) //1.7567.KR + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BCD4 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012B9F8 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x002407D4 + #define SVC_0A_BX_LR 0x001041F8 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CA80 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FA8C + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x00168718 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001687A0 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B9358 + #define CALL_BX_LR 0x0023FC6C + #define CALL_BX_LR_2 0x00191554 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C5760 + #define POP_LR_PC 0x0012FE4C + #define POP_R0_1_2_3_4_7_PC 0x0019473B + #define POP_R1_PC 0x0022825C + #define POP_R2_3_PC 0x0014D4E0 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE48 + #define MAGIC 0x0012FE34 + #elif defined(SPIDER_9X_TW) //1.7567.TW + #define HANDLE_PTR 0x003DA70C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012BD00 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BA24 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x00240868 + #define SVC_0A_BX_LR 0x001041F8 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CAC8 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FB20 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x00168760 + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x001687E8 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B93A0 + #define CALL_BX_LR 0x0023FD00 + #define CALL_BX_LR_2 0x0019159C + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C57F8 + #define POP_LR_PC 0x0012FE78 + #define POP_R0_1_2_3_4_7_PC 0x00194783 + #define POP_R1_PC 0x002282D8 + #define POP_R2_3_PC 0x0014D4E8 + #define SP_LR_LDMFD_SP_LR_PC 0x0012FE74 + #define MAGIC 0x0012FE60 + #else //1.7567.JP/US/EU + #define DLPLAY_CODE_LOC (DLPLAY_CODE_LOC_VA-0x00100000+0x03F50000+0x14000000) + #define DLPLAY_HOOK_LOC (0x03FF3500+0x14000000) + #define HANDLE_PTR 0x003DA72C + #define GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC 0x0012C1E0 + #define nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC 0x0012BF04 + #define BLX_R5_LDMFD_SP_R4_5_6_7_8_PC 0x001B9308 + #define CALL_BX_LR 0x0023FFE8 + #define CALL_BX_LR_2 0x00191504 + #define LDMFD_SP_R4_5_6_LR_BX_R12 0x002C5AE0 + #define POP_LR_PC 0x0013035C + #define POP_R0_1_2_3_4_PC 0x0010B5B4 + #define POP_R0_1_2_3_4_7_PC 0x001946EB + #define POP_R1_PC 0x00228AF4 + #define POP_R2_3_PC 0x0014D598 + #define POP_R2_3_4_PC 0x001007B4 + #define POP_R4_PC 0x001050F0 + #define SP_LR_LDMFD_SP_LR_PC 0x00130358 + #define SVC_0A_BX_LR 0x001041F8 + #define FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC 0x0019CA30 + #define IFile_Open_LDMFD_SP_R4_5_6_7_8_PC 0x0022FE08 + #define IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC 0x001686DC + #define IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC 0x00168764 + #define MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR 0x00240B50 + #define MAGIC 0x00130344 + #define ROP_LOC 0x08B88400 +// #define ROP_LOC 0x08CF2000 + #endif +#else + #error ROP version not defined +#endif +#if defined(MSET_4X) || defined(MSET_4X_DG) || defined(MSET_6X) + #define CODE_ENTRY 0x00240000 + #define BUFFER_LOC 0x14700000 + #define rop_fs_mount(drive) .word POP_R0_PC, drive, FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC + 4, GARBAGE, GARBAGE, GARBAGE + #define rop_file_open(handle, filename, mode) .word POP_R0_PC, handle, POP_R1_2_3_PC, ROP_LOC+filename, mode, GARBAGE, IFile_Open + 4, GARBAGE, GARBAGE, GARBAGE, GARBAGE, POP_PC + #define rop_flush_data_cache(buffer, size) .word POP_R0_PC, HANDLE_PTR, POP_R1_2_3_PC, KPROCESS_HANDLE, buffer, size, GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC + 4, GARBAGE, GARBAGE, GARBAGE + #if defined(MSET_6X) + #define THIS 0x00287000 + #define rop_file_read(handle, readcount, buffer, size) .word POP_R0_PC, handle, POP_R1_2_3_PC, readcount, buffer, size, IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC + 4, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE + #define rop_file_write(handle, writecount, buffer, size) .word POP_R1_2_3_PC, GARBAGE, POP_PC, GARBAGE, POP_R4_LR_BX_R2, GARBAGE, POP_PC, POP_R0_PC, handle, POP_R1_2_3_PC, writecount, buffer, size, IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC + #define rop_memcpy(dst, src, size) .word POP_R0_PC, dst, POP_R1_2_3_PC, src, size, GARBAGE, MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR + 4, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE + #define rop_sleep(ns) .word POP_R0_PC, ns, POP_R1_2_3_PC, 0, POP_PC, GARBAGE, POP_R4_LR_BX_R2, GARBAGE, POP_PC, SVC_0A_BX_LR + #else + #define THIS 0x00279000 + #define rop_file_read(handle, readcount, buffer, size) .word POP_R0_R2_PC, handle, POP_PC, POP_R4_LR_BX_R2, GARBAGE, POP_PC, POP_R1_2_3_PC, readcount, buffer, size, IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC + #define rop_file_write(handle, writecount, buffer, size) .word POP_R0_R2_PC, handle, POP_PC, POP_R4_LR_BX_R2, GARBAGE, POP_PC, POP_R1_2_3_PC, writecount, buffer, size, IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC + #define rop_memcpy(dst, src, size) .word POP_R0_R2_PC, dst, POP_PC, POP_R4_LR_BX_R2, GARBAGE, POP_PC, POP_R1_2_3_PC, src, size, GARBAGE, MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR + #define rop_sleep(ns) .word POP_R0_R2_PC, ns, POP_PC, POP_R4_LR_BX_R2, GARBAGE, POP_PC, POP_R1_PC, 0, SVC_0A_BX_LR + #endif +#else //Spider + #define CODE_ENTRY 0x009D2000 + #define THIS 0x0978CC00 + #ifndef CODE_TARGET + #define CODE_TARGET 0x19592000 + #endif + //#define THIS 0x08F10000 + #ifndef ROP_LOC + #if (defined(SPIDER_42_CN) || defined(SPIDER_45_CN) || defined(SPIDER_5X_CN) || defined(SPIDER_9X_CN)) + #define ROP_LOC 0x08CC0000 + #else + #define ROP_LOC 0x08CD0000 + #endif + #endif + #define BUFFER_LOC 0x18410000 + #define rop_flush_data_cache(buffer, size) .word POP_LR_PC, POP_PC, POP_R0_PC, HANDLE_PTR, POP_R1_2_3_PC, KPROCESS_HANDLE, buffer, size, GSPGPU_FlushDataCache_LDMFD_SP_R4_5_6_PC + #define rop_fs_mount(drive) .word POP_LR_PC, POP_PC, POP_R0_PC, drive, FS_MOUNTSDMC_LDMFD_SP_R3_4_5_PC + #define rop_sleep(ns) .word POP_LR_PC, POP_PC, POP_R0_PC, ns, POP_R1_PC, 0, SVC_0A_BX_LR + #define rop_memcpy(dst, src, size) .word POP_LR_PC, POP_PC, POP_R0_PC, dst, POP_R1_2_3_PC, src, size, GARBAGE, MEMCPY_LDMFD_SP_R4_5_6_7_8_9_10_LR + #define rop_file_read(handle, readcount, buffer, size) .word POP_LR_PC, POP_PC, POP_R0_PC, handle, POP_R1_2_3_PC, readcount, buffer, size, IFile_Read_LDMFD_SP_R4_5_6_7_8_9_PC + #define rop_file_write(handle, writecount, buffer, size) .word POP_LR_PC, POP_PC, POP_R0_PC, handle, POP_R1_2_3_PC, writecount, buffer, size, IFile_Write_LDMFD_SP_R4_5_6_7_8_9_10_11_PC + #if defined(SPIDER_4X) + #define rop_file_open(handle, filename, mode) .word POP_LR_PC, POP_PC, POP_R0_PC, handle, POP_R1_2_3_PC, ROP_LOC+filename, mode, GARBAGE, IFile_Open_LDMFD_SP_R4_5_6_7_PC + #else + #define rop_file_open(handle, filename, mode) .word POP_LR_PC, POP_PC, POP_R0_PC, handle, POP_R1_2_3_PC, ROP_LOC+filename, mode, GARBAGE, IFile_Open_LDMFD_SP_R4_5_6_7_8_PC + #endif +#endif +#define JOIN(a,b) a##b +#define LABEL(a) JOIN(loc_, a) +#define LINE_LABEL LABEL(__LINE__) +#if defined(SPIDER_4X) + #define rop_gx_texture_copy(src, dst, size) LINE_LABEL: .word POP_R0_PC, nn__gxlow__CTR__detail__GetInterruptReceiver+0x58, POP_R1_PC, ROP_LOC+LINE_LABEL+0x14, nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_PC + 4, GX_SetTextureCopy, src, dst, (size+0xF)&~0xF, 0xFFFFFFFF, POP_R0_PC, 0x00000008 +#else + #define rop_gx_texture_copy(src, dst, size) LINE_LABEL: .word POP_R0_PC, nn__gxlow__CTR__detail__GetInterruptReceiver+0x58, POP_R1_PC, ROP_LOC+LINE_LABEL+0x14, nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue_LDMFD_SP_R4_5_6_7_8_9_10_PC + 4, GX_SetTextureCopy, src, dst, (size+0xF)&~0xF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000008 +#endif +#define rop_jump(address) .word POP_R4_5_6_7_8_9_10_11_12_PC, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, GARBAGE, POP_PC, LDMFD_SP_R4_5_6_LR_BX_R12, GARBAGE, GARBAGE, GARBAGE, address-4, SP_LR_LDMFD_SP_LR_PC +#define rop_jump_arm .word CODE_ENTRY +#define rop_store(addr, val) .word POP_R0_PC, addr, POP_R1_PC, val, STR_R1_0_POP_R4_PC, GARBAGE diff --git a/mset/source/appcompat.c b/mset/source/appcompat.c new file mode 100644 index 0000000..6e33961 --- /dev/null +++ b/mset/source/appcompat.c @@ -0,0 +1,316 @@ +#include "appcompat.h" + +compat_app_s* app = (compat_app_s *) APP_COMPAT; + +#if defined(ENTRY_MSET) +compat_app_s mset_4x = +{ + .memcpy = (void*) 0x001BFA60, + .GSPGPU_FlushDataCache = (void*) 0x001346C4, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x001AC924, + .svcControlMemory = (void*) 0x001C3E24, + .fopen = (void*) 0x001B82A8, + .fread = (void*) 0x001B3954, + .fwrite = (void*) 0x001B3B50, + + .gpuHandle = (0x0027C580 + 0x58), +}; + +compat_app_s mset_6x = +{ + .memcpy = (void*) 0x001C814C, + .GSPGPU_FlushDataCache = (void*) 0x00134A84, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x001B4E8C, + .svcControlMemory = (void*) 0x001CC63C, + .fopen = (void*) 0x001C08B4, + .fread = (void*) 0x001BC188, + .fwrite = (void*) 0x001BC380, + + .gpuHandle = (0x0028A580 + 0x58), +}; + +#elif defined(ENTRY_SPIDER) +compat_app_s spider_4x = +{ + .memcpy = (void*) 0x0029BF60, + .GSPGPU_FlushDataCache = (void*) 0x00344B84, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x002CF3EC, + .svcControlMemory = (void*) 0x002D6ADC, + .fopen = (void*) 0x0025B0A4, + .fread = (void*) 0x002FC8E4, + .fwrite = (void*) 0x00311D90, + + .GX_SetTextureCopy = (void*) 0x002C62E4, + .svcSleepThread = (void*) 0x002A513C, + + .gpuHandle = (0x003F54E8 + 0x58), +}; + +compat_app_s spider_5x = +{ + .memcpy = (void*) 0x00240B58, + .GSPGPU_FlushDataCache = (void*) 0x001914FC, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BF4C, + .svcControlMemory = (void*) 0x001431C0, + .fopen = (void*) 0x0022FE44, + .fread = (void*) 0x001686C0, + .fwrite = (void*) 0x00168748, + + .GX_SetTextureCopy = (void*) 0x0011DD80, + .svcSleepThread = (void*) 0x0010420C, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_9x = +{ + .memcpy = (void*) 0x00240B50, + .GSPGPU_FlushDataCache = (void*) 0x00191504, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BF04, + .svcControlMemory = (void*) 0x001431A0, + .fopen = (void*) 0x0022FE08, + .fread = (void*) 0x001686DC, + .fwrite = (void*) 0x00168764, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x0023FFE8, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_42_cn = +{ + .memcpy = (void*) 0x0023F048, + .GSPGPU_FlushDataCache = (void*) 0x00190118, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA40, + .svcControlMemory = (void*) 0x00142F64, + .fopen = (void*) 0x0022E334, + .fread = (void*) 0x001674BC, + .fwrite = (void*) 0x00167544, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x00104218, + + .gpuHandle = (0x003D6C40 + 0x58), +}; + +compat_app_s spider_45_cn = +{ + .memcpy = (void*) 0x0023EFA0, + .GSPGPU_FlushDataCache = (void*) 0x0018FC0C, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA54, + .svcControlMemory = (void*) 0x00142F58, + .fopen = (void*) 0x0022E2B0, + .fread = (void*) 0x00166FC8, + .fwrite = (void*) 0x00167050, + + .GX_SetTextureCopy = (void*) 0x0011DD68, + .svcSleepThread = (void*) 0x0010420C, + + .gpuHandle = (0x003D6C40 + 0x58), +}; + +compat_app_s spider_5x_cn = +{ + .memcpy = (void*) 0x0023F80C, + .GSPGPU_FlushDataCache = (void*) 0x001902A8, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA6C, + .svcControlMemory = (void*) 0x00143110, + .fopen = (void*) 0x0022EA5C, + .fread = (void*) 0x0016751C, + .fwrite = (void*) 0x001675A4, + + .GX_SetTextureCopy = (void*) 0x0011DD80, + .svcSleepThread = (void*) 0x0010420C, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_9x_cn = +{ + .memcpy = (void*) 0x0023F808, + .GSPGPU_FlushDataCache = (void*) 0x001902B8, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA24, + .svcControlMemory = (void*) 0x001430F0, + .fopen = (void*) 0x0022EA24, + .fread = (void*) 0x00167540, + .fwrite = (void*) 0x001675C8, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x001041F8, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_4x_kr = +{ + .memcpy = (void*) 0x0023FF90, + .GSPGPU_FlushDataCache = (void*) 0x00190D30, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA14, + .svcControlMemory = (void*) 0x00142FA0, + .fopen = (void*) 0x0022F284, + .fread = (void*) 0x001680F8, + .fwrite = (void*) 0x00168180, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x00104218, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_5x_kr = +{ + .memcpy = (void*) 0x002407DC, + .GSPGPU_FlushDataCache = (void*) 0x0019154C, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA40, + .svcControlMemory = (void*) 0x00143108, + .fopen = (void*) 0x0022FAC8, + .fread = (void*) 0x001686FC, + .fwrite = (void*) 0x00168784, + + .GX_SetTextureCopy = (void*) 0x0011DD80, + .svcSleepThread = (void*) 0x0010420C, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_9x_kr = +{ + .memcpy = (void*) 0x002407D4, + .GSPGPU_FlushDataCache = (void*) 0x00191554, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012B9F8, + .svcControlMemory = (void*) 0x001430E8, + .fopen = (void*) 0x0022FA8C, + .fread = (void*) 0x00168718, + .fwrite = (void*) 0x001687A0, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x001041F8, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_4x_tw = +{ + .memcpy = (void*) 0x0023FFE4, + .GSPGPU_FlushDataCache = (void*) 0x00190D34, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA40, + .svcControlMemory = (void*) 0x00142F64, + .fopen = (void*) 0x0022F2D8, + .fread = (void*) 0x001680FC, + .fwrite = (void*) 0x00168184, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x00104218, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_5x_tw = +{ + .memcpy = (void*) 0x00240870, + .GSPGPU_FlushDataCache = (void*) 0x00191594, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA6C, + .svcControlMemory = (void*) 0x00143110, + .fopen = (void*) 0x0022FB5C, + .fread = (void*) 0x00168744, + .fwrite = (void*) 0x001687CC, + + .GX_SetTextureCopy = (void*) 0x0011DD80, + .svcSleepThread = (void*) 0x0010420C, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +compat_app_s spider_9x_tw = +{ + .memcpy = (void*) 0x00240868, + .GSPGPU_FlushDataCache = (void*) 0x0019159C, + .nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue = (void*) 0x0012BA24, + .svcControlMemory = (void*) 0x001430F0, + .fopen = (void*) 0x0022FB20, + .fread = (void*) 0x00168760, + .fwrite = (void*) 0x001687E8, + + .GX_SetTextureCopy = (void*) 0x0011DD48, + .svcSleepThread = (void*) 0x001041F8, + + .gpuHandle = (0x003D7C40 + 0x58), +}; + +#endif + +// Slow ass but safe memcpy +void _memcpy(void* dst, const void* src, uint32_t size) +{ + char *destc = (char *) dst; + const char *srcc = (const char *) src; + for(uint32_t i = 0; i < size; i++) + { + destc[i] = srcc[i]; + } +} + +int set_app_offsets() +{ + const compat_app_s *sapp = 0; + uint32_t app_spec = *(uint32_t *)0x0010000C; + switch(app_spec) + { +#if defined(ENTRY_MSET) + case 0xEB0312BF: + sapp = &mset_4x; + break; + case 0xEB0334CF: + sapp = &mset_6x; + break; +#elif defined(ENTRY_SPIDER) + case 0xEB0676B5: + sapp = &spider_4x; + break; + case 0xEB050B2A: + sapp = &spider_5x; + break; + case 0xEB050B28: + sapp = &spider_9x; + break; + case 0xEB050466: + sapp = &spider_42_cn; + break; + case 0xEB05043C: + sapp = &spider_45_cn; + break; + case 0xEB050657: + sapp = &spider_5x_cn; + break; + case 0xEB050656: + sapp = &spider_9x_cn; + break; + case 0xEB050838: + sapp = &spider_4x_kr; + break; + case 0xEB050A4B: + sapp = &spider_5x_kr; + break; + case 0xEB050A49: + sapp = &spider_9x_kr; + break; + case 0xEB05084D: + sapp = &spider_4x_tw; + break; + case 0xEB050A70: + sapp = &spider_5x_tw; + break; + case 0xEB050A6E: + sapp = &spider_9x_tw; + break; +#endif + } + + if (sapp) { + _memcpy(app, sapp, sizeof(compat_app_s)); + return 0; + } + else + return 1; +} diff --git a/mset/source/appcompat.h b/mset/source/appcompat.h new file mode 100644 index 0000000..3cd5152 --- /dev/null +++ b/mset/source/appcompat.h @@ -0,0 +1,52 @@ +#ifndef __appcompat_h__ +#define __appcompat_h__ + +#include +#include "launcher_path.h" + +typedef struct compat_app_s +{ + void (*memcpy)(void *dest, void *src, uint32_t len); + int (*GSPGPU_FlushDataCache)(void *address, uint32_t length); + void (*nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue)(void *arg1, void *arg2); + uint32_t (*svcControlMemory)(uint32_t *outaddr, uint32_t *addr0, uint32_t *addr1, uint32_t size, uint32_t operation, uint32_t permissions); + int (*fopen)(uint32_t (*handle)[], short unsigned int *path, int flags); + int (*fread)(uint32_t (*handle)[], uint32_t *read, void *buffer, uint32_t size); + int (*fwrite)(uint32_t (*handle)[], uint32_t *written, void *src, uint32_t size); + + uint32_t gpuHandle; + +#if defined(ENTRY_SPIDER) + int (*GX_SetTextureCopy)(void *input_buffer, void *output_buffer, uint32_t size, int in_x, int in_y, int out_x, int out_y, int flags); + int (*svcSleepThread)(unsigned long long nanoseconds); +#endif +} compat_app_s; + +extern compat_app_s* app; +int set_app_offsets(); + +#if defined(ENTRY_MSET) + // The usable area for this app + #define APP_FCRAM_ADDR 0x14000000 + + #define APP_CFW_OFFSET 0x400000 + #define APP_LAUNCHER_PATH (L"YS:/" LAUNCHER_PATH) + +#elif defined(ENTRY_SPIDER) + // The usable area for this app + #define APP_FCRAM_ADDR 0x18400000 + + #define APP_CFW_OFFSET 0x4410000 + #define APP_LAUNCHER_PATH (L"dmc:/" LAUNCHER_PATH) +#endif + +// Locations in fcram +#define APP_CHECK_MEM (APP_FCRAM_ADDR + 0x1000) +#define APP_ARM11_BUFFER (APP_FCRAM_ADDR + 0x2000) +#define APP_MEM_HAX_MEM (APP_FCRAM_ADDR + 0x50000) +#define APP_COMPAT (APP_FCRAM_ADDR + 0x20000) +#define APP_FIRM_COMPAT (APP_FCRAM_ADDR + 0x20100) + +#define ARM9_PAYLOAD_MAXSIZE 0x10000 + +#endif diff --git a/mset/source/arm11_tools.h b/mset/source/arm11_tools.h new file mode 100644 index 0000000..53b3531 --- /dev/null +++ b/mset/source/arm11_tools.h @@ -0,0 +1,10 @@ +#ifndef __arm11_tools_h__ +#define __arm11_tools_h__ + +#include + +void invalidate_data_cache(); +void invalidate_instruction_cache(); +void asm_memcpy(void *dest, void *src, uint32_t length); + +#endif diff --git a/mset/source/arm11_tools.s b/mset/source/arm11_tools.s new file mode 100644 index 0000000..0e7fed5 --- /dev/null +++ b/mset/source/arm11_tools.s @@ -0,0 +1,27 @@ +.global invalidate_data_cache +invalidate_data_cache: + mov r0, #0 + mcr p15, 0, r0, c7, c14, 0 @ Clean and Invalidate Entire Data Cache + mcr p15, 0, r0, c7, c10, 4 @ Data Synchronization Barrier + bx lr + +.global invalidate_instruction_cache +invalidate_instruction_cache: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + mcr p15, 0, r0, c7, c5, 4 + mcr p15, 0, r0, c7, c5, 6 + mcr p15, 0, r0, c7, c10, 4 + bx lr + +.global asm_memcpy +asm_memcpy: + add r2, r1 + + .memcpy_loop: + ldmia r1!, {r3} + stmia r0!, {r3} + cmp r1, r2 + bcc .memcpy_loop + + bx lr diff --git a/mset/source/firmcompat.c b/mset/source/firmcompat.c new file mode 100644 index 0000000..1fc2fa6 --- /dev/null +++ b/mset/source/firmcompat.c @@ -0,0 +1,45 @@ +#include "firmcompat.h" + +#include + +struct firmware_offsets *fw = (struct firmware_offsets *)APP_FIRM_COMPAT; + +int set_firmware_offsets() +{ + uint32_t kernel_version = *(uint32_t *)0x1FF80000; + + // Offsets taken from bootstrap + switch (kernel_version) { + + case 0x022E0000: // N3DS 9.0.0 + fw->kernel_patch_address = 0xDFF8382F; + fw->reboot_patch_address = 0xDFFF4994; + fw->reboot_func_address = 0xFFF158F8; + fw->jump_table_address = 0xDFFF4C80; + fw->fcram_address = 0xE0000000; + fw->func_patch_address = 0xDFFE7A50; + fw->func_patch_return = 0xFFF28A58; + fw->pdn_regs = 0xFFFBE000; + fw->pxi_regs = 0xFFFC0000; + fw->gpu_regs = 0xFFFBC000; + break; + + case 0x022C0600: // N3DS 8.0.0 + fw->kernel_patch_address = 0xDFF8382F; + fw->reboot_patch_address = 0xDFFF4994; + fw->reboot_func_address = 0xFFF158F8; + fw->jump_table_address = 0xDFFF4C80; + fw->fcram_address = 0xE0000000; + fw->func_patch_address = 0xDFFE7A50; + fw->func_patch_return = 0xFFF28A58; + fw->pdn_regs = 0xFFFBE000; + fw->pxi_regs = 0xFFFC0000; + fw->gpu_regs = 0xFFFBC000; + break; + + default: + return 1; + } + + return 0; +} diff --git a/mset/source/firmcompat.h b/mset/source/firmcompat.h new file mode 100644 index 0000000..16ec454 --- /dev/null +++ b/mset/source/firmcompat.h @@ -0,0 +1,24 @@ +#ifndef __firmcompat_h__ +#define __firmcompat_h__ + +#include +#include "appcompat.h" + +struct firmware_offsets { + uint32_t kernel_patch_address; + uint32_t reboot_patch_address; + uint32_t reboot_func_address; + uint32_t jump_table_address; + uint32_t fcram_address; + uint32_t func_patch_address; + uint32_t func_patch_return; + uint32_t pdn_regs; + uint32_t pxi_regs; + uint32_t gpu_regs; +}; + +struct firmware_offsets *fw; + +int set_firmware_offsets(); + +#endif diff --git a/mset/source/firmlaunchax.c b/mset/source/firmlaunchax.c new file mode 100644 index 0000000..715ae07 --- /dev/null +++ b/mset/source/firmlaunchax.c @@ -0,0 +1,72 @@ +#include "firmlaunchax.h" + +#include +#include "firmcompat.h" +#include "appcompat.h" +#include "arm11_tools.h" +#include "jump_table.h" + +void setup_gpu() +{ + volatile uint32_t *top_left1 = (volatile uint32_t *)(fw->gpu_regs + 0x468); + volatile uint32_t *top_left2 = (volatile uint32_t *)(fw->gpu_regs + 0x46C); + volatile uint32_t *top_right1 = (volatile uint32_t *)(fw->gpu_regs + 0x494); + volatile uint32_t *top_right2 = (volatile uint32_t *)(fw->gpu_regs + 0x498); + volatile uint32_t *top_selected = (volatile uint32_t *)(fw->gpu_regs + 0x478); + volatile uint32_t *bottom1 = (volatile uint32_t *)(fw->gpu_regs + 0x568); + volatile uint32_t *bottom2 = (volatile uint32_t *)(fw->gpu_regs + 0x56C); + volatile uint32_t *bottom_selected = (volatile uint32_t *)(fw->gpu_regs + 0x578); + + uint32_t *save = (uint32_t *)(fw->fcram_address + 0x3FFFE00); + if (*top_selected) { + save[0] = *top_left2; + save[1] = *top_right2; + } else { + save[0] = *top_left1; + save[1] = *top_right1; + } + + if (*bottom_selected) { + save[2] = *bottom2; + } else { + save[2] = *bottom1; + } +} + +void firmlaunch_arm9hax() +{ + invalidate_data_cache(); + invalidate_instruction_cache(); + + // Copy arm9 code + uint32_t code_offset = 0x3F00000; + asm_memcpy((void *)(fw->fcram_address + code_offset), + (void *)(fw->fcram_address + APP_CFW_OFFSET), ARM9_PAYLOAD_MAXSIZE); + + // Prepare framebuffer info for arm9 + setup_gpu(); + + // Copy the jump table + asm_memcpy((void *)fw->jump_table_address, &jump_table, (&jump_table_end - &jump_table + 1) * 4); + + // Write firmware-specific offsets to the jump table + *(uint32_t *)(fw->jump_table_address + + (&jt_return - &jump_table) * 4) = fw->func_patch_return; + *(uint32_t *)(fw->jump_table_address + + (&jt_pdn_regs - &jump_table) * 4) = fw->pdn_regs; + *(uint32_t *)(fw->jump_table_address + + (&jt_pxi_regs - &jump_table) * 4) = fw->pxi_regs; + + // Patch arm11 functions + *(uint32_t *)fw->func_patch_address = 0xE51FF004; + *(uint32_t *)(fw->func_patch_address + 4) = 0xFFFF0C80; + *(uint32_t *)fw->reboot_patch_address = 0xE51FF004; + *(uint32_t *)(fw->reboot_patch_address + 4) = 0x1FFF4C80+4; + + invalidate_data_cache(); + + // Trigger reboot + ((void (*)())fw->reboot_func_address)(0, 0, 2, 0); + + while (1) {}; +} diff --git a/mset/source/firmlaunchax.h b/mset/source/firmlaunchax.h new file mode 100644 index 0000000..ae99c15 --- /dev/null +++ b/mset/source/firmlaunchax.h @@ -0,0 +1,6 @@ +#ifndef __firmlaunchax_h__ +#define __firmlaunchax_h__ + +void firmlaunch_arm9hax(); + +#endif diff --git a/mset/source/jump_table.h b/mset/source/jump_table.h new file mode 100644 index 0000000..c42faf6 --- /dev/null +++ b/mset/source/jump_table.h @@ -0,0 +1,10 @@ +#ifndef __jump_table_h__ +#define __jump_table_h__ + +extern void *jump_table __asm__("jump_table"); +extern void *jump_table_end __asm__("jump_table_end"); +extern void *jt_return __asm__("jt_return"); +extern void *jt_pdn_regs __asm__("jt_pdn_regs"); +extern void *jt_pxi_regs __asm__("jt_pxi_regs"); + +#endif diff --git a/mset/source/jump_table.s b/mset/source/jump_table.s new file mode 100644 index 0000000..4085d59 --- /dev/null +++ b/mset/source/jump_table.s @@ -0,0 +1,150 @@ +.arm +.align 4 +.code 32 +.text + +.global jump_table +jump_table: + b func_patch_hook + b reboot_function + +func_patch_hook: + push {r0-r12, lr} + + mov r0, #0 + bl pxi_send + bl pxi_sync + + mov r0, #0x10000 + bl pxi_send + bl pxi_recv + bl pxi_recv + bl pxi_recv + + ldr r1, jt_pdn_regs + mov r0, #2 + strb r0, [r1, #0x230] + mov r0, #0x10 + bl busy_spin + mov r0, #0 + strb r0, [r1, #0x230] + mov r0, #0x10 + bl busy_spin + + pop {r0-r12, lr} + + ldr r0, =0x44836 + str r0, [r1] + ldr pc, jt_return + +reboot_function: + adr r0, arm11_reboot_hook + adr r1, arm11_reboot_hook_end + ldr r2, =0x1FFFFC00 + mov r4, r2 + bl copy_mem + bx r4 + +copy_mem: + sub r3, r1, r0 + mov r1, r3,asr#2 + cmp r1, #0 + ble copy_mem_ret + movs r1, r3,lsl#29 + sub r0, r0, #4 + sub r1, r2, #4 + bpl copy_mem_loc1 + ldr r2, [r0,#4]! + str r2, [r1,#4]! + + copy_mem_loc1: + movs r2, r3,asr#3 + beq copy_mem_ret + + copy_mem_loc2: + ldr r3, [r0,#4] + subs r2, r2, #1 + str r3, [r1,#4] + ldr r3, [r0,#8]! + str r3, [r1,#8]! + bne copy_mem_loc2 + + copy_mem_ret: + bx lr + +.pool + +arm11_reboot_hook: + ldr r0, pxi_regs + ldr r1, pxi_command + str r1, [r0] + + ldr r8, io_mem + ldr r9, arm9_payload + ldr r10, firm_header + + wait_arm9_loop: + ldrb r0, [r8] + ands r0, r0, #1 + bne wait_arm9_loop + + str r9, [r10, #0xC] + + mov r0, #0x1FFFFFF8 + wait_arm11_loop: + ldr r1, [r0] + cmp r1, #0 + beq wait_arm11_loop + + bx r1 + +pxi_regs: .long 0x10163008 +pxi_command: .long 0x44846 +io_mem: .long 0x10140000 +arm9_payload: .long 0x23F00000 +firm_header: .long 0x24000000 + +arm11_reboot_hook_end: + +.pool + +busy_spin: + subs r0, #2 + nop + bgt busy_spin + bx lr + +pxi_send: + ldr r1, jt_pxi_regs + pxi_send_l1: + ldrh r2, [r1,#4] + tst r2, #2 + bne pxi_send_l1 + str r0, [r1,#8] + bx lr + +pxi_sync: + ldr r0, jt_pxi_regs + ldrb r1, [r0,#3] + orr r1, #0x40 + strb r1, [r0,#3] + bx lr + +pxi_recv: + ldr r0, jt_pxi_regs + pxi_recv_l1: + ldrh r1, [r0,#4] + tst r1, #0x100 + bne pxi_recv_l1 + ldr r0, [r0,#0xC] + bx lr + +.global jt_pdn_regs +jt_pdn_regs: .long 0 +.global jt_pxi_regs +jt_pxi_regs: .long 0 +.global jt_return +jt_return: .long 0 + +.global jump_table_end +jump_table_end: diff --git a/mset/source/launcher_path.h b/mset/source/launcher_path.h new file mode 100644 index 0000000..1007577 --- /dev/null +++ b/mset/source/launcher_path.h @@ -0,0 +1,10 @@ +#ifndef __launcher_path_h__ +#define __launcher_path_h__ + +// This is a stupid file which just makes sure LAUNCHER_PATH is defined +// It should be defined externally, though. +#ifndef LAUNCHER_PATH +#define LAUNCHER_PATH "/reiNand.dat" +#endif + +#endif diff --git a/mset/source/main.c b/mset/source/main.c new file mode 100644 index 0000000..5d860d1 --- /dev/null +++ b/mset/source/main.c @@ -0,0 +1,71 @@ +#include +#include "firmcompat.h" +#include "appcompat.h" +#include "memchunkhax.h" +#include "firmlaunchax.h" + +int load_file(char *dest, short unsigned int *path, uint32_t offset, uint32_t size) +{ + uint32_t file_handle[8] = {0}; + uint32_t bytes_read = 0; + + int result = app->fopen(&file_handle, path, 1); + if (result != 0) { + return 1; + } + file_handle[1] = offset; + + app->fread(&file_handle, &bytes_read, dest, size); + + return 0; +} + +__attribute__((naked)) +void arm11_kernel_code() +{ + __asm__ volatile ("clrex"); + + // Reboot and load our arm9 payload in arm9 kernel mode + firmlaunch_arm9hax(); + + // We should never return here + while (1) {}; +} + +void cls(){ + uint8_t *const top_left_fb = (uint8_t *const)0x14184E60; + uint8_t *const top_right_fb = (uint8_t *const)0x14282160; + uint8_t *const top_left_fb2 = (uint8_t *const)0x141CB370; + uint8_t *const top_right_fb2 = (uint8_t *const)0x142C8670; + for(int i = 0; i < 0x00046500; ++i) + { + top_left_fb[i] = 0xFF; + top_right_fb[i] = 0xFF; + top_left_fb2[i] = 0xFF; + top_right_fb2[i] = 0xFF; + } +} + +void main() +{ + cls(); + int result; + + // Some offsets that differ per entry + result = set_app_offsets(); + if (result != 0) return; + + // Some offsets differ per firmware + result = set_firmware_offsets(); + if (result != 0) return; // Unsupported firmware, bailing out. + + // Load the arm9 payload to memory + // Spider has size restrictions to the Launcher, so we need to load the arm9 + // payload separately. + result = load_file((char *)(0x14000000 + APP_CFW_OFFSET), + APP_LAUNCHER_PATH, 0x12000, ARM9_PAYLOAD_MAXSIZE); + if (result != 0) return; // The user is probably an idiot, bailing out. + + // Now, we gain arm11 kernel mode + memchunk_arm11hax(arm11_kernel_code); +} diff --git a/mset/source/memchunkhax.c b/mset/source/memchunkhax.c new file mode 100644 index 0000000..c96fe04 --- /dev/null +++ b/mset/source/memchunkhax.c @@ -0,0 +1,112 @@ +#include "memchunkhax.h" + +#include +#include +#include "firmcompat.h" +#include "appcompat.h" + +void gspwn_copy(void *dest, void *src, uint32_t length, int check, int check_offset) +{ + // We'll use some memory to check the copy is being done right. + void *check_mem = (void *)APP_CHECK_MEM; + int *check_loc = (int *)(check_mem + check_offset); + + while(*check_loc != check) { + app->memcpy(check_mem, check_mem, 0x10000); + app->GSPGPU_FlushDataCache(src, length); + + uint32_t arr1[] = {4, (uint32_t)src, (uint32_t)dest, length, + 0xFFFFFFFF, 0xFFFFFFFF, 8, 0}; + app->nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue((void *)app->gpuHandle, arr1); + + app->GSPGPU_FlushDataCache(check_mem, 0x10); + + uint32_t arr2[] = {4, (uint32_t)dest, (uint32_t)check_mem, 0x10, + 0xFFFFFFFF, 0xFFFFFFFF, 8, 0}; + app->nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue((void *)app->gpuHandle, arr2); + + app->memcpy(check_mem, check_mem, 0x10000); + } +} + +#if defined(ENTRY_MSET) +void build_nop_slide(uint32_t *dest, unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) { + dest[i] = 0xE1A00000; // ARM instruction: nop + } + dest[i - 1] = 0xE12FFF1E; // ARM instruction: bx lr +} +#endif + +__attribute__((naked)) +void corrupted_svcCreateThread(__attribute__((unused)) void (*func)()) +{ + __asm__ volatile ("svc 8"); +} + +void memchunk_arm11hax(void (*func)()) +{ + // I need some memory locations to use + uint32_t *mem_hax_mem = (uint32_t *)APP_MEM_HAX_MEM; + uint32_t *mem_hax_mem_free = (uint32_t *)((uint32_t)mem_hax_mem + 0x1000); + + // There's not much documentation on this code. + // I've never studied the logic of it, so I can't describe it in more detail. + + // Free some random chunk of memory + uint32_t tmp_addr; + app->svcControlMemory(&tmp_addr, mem_hax_mem_free, NULL, 0x1000, 1 /* MEMOP_FREE */, 0); + + // arm11_buffer is the location that is copied *from* when using gspwn_copy + uint32_t *arm11_buffer = (uint32_t *)APP_ARM11_BUFFER; + arm11_buffer[0] = 1; + arm11_buffer[1] = fw->kernel_patch_address; + arm11_buffer[2] = 0; + arm11_buffer[3] = 0; + + // Copy the arm11_buffer to the freed memory with gspwn + gspwn_copy(mem_hax_mem_free, arm11_buffer, 0x10, fw->kernel_patch_address, 4); + + // Trigger kernel write + app->svcControlMemory(&tmp_addr, mem_hax_mem, NULL, 0x1000, 1 /* MEMOP_FREE */, 0); + + // Tricks to clear the instruction cache +#if defined(ENTRY_MSET) + int slide_len = 0x1000; + + // Build a NOP slide + build_nop_slide(arm11_buffer, slide_len); + + // Copy the NOP slide + uint32_t app_code_offset = 0; + uint32_t kernel_version = *(uint32_t *)0x1FF80000; + if(kernel_version < 0x02230600) { // firm 4.x + app_code_offset = 0x03E6D000; + } + else { // > firm 4.x + app_code_offset = 0x7B00000; + } + + gspwn_copy((void *)(0x14000000 + app_code_offset + 0x4000), arm11_buffer, + slide_len * 4, 0xE1A00000, 0); + + // Run the NOP slide + ((void (*)())0x104000)(); +#elif defined(ENTRY_SPIDER) + // Fills the bottom buffer with a random pattern + void *src = (void *)0x18000000; + for (int i = 0; i < 3; i++) { // Do it 3 times to be safe + app->GSPGPU_FlushDataCache(src, 0x00038400); + app->GX_SetTextureCopy(src, (void *)0x1F48F000, 0x00038400, 0, 0, 0, 0, 8); + app->svcSleepThread(0x400000LL); + app->GSPGPU_FlushDataCache(src, 0x00038400); + app->GX_SetTextureCopy(src, (void *)0x1F4C7800, 0x00038400, 0, 0, 0, 0, 8); + app->svcSleepThread(0x400000LL); + } +#endif + + // Get into arm11 + corrupted_svcCreateThread(func); +} diff --git a/mset/source/memchunkhax.h b/mset/source/memchunkhax.h new file mode 100644 index 0000000..dd2a5d6 --- /dev/null +++ b/mset/source/memchunkhax.h @@ -0,0 +1,6 @@ +#ifndef __memchunkhax_h__ +#define __memchunkhax_h__ + +void memchunk_arm11hax(void (*func)()); + +#endif diff --git a/mset/source/payload/fatfs/00readme.txt b/mset/source/payload/fatfs/00readme.txt new file mode 100644 index 0000000..c46ac5f --- /dev/null +++ b/mset/source/payload/fatfs/00readme.txt @@ -0,0 +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. + diff --git a/mset/source/payload/fatfs/diskio.c b/mset/source/payload/fatfs/diskio.c new file mode 100644 index 0000000..0eb8eb9 --- /dev/null +++ b/mset/source/payload/fatfs/diskio.c @@ -0,0 +1,102 @@ +/*-----------------------------------------------------------------------*/ +/* 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 */ + 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; + } + + 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 diff --git a/mset/source/payload/fatfs/diskio.h b/mset/source/payload/fatfs/diskio.h new file mode 100644 index 0000000..9ca0c7e --- /dev/null +++ b/mset/source/payload/fatfs/diskio.h @@ -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 diff --git a/mset/source/payload/fatfs/ff.c b/mset/source/payload/fatfs/ff.c new file mode 100644 index 0000000..41de2fc --- /dev/null +++ b/mset/source/payload/fatfs/ff.c @@ -0,0 +1,4694 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module 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. +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 32020 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp feature */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File access control feature */ +#if _FS_LOCK +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, directory (0:root) */ + WORD idx; /* Object ID 3, directory index */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS extend character conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +/*#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}*/ + +static +const unsigned short 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 +}; + +WCHAR ff_convert( /* Converted character, Returns zero on error */ +WCHAR src, /* Character code to be converted */ +UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + if(src < 0x80) + { /* ASCII */ + c = src; + } + else + { + if(dir) + { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + } + else + { /* Unicode to OEMCP */ + for(c = 0; c < 0x80; c++) + { + if(src == Tbl[c]) + break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + +WCHAR ff_wtoupper(WCHAR chr) +{ + if(chr >= 'a' && chr <= 'z') + return (chr - 32); + else + return chr; +} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN +#error Cannot use LFN feature without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Name status flags */ +#define NSFLAG 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */ +#define MIN_FAT16 4086U /* Minimum number of clusters as FAT16 */ +#define MIN_FAT32 65526U /* Minimum number of clusters as FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* x86 jump instruction (3) */ +#define BS_OEMName 3 /* OEM name (8) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ +#define BPB_NumFATs 16 /* Number of FAT copies (1) */ +#define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */ +#define BPB_TotSec16 19 /* Volume size [sector] (2) */ +#define BPB_Media 21 /* Media descriptor (1) */ +#define BPB_FATSz16 22 /* FAT size [sector] (2) */ +#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ +#define BPB_NumHeads 26 /* Number of heads (2) */ +#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ +#define BPB_TotSec32 32 /* Volume size [sector] (4) */ +#define BS_DrvNum 36 /* Physical drive number (2) */ +#define BS_BootSig 38 /* Extended boot signature (1) */ +#define BS_VolID 39 /* Volume serial number (4) */ +#define BS_VolLab 43 /* Volume label (8) */ +#define BS_FilSysType 54 /* File system type (1) */ +#define BPB_FATSz32 36 /* FAT size [sector] (4) */ +#define BPB_ExtFlags 40 /* Extended flags (2) */ +#define BPB_FSVer 42 /* File system version (2) */ +#define BPB_RootClus 44 /* Root directory first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ +#define BS_DrvNum32 64 /* Physical drive number (2) */ +#define BS_BootSig32 66 /* Extended boot signature (1) */ +#define BS_VolID32 67 /* Volume serial number (4) */ +#define BS_VolLab32 71 /* Volume label (8) */ +#define BS_FilSysType32 82 /* File system type (1) */ +#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ +#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ +#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ +#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ +#define MBR_Table 446 /* MBR: Partition table offset (2) */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define BS_55AA 510 /* Signature word (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* Lower case flag (1) */ +#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_LstAccDate 18 /* Last accessed date (2) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ +#define DIR_WrtTime 22 /* Modified time (2) */ +#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ +#define DIR_FileSize 28 /* File size (4) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ +#define LDIR_Attr 11 /* LFN attribute (1) */ +#define LDIR_Type 12 /* LFN type (1) */ +#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Must be zero (0) */ +#define SZ_DIRE 32 /* Size of a directory entry */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + + + + +/*------------------------------------------------------------*/ +/* Module private work area */ +/*------------------------------------------------------------*/ +/* Remark: Uninitialized variables with static duration are +/ guaranteed zero/null at start-up. If not, either the linker +/ or start-up routine being used is out of ANSI-C standard. +*/ + +#if _VOLUMES < 1 || _VOLUMES > 9 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non LFN feature */ +#define DEFINE_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() +#else +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN setting +#endif +#if _USE_LFN == 1 /* LFN feature with static working buffer */ +static WCHAR LfnBuf[_MAX_LFN + 1]; +#define DEFINE_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() +#elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ +#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() +#elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ +#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) +#else +#error Wrong _USE_LFN setting +#endif +#endif + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended characters */ +#endif + + + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof (int)) { + *(int*)d = *(int*)s; + d += sizeof (int); s += sizeof (int); + cnt -= sizeof (int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT +static +int lock_fs ( + FATFS* fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS* fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && + res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_LOCK + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + + /* The object has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->fs && + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->fs; + Files[i].clu = dp->sclust; + Files[i].idx = dp->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSINFO sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSINFO structure */ + mem_set(fs->win, 0, SS(fs)); + ST_WORD(fs->win + BS_55AA, 0xAA55); + ST_DWORD(fs->win + FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win + FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win + FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win + FSI_Nxt_Free, fs->last_clust); + /* Write it into the FSINFO sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */ + FATFS* fs, /* File system object */ + DWORD clst /* FAT index number (cluster number) to get the value */ +) +{ + UINT wc, bc; + BYTE *p; + DWORD val; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = clst & 1 ? wc >> 4 : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + val = LD_WORD(p); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val = LD_DWORD(p) & 0x0FFFFFFF; + break; + + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +#if !_FS_READONLY +FRESULT put_fat ( + FATFS* fs, /* File system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc++ % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + fs->wflag = 1; + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + fs->wflag = 1; + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + fs->wflag = 1; + break; + + default : + res = FR_INT_ERR; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_TRIM + DWORD scl = clst, ecl = clst, rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSINFO */ + fs->free_clust++; + fs->fsi_flag |= 1; + } +#if _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous clusters */ + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag |= 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +#if _USE_FASTSEEK +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (!ncl) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} +#endif /* _USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + DIR* dp, /* Pointer to directory object */ + UINT idx /* Index of directory table */ +) +{ + DWORD clst, sect; + UINT ic; + + + dp->index = (WORD)idx; /* Current index */ + clst = dp->sclust; /* Table start cluster (0:root) */ + if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dp->fs->dirbase; + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (idx >= dp->fs->n_rootdir) /* Is index out of range? */ + return FR_INT_ERR; + sect = dp->fs->dirbase; + } + else { /* Dynamic table (root-directory in FAT32 or sub-directory) */ + ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dp->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */ + return FR_INT_ERR; + idx -= ic; + } + sect = clust2sect(dp->fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!sect) return FR_INT_ERR; + dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE); /* Sector# of the directory entry */ + dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Ptr to the entry in the sector */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + UINT i; +#if !_FS_READONLY + UINT c; +#endif + + + i = dp->index + 1; + if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dp->fs) / SZ_DIRE))) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dp->fs, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */ +#if !_FS_READONLY + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */ + clst = create_chain(dp->fs, dp->clust); /* Stretch cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up stretched table */ + if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */ + mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */ + dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */ + for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */ + dp->fs->wflag = 1; + if (sync_window(dp->fs)) return FR_DISK_ERR; + dp->fs->winsect++; + } + dp->fs->winsect -= c; /* Rewind window offset */ +#else + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */ + return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(dp->fs, clst); + } + } + } + + dp->index = (WORD)i; /* Current index */ + dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Current entry in the window */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve directory entry */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT dir_alloc ( + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate (1-21) */ +) +{ + FRESULT res; + UINT n; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); + } + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir /* Pointer to the directory entry */ +) +{ + DWORD cl; + + cl = LD_WORD(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) + cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + BYTE* dir, /* Pointer to the directory entry */ + DWORD cl /* Value to be set */ +) +{ + ST_WORD(dir + DIR_FstClusLO, cl); + ST_WORD(dir + DIR_FstClusHI, cl >> 16); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + + +static +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR* lfnbuf, /* Pointer to the LFN to be compared */ + BYTE* dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last character has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all characters in the entry are checked */ + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE* dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last character has not been processed */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR* lfnbuf, /* Pointer to the LFN buffer */ + BYTE* dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding characters following last character */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR* dp /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dp->lfn) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; /* LFN start order */ + dp->lfn_idx = dp->index; /* Start index of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res; + BYTE a, c, *dir; +#if _USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dp->sect) { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; + dp->lfn_idx = dp->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dp->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; + + return res; +} +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT n, nent; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dp->fn; lfn = dp->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; + } + + if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */ + for (n = 0; lfn[n]; n++) ; + nent = (n + 25) / 13; + } else { /* Otherwise allocate an entry for an SFN */ + nent = 1; + } + res = dir_alloc(dp, nent); /* Allocate entries */ + + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->index - nent); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Sum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); + dp->fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ +#endif + + if (res == FR_OK) { /* Set SFN entry */ + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clean the entry */ + mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */ +#if _USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dp->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT i; + + i = dp->index; /* SFN index */ + res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dp, dp->index); + if (res == FR_OK) { + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +static +void get_fileinfo ( /* No return code */ + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + TCHAR *p, c; + BYTE *dir; +#if _USE_LFN + WCHAR w, *lfn; +#endif + + p = fno->fname; + if (dp->sect) { /* Get SFN */ + dir = dp->dir; + i = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */ +#if _USE_LFN + if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) + c += 0x20; /* To lower */ +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i])) + c = c << 8 | dir[i++]; + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#endif + *p++ = c; + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir + DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir + DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir + DIR_WrtTime); /* Time */ + } + *p = 0; /* Terminate SFN string by a \0 */ + +#if _USE_LFN + if (fno->lfname) { + i = 0; p = fno->lfname; + if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */ + lfn = dp->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (!w) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + p[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ + p[i++] = (TCHAR)w; + } + } + p[i] = 0; /* Terminate LFN string by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ +#if _USE_FIND && _FS_MINIMIZE <= 1 +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ + WCHAR chr; + +#if !_LFN_UNICODE + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ + if (IsDBCS1(chr) && IsDBCS2(**ptr)) /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#else + chr = ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif + return chr; +} + + +static +int pattern_matching ( /* Return value: 0:mismatched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (!pc) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + lfn = dp->lfn; + si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode character */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dp->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN character */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* DBC (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with deleted mark, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended character? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject nul string */ + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NSFLAG] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dp->sclust = 0; /* Strip it and start from the root directory */ + } else { /* No heading separator */ + dp->sclust = dp->fs->cdir; /* Start from the current directory */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dp->sclust = 0; /* Always start from the root directory */ +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + res = dir_sdi(dp, 0); + dp->dir = 0; + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the sagment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */ + dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + res = FR_OK; /* Ended at the root directroy. Function completed. */ + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + dir = dp->dir; /* Follow the sub-directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } + dp->sclust = ld_clust(dp->fs, dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const str[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = str[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) /* Load boot record */ + return 3; + + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + BYTE wmode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number from the path name */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; +#endif + /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ + if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + MBR_Table + i * SZ_PTE; + br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */ + } while (!LD2PT(vol) && fmt && ++i < 4); + } + if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found. Following code initializes the file system object */ + + if (LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */ + return FR_NO_FILESYSTEM; + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */ + return FR_NO_FILESYSTEM; + + fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZ_DIRE)) /* (Must be sector aligned) */ + return FR_NO_FILESYSTEM; + + tsect = LD_WORD(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win + BPB_TotSec32); + + nrsv = LD_WORD(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win + BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->last_clust = fs->free_clust = 0xFFFFFFFF; + + /* Get fsinfo if available */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */ + && LD_WORD(fs->win + BPB_FSInfo) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (LD_WORD(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 + && LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _FS_RPATH + fs->cdir = 0; /* Set current directory to root */ +#endif +#if _FS_LOCK /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + void* obj /* Pointer to the object FIL/DIR to check validity */ +) +{ + FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */ + + + if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id) + return FR_INVALID_OBJECT; + + ENTER_FF(fil->fs); /* Lock file system */ + + if (disk_status(fil->fs->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&fs, &path, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; +#if !_FS_READONLY + DWORD dw, cl; +#endif + + + if (!fp) return FR_INVALID_OBJECT; + fp->fs = 0; /* Clear file object */ + + /* Get logical drive number */ +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = find_volume(&dj.fs, &path, 0); +#endif + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Default directory itself */ + res = FR_INVALID_NAME; +#if _FS_LOCK + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_LOCK + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) /* Cannot create as new file */ + res = FR_EXIST; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = GET_FATTIME(); /* Created time */ + ST_DWORD(dir + DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir + DIR_FileSize, 0);/* size = 0 */ + cl = ld_clust(dj.fs, dir); /* Get start cluster */ + st_clust(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_LOCK + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + dir = dj.dir; + if (!dir) { /* Current directory itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->err = 0; /* Clear error flag */ + fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir + DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* Normal seek mode */ +#endif + fp->fs = dj.fs; /* Validate file object */ + fp->id = fp->fs->id; + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE csect, *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + } + if (clst < 2) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->dsect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = (const BYTE*)buff; + BYTE csect; + + + *bw = 0; /* Clear write byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fptr + btw < fp->fptr) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + if (clst == 0) /* When no cluster is allocated, */ + clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* Write-back sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ + if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector cache with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file change flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tm; + BYTE *dir; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ + /* Write-back dirty buffer */ +#if !_FS_TINY + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir + DIR_FileSize, fp->fsize); /* Update file size */ + st_clust(dir, fp->sclust); /* Update start cluster */ + tm = GET_FATTIME(); /* Update updated time */ + ST_DWORD(dir + DIR_WrtTime, tm); + ST_WORD(dir + DIR_LstAccDate, 0); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync_fs(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if !_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(fp); /* Lock volume */ + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = fp->fs; +#endif +#if _FS_LOCK + res = dec_lock(fp->lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + fp->fs = 0; /* Invalidate file object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = ld_clust(dj.fs, dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEFINE_NAMEBUF; + + + *buff = 0; + /* Get logical drive number */ + res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = len; /* Bottom of buffer (directory stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1); /* Get parent directory */ + if (res != FR_OK) break; + res = dir_read(&dj, 0); + if (res != FR_OK) break; + dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = buff; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + tp = fno.fname; +#if _USE_LFN + if (*buff) tp = buff; +#endif + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = tp[--n]; + buff[--i] = '/'; + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clst, bcs, nsect, ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->sclust; /* Top of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) + *tbl = 0; /* Terminate table */ + else + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fp->fs, fp->clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + } + + LEAVE_FF(fp->fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS* fs; + DEFINE_NAMEBUF; + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive number */ + res = find_volume(&fs, &path, 0); + if (res == FR_OK) { + dp->fs = fs; + INIT_BUF(*dp); + res = follow_path(dp, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dp->dir) { /* It is not the origin directory itself */ + if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */ + dp->sclust = ld_clust(fs, dp->dir); + else /* The object is a file */ + res = FR_NO_PATH; + } + if (res == FR_OK) { + dp->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK + if (res == FR_OK) { + if (dp->sclust) { + dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->lockid) + res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + + + res = validate(dp); + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = dp->fs; +#endif +#if _FS_LOCK + if (dp->lockid) /* Decrement sub-directory open counter */ + res = dec_lock(dp->lockid); + if (res == FR_OK) +#endif + dp->fs = 0; /* Invalidate directory object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEFINE_NAMEBUF; + + + res = validate(dp); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dp); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) { /* Reached end of directory */ + dp->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dp->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dp->fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find next file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ +#if _USE_LFN + if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break; /* Test for LFN if exist */ +#endif + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ + } + return res; + +} + + + +/*-----------------------------------------------------------------------*/ +/* Find first file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) + res = f_findnext(dp, fno); /* Find the first item */ + return res; +} + +#endif /* _USE_FIND */ + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } else { /* It is root directory */ + res = FR_INVALID_NAME; + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get logical drive number */ + res = find_volume(fatfs, &path, 0); + fs = *fatfs; + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if (fs->free_clust <= fs->n_fatent - 2) { + *nclst = fs->free_clust; + } else { + /* Get number of free clusters */ + fat = fs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(fs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < fs->n_fatent); + } else { + clst = fs->n_fatent; + sect = fs->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + fs->free_clust = n; + fs->fsi_flag |= 1; + *nclst = n; + } + } + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->err) { /* Check error */ + res = (FRESULT)fp->err; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->sclust); + fp->sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->n_fatent) { + res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } +#if !_FS_TINY + if (res == FR_OK && (fp->flag & FA__DIRTY)) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + res = FR_DISK_ERR; + else + fp->flag &= ~FA__DIRTY; + } +#endif + } + if (res != FR_OK) fp->err = (FRESULT)res; + } + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + BYTE *dir; + DWORD dclst = 0; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + if (res == FR_OK) { + dclst = ld_clust(dj.fs, dir); + if (dclst && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-directory ? */ +#if _FS_RPATH + if (dclst == dj.fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + mem_cpy(&sdj, &dj, sizeof (DIR)); /* Open the sub-directory */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item (excluding dot entries) */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? (cannot remove) */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? (can remove) */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync_fs(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tm = GET_FATTIME(); + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = sync_window(dj.fs); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir + DIR_WrtTime, tm); + st_clust(dir, dcl); + mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); /* Create ".." entry */ + dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + st_clust(dir + SZ_DIRE, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = sync_window(dj.fs); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir + DIR_WrtTime, tm); /* Created time */ + st_clust(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEFINE_NAMEBUF; + + + /* Get logical drive number of the source object */ + res = find_volume(&djo.fs, &path_old, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */ + res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */ + else + res = FR_INVALID_DRIVE; + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { +/* Start of critical section where any interruption can cause a cross-link */ + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djo.fs, dw); + dir = djo.fs->win + SZ_DIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(dir, djn.sclust); + djo.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync_fs(djo.fs); + } +/* End of critical section */ + } + } + } + } + FREE_BUF(); + } + + LEAVE_FF(djo.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir + DIR_WrtTime, fno->ftime); + ST_WORD(dir + DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + DIR dj; + UINT i, j; +#if _USE_LFN && _LFN_UNICODE + WCHAR w; +#endif + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is exist */ +#if _USE_LFN && _LFN_UNICODE + i = j = 0; + do { + w = (i < 11) ? dj.dir[i++] : ' '; + if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i])) + w = w << 8 | dj.dir[i++]; + label[j++] = ff_convert(w, 1); /* OEM -> Unicode */ + } while (j < 11); +#else + mem_cpy(label, dj.dir, 11); +#endif + j = 11; + do { + label[j] = 0; + if (!j) break; + } while (label[--j] == ' '); + } + if (res == FR_NO_FILE) { /* No label, return nul string */ + label[0] = 0; + res = FR_OK; + } + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(dj.fs, dj.fs->volbase); + if (res == FR_OK) { + i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; + *vsn = LD_DWORD(&dj.fs->win[i]); + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + DIR dj; + BYTE vn[11]; + UINT i, j, sl; + WCHAR w; + DWORD tm; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &label, 1); + if (res) LEAVE_FF(dj.fs, res); + + /* Create a volume label in directory form */ + vn[0] = 0; + for (sl = 0; label[sl]; sl++) ; /* Get name length */ + for ( ; sl && label[sl - 1] == ' '; sl--) ; /* Remove trailing spaces */ + if (sl) { /* Create volume label in directory form */ + i = j = 0; + do { +#if _USE_LFN && _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) + w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; +#if _USE_LFN + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (w >= 0x100) vn[j++] = (BYTE)(w >> 8); + vn[j++] = (BYTE)w; + } while (i < sl); + while (j < 11) vn[j++] = ' '; /* Fill remaining name field */ + if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + + /* Set volume label */ + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is found */ + if (vn[0]) { + mem_cpy(dj.dir, vn, 11); /* Change the volume label name */ + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + } else { + dj.dir[0] = DDEM; /* Remove the volume label */ + } + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } else { /* No volume label is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (vn[0]) { /* Create volume label as new */ + res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZ_DIRE); /* Set volume label */ + mem_cpy(dj.dir, vn, 11); + dj.dir[DIR_Attr] = AM_VOL; + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (available on only tiny cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + BYTE csect; + + + *bf = 0; /* Clear transfer byte counter */ + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->sclust : get_fat(fp->fs, fp->clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + if (move_window(fp->fs, sect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btf) rcnt = btf; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create file system on the logical drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ +#define N_FATS 1 /* Number of FATs (1 or 2) */ + + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Size of allocation unit in unit of byte or sector */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + int vol; + BYTE fmt, md, sys, *tbl, pdrv, part; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; +#if _USE_TRIM + DWORD eb[2]; +#endif + + + /* Check mounted drive and clear work area */ + if (sfd > 1) return FR_INVALID_PARAMETER; + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + fs = FatFs[vol]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ + + /* Get disk statics */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != _MIN_SS /* Get disk sector size */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) + return FR_DISK_ERR; +#endif + if (_MULTI_PARTITION && part) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; + if (LD_WORD(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = LD_DWORD(tbl + 8); /* Volume start sector */ + n_vol = LD_DWORD(tbl + 12); /* Volume size */ + } else { + /* Create a partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; /* Volume size */ + } + + if (au & (au - 1)) au = 0; + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ + if (!au) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT sub-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = (DWORD)N_ROOTDIR * SZ_DIRE / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + /* Determine system ID in the partition table */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (fmt == FS_FAT12 && n_vol < 0x10000) { + sys = 0x01; /* FAT12(<65536) */ + } else { + sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + } + } + + if (_MULTI_PARTITION && part) { + /* Update system ID in the partition table */ + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + tbl[4] = sys; + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to teh MBR */ + return FR_DISK_ERR; + md = 0xF8; + } else { + if (sfd) { /* No partition table (SFD) */ + md = 0xF0; + } else { /* Create partition table (FDISK) */ + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */ + tbl[1] = 1; /* Partition start head */ + tbl[2] = 1; /* Partition start sector */ + tbl[3] = 0; /* Partition start cylinder */ + tbl[4] = sys; /* System type */ + tbl[5] = 254; /* Partition end head */ + n = (b_vol + n_vol) / 63 / 255; + tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */ + tbl[7] = (BYTE)n; /* End cylinder */ + ST_DWORD(tbl + 8, 63); /* Partition start in LBA */ + ST_DWORD(tbl + 12, n_vol); /* Partition size in LBA */ + ST_WORD(fs->win + BS_55AA, 0xAA55); /* MBR signature */ + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR */ + return FR_DISK_ERR; + md = 0xF8; + } + } + + /* Create BPB in the VBR */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl + BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl + BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */ + ST_WORD(tbl + BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl + BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl + BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + ST_WORD(tbl + BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl + BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl + BPB_HiddSec, b_vol); /* Hidden sectors */ + n = GET_FATTIME(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl + BS_VolID32, n); /* VSN */ + ST_DWORD(tbl + BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl + BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl + BPB_FSInfo, 1); /* FSINFO record offset (VBR + 1) */ + ST_WORD(tbl + BPB_BkBootSec, 6); /* Backup boot record offset (VBR + 6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl + BS_VolID, n); /* VSN */ + ST_WORD(tbl + BPB_FATSz16, n_fat); /* Number of sectors per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + ST_WORD(tbl + BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR + 6) */ + disk_write(pdrv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl + 4, 0xFFFFFFFF); + ST_DWORD(tbl + 8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */ + } + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : (UINT)n_dir; + do { + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_TRIM /* Erase data area if needed */ + { + eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; + disk_ioctl(pdrv, CTRL_TRIM, eb); + } +#endif + + /* Create FSINFO if needed */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl + FSI_LeadSig, 0x41615252); + ST_DWORD(tbl + FSI_StrucSig, 0x61417272); + ST_DWORD(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + ST_DWORD(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + ST_WORD(tbl + BS_55AA, 0xAA55); + disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */ + disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */ + } + + return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + + + +#if _MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create partition table on the physical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD szt[], /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine CHS in the table regardless of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x06; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + ST_DWORD(p + 8, s_part); /* Start sector in LBA */ + ST_DWORD(p + 12, sz_part); /* Partition size */ + + /* Next partition */ + b_cyl += p_cyl; + } + ST_WORD(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + + +#endif /* _MULTI_PARTITION */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL* fp; + int idx, nchr; + BYTE buf[64]; +} putbuff; + + +static +void putc_bfd ( + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + + i = pb->idx; /* Buffer write index (-1:error) */ + if (i < 0) return; + +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + putc_bfd(&pb, c); /* Put a character */ + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + while (*str) /* Put the string */ + putc_bfd(&pb, *str++); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT nw, i, j, w; + DWORD v; + TCHAR c, d, s[16], *p; + putbuff pb; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + s[i++] = d + '0'; + } while (v && i < sizeof s / sizeof s[0]); + if (f & 8) s[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do putc_bfd(&pb, s[--i]); while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/mset/source/payload/fatfs/ff.h b/mset/source/payload/fatfs/ff.h new file mode 100644 index 0000000..22f1810 --- /dev/null +++ b/mset/source/payload/fatfs/ff.h @@ -0,0 +1,350 @@ +/*---------------------------------------------------------------------------/ +/ 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 */ diff --git a/mset/source/payload/fatfs/ffconf.h b/mset/source/payload/fatfs/ffconf.h new file mode 100644 index 0000000..8c80854 --- /dev/null +++ b/mset/source/payload/fatfs/ffconf.h @@ -0,0 +1,271 @@ +/*---------------------------------------------------------------------------/ +/ 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 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 2 +/* 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. +*/ + diff --git a/mset/source/payload/fatfs/history.txt b/mset/source/payload/fatfs/history.txt new file mode 100644 index 0000000..b101820 --- /dev/null +++ b/mset/source/payload/fatfs/history.txt @@ -0,0 +1,180 @@ +---------------------------------------------------------------------------- + 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) diff --git a/mset/source/payload/fatfs/integer.h b/mset/source/payload/fatfs/integer.h new file mode 100644 index 0000000..f254b2a --- /dev/null +++ b/mset/source/payload/fatfs/integer.h @@ -0,0 +1,33 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#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 diff --git a/mset/source/payload/fatfs/sdmmc/common.h b/mset/source/payload/fatfs/sdmmc/common.h new file mode 100644 index 0000000..76647ad --- /dev/null +++ b/mset/source/payload/fatfs/sdmmc/common.h @@ -0,0 +1,39 @@ +/* + * 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/. + */ + +#pragma once + +#include +#include +#include + +#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 diff --git a/mset/source/payload/fatfs/sdmmc/delay.h b/mset/source/payload/fatfs/sdmmc/delay.h new file mode 100644 index 0000000..b27fd33 --- /dev/null +++ b/mset/source/payload/fatfs/sdmmc/delay.h @@ -0,0 +1,31 @@ +/* + * 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/. + */ +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common.h" + +void ioDelay(u32 us); diff --git a/mset/source/payload/fatfs/sdmmc/delay.s b/mset/source/payload/fatfs/sdmmc/delay.s new file mode 100644 index 0000000..5c3528e --- /dev/null +++ b/mset/source/payload/fatfs/sdmmc/delay.s @@ -0,0 +1,37 @@ +/* + * 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/. + */ + +.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 diff --git a/mset/source/payload/fatfs/sdmmc/sdmmc.c b/mset/source/payload/fatfs/sdmmc/sdmmc.c new file mode 100644 index 0000000..0045d09 --- /dev/null +++ b/mset/source/payload/fatfs/sdmmc/sdmmc.c @@ -0,0 +1,487 @@ +/* + * 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 "common.h" + +#include "sdmmc.h" +#include "delay.h" + +//Uncomment to enable 32bit fifo support? +//not currently working +//#define DATA32_SUPPORT + +static struct mmcdevice handleNAND; +static struct mmcdevice handleSD; + +mmcdevice *getMMCDevice(int drive) +{ + if(drive==0) return &handleNAND; + return &handleSD; +} + +int __attribute__((noinline)) geterror(struct mmcdevice *ctx) +{ + //if(ctx->error == 0x4) return -1; + //else return 0; + return (ctx->error << 29) >> 31; +} + + +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); + } + +} + + +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); + +#ifdef DATA32_SUPPORT + if (readdata) + sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0x800); + if (writedata) + sdmmc_mask16(REG_SDDATACTL32, 0x800, 0x1000); +#else + sdmmc_mask16(REG_SDDATACTL32,0x1800,0); +#endif + + sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); + sdmmc_write16(REG_SDCMDARG1,args >> 16); + sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + + u32 size = ctx->size; + u16 *dataPtr = (u16*)ctx->data; + +#ifdef DATA32_SUPPORT + u32 *dataPtr32 = (u32*)ctx->data; +#endif + + bool useBuf = ( NULL != dataPtr ); + +#ifdef DATA32_SUPPORT + bool useBuf32 = (useBuf && (0 == (3 & ((u32)dataPtr)))); +#endif + + 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) { +#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; + } + } + } + + 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) { +#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; + } + } + } + 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); + } +} + +int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in) +{ + if (handleSD.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleSD); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); +#endif + + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handleSD.data = in; + handleSD.size = numsectors << 9; + sdmmc_send_command(&handleSD,0x52C19,sector_no); + return geterror(&handleSD); +} + +int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) +{ + if (handleSD.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleSD); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); +#endif + + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handleSD.data = out; + handleSD.size = numsectors << 9; + sdmmc_send_command(&handleSD,0x33C12,sector_no); + return geterror(&handleSD); +} + + + +int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) +{ + if (handleNAND.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleNAND); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write32(REG_SDBLKCOUNT32,numsectors); +#else + sdmmc_write16(REG_SDBLKCOUNT,numsectors); +#endif + + handleNAND.data = out; + handleNAND.size = numsectors << 9; + sdmmc_send_command(&handleNAND,0x33C12,sector_no); + inittarget(&handleSD); + return geterror(&handleNAND); +} + +int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in) //experimental +{ + if (handleNAND.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleNAND); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write32(REG_SDBLKCOUNT32,numsectors); +#else + sdmmc_write16(REG_SDBLKCOUNT,numsectors); +#endif + + handleNAND.data = in; + handleNAND.size = numsectors << 9; + sdmmc_send_command(&handleNAND,0x52C19,sector_no); + inittarget(&handleSD); + return geterror(&handleNAND); +} + +u32 calcSDSize(u8* csd, int type) +{ + u32 result = 0; + u8 temp = csd[0xE]; + //int temp3 = type; + + switch (type) { + case -1: + type = temp >> 6; + break; + case 0: + { + u32 temp = (csd[0x7] << 0x2 | csd[0x8] << 0xA | csd[0x6] >> 0x6 | (csd[0x9] & 0xF) << 0x10) & 0xFFF; + u32 temp2 = temp * (1 << (csd[0x9] & 0xF)); + u32 retval = temp2 * (1 << (((csd[0x4] >> 7 | csd[0x5] << 1) & 7) + 2)); + result = retval >> 9; + break; + } + case 1: + result = (((csd[0x7] & 0x3F) << 0x10 | csd[0x6] << 8 | csd[0x5]) + 1) << 0xA; + break; + default: + result = 0; + break; + } + + return result; +} + +void InitSD() +{ + //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; + + //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); + + *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 + *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 +#ifdef DATA32_SUPPORT + *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 +#else + *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 +#endif + *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; +#ifdef DATA32_SUPPORT + *(vu16*)0x10006100 &= 0xFFFFu; //SDDATACTL32 + *(vu16*)0x100060D8 &= 0xFFDFu; //SDDATACTL + *(vu16*)0x10006104 = 512; //SDBLKLEN32 +#else + *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 + *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL + *(vu16*)0x10006104 = 0; //SDBLKLEN32 +#endif + *(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 +#ifdef DATA32_SUPPORT + *(vu16*)0x10006024 = 0x20; + *(vu16*)0x10006028 = 0x40EE; +#else + *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 + *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE +#endif + *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL + *(vu16*)0x10006026 = 512; //SDBLKLEN + *(vu16*)0x10006008 = 0; //SDSTOP + + inittarget(&handleSD); +} + +int Nand_Init() +{ + 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 SD_Init() +{ + inittarget(&handleSD); + //ioDelay(0x3E8); + ioDelay(0xF000); + 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); + //do + //{ + // sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); + // sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); + // + //} + //while(!(handleSD.ret[0] & 0x80000000)); + + if(!((handleSD.ret[0] >> 30) & 1) || !temp) + temp2 = 0; + + handleSD.isSDHC = temp2; + //handleSD.isSDHC = (handleSD.ret[0] & 0x40000000); + + 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(); + Nand_Init(); + SD_Init(); +} diff --git a/mset/source/payload/fatfs/sdmmc/sdmmc.h b/mset/source/payload/fatfs/sdmmc/sdmmc.h new file mode 100644 index 0000000..06a1fc4 --- /dev/null +++ b/mset/source/payload/fatfs/sdmmc/sdmmc.h @@ -0,0 +1,191 @@ +/* + * 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/. + */ + +#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 { + u8* 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; + +/*int sdmmc_sdcard_init(); +void sdmmc_sdcard_readsector(uint32_t sector_no, void *out); +void sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, void *out); +void sdmmc_sdcard_writesector(uint32_t sector_no, void *in); +void sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, void *in); +void sdmmc_blktransferinit();*/ + +void sdmmc_sdcard_init(); +int sdmmc_sdcard_readsector(u32 sector_no, u8 *out); +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_sdcard_writesector(u32 sector_no, u8 *in); +int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in); + +int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in); + +mmcdevice *getMMCDevice(int drive); + +void InitSDMMC(); +int Nand_Init(); +int SD_Init(); + +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); +} diff --git a/mset/source/payload/main.c b/mset/source/payload/main.c new file mode 100644 index 0000000..4c544d9 --- /dev/null +++ b/mset/source/payload/main.c @@ -0,0 +1,72 @@ +#include +#include +#include "fatfs/ff.h" +#include "fatfs/sdmmc/sdmmc.h" +#include "../launcher_path.h" + +void *payload_loc = (void *)0x08000000; +unsigned int payload_offset = 0x22000; +unsigned int payload_size = 0x50000; + +// This is where the loader writes the framebuffer offsets +struct framebuffers { + uint32_t *top_left; + uint32_t *top_right; + uint32_t *bottom; +} *framebuffers = (struct framebuffers *)0x23FFFE00; + +#define screen_top_width 400 +#define screen_top_height 240 +#define screen_bottom_width 340 +#define screen_bottom_height 240 +#define screen_top_size (screen_top_width * screen_top_height * 3) +#define screen_bottom_size (screen_bottom_width * screen_bottom_height * 3) + +void memset32(void *dest, uint32_t filler, uint32_t size) +{ + uint32_t *dest32 = (uint32_t *)dest; + for (uint32_t i = 0; i < size / 4; i++) { + dest32[i] = filler; + } +} + +void clear_screens() { + memset32(framebuffers->top_left, 0, screen_top_size); + memset32(framebuffers->top_right, 0, screen_top_size); + memset32(framebuffers->bottom, 0, screen_bottom_size); +} + +// This is supposed to be a small-ish payload that loads a bigger payload. +// This exists because some payloads are too big to be loaded in the memory of some apps. +void main() +{ + FATFS fs; + FIL handle; + unsigned int bytes_read; + + // Mount the SD card + if (f_mount(&fs, "0:", 1) != FR_OK) return; + + // Load the payload + if (f_open(&handle, "/" LAUNCHER_PATH, FA_READ) != FR_OK) return; + if (f_lseek(&handle, payload_offset) != FR_OK) return; + if (f_read(&handle, payload_loc, payload_size, &bytes_read) != FR_OK) return; + if (f_close(&handle) != FR_OK) return; + + // Unmount the SD card + if (f_mount(NULL, "0:", 1) != FR_OK) return; + + // This is mostly to adhere to the gateway "standard", + // so existing gateway payloads don't have to be modified. + // Write the framebuffer offsets to the places where gateway expects them + *(uint32_t **)0x080FFFC0 = framebuffers->top_left; + *(uint32_t **)0x080FFFC4 = framebuffers->top_right; + *(uint32_t **)0x080FFFD4 = framebuffers->bottom; + *(uint32_t *)0x080FFFD8 = 0; // Select top framebuffer? + + // Loaded correctly. The rest is up to the payload. + clear_screens(); + + // Jump to the payload, right behind the interrupt vector table. + ((void (*)())(payload_loc + 0x30))(); +} diff --git a/mset/source/payload/start.s b/mset/source/payload/start.s new file mode 100644 index 0000000..70015b0 --- /dev/null +++ b/mset/source/payload/start.s @@ -0,0 +1,48 @@ +.section .text.start +.align 4 +.global _start +_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 + + @ 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 + + @ Undocumented: Fixes mounting of SDMC + ldr r0, =0x10000020 + mov r1, #0x340 + str r1, [r0] + + bl main + +.die: + b .die diff --git a/mset/source/start.s b/mset/source/start.s new file mode 100644 index 0000000..82107f4 --- /dev/null +++ b/mset/source/start.s @@ -0,0 +1,5 @@ +.section .text.start +.align 4 +.global _start +_start: + b main diff --git a/source/emunand.c b/source/emunand.c new file mode 100644 index 0000000..784b425 --- /dev/null +++ b/source/emunand.c @@ -0,0 +1,40 @@ +/* +* emunand.c +* by Reisyukaku +*/ + +#include "emunand.h" +#include "fatfs/ff.h" +#include "fatfs/sdmmc/sdmmc.h" + +typedef struct emunand { + u32 offset; + u32 header; + const char* name; +} emunand; + +emunand emunands[] = { + {.offset = 1, .header = 1, .name = "redNAND"}, + {.offset = 0, .header = 0x1D7800, .name = "Toshiba GW/MT"}, + {.offset = 0, .header = 0x1DD000, .name = "Samsung GW/MT"}, + {.offset = 0, .header = 0x26C000, .name = "Samsung N3DS GW"}, + {.offset = 0, .header = 0x3B0000, .name = "Unknown N3DS GW"}, + {.offset = 0, .header = 0, .name = 0}, +}; + +static u8 *temp = (u8 *)0x24300000; + +u8 getEmunand(u32 *off, u32 *head){ + u8 ret = 0; + for(int i = 0; emunands[i].name; i++){ + if (sdmmc_sdcard_readsectors(emunands[i].header, 1, temp) == 0) { + if (*(u32 *)(temp + 0x100) == NCSD_MAGIC) { + *off = (u32)&emunands[i].offset; + *head = (u32)&emunands[i].header; + ret = 1; + break; + } + } + } + return ret; +} \ No newline at end of file diff --git a/source/emunand.h b/source/emunand.h new file mode 100644 index 0000000..5a1db07 --- /dev/null +++ b/source/emunand.h @@ -0,0 +1,15 @@ +/* +* emunand.h +* by Reisyukaku +*/ + +#ifndef EMU_INC +#define EMU_INC + +#include "types.h" + +#define NCSD_MAGIC (0x4453434E) + +u8 getEmunand(u32 *off, u32 *head); + +#endif \ No newline at end of file diff --git a/source/fatfs/00readme.txt b/source/fatfs/00readme.txt new file mode 100644 index 0000000..c46ac5f --- /dev/null +++ b/source/fatfs/00readme.txt @@ -0,0 +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. + diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c new file mode 100644 index 0000000..0eb8eb9 --- /dev/null +++ b/source/fatfs/diskio.c @@ -0,0 +1,102 @@ +/*-----------------------------------------------------------------------*/ +/* 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 */ + 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; + } + + 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 diff --git a/source/fatfs/diskio.h b/source/fatfs/diskio.h new file mode 100644 index 0000000..9ca0c7e --- /dev/null +++ b/source/fatfs/diskio.h @@ -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 diff --git a/source/fatfs/ff.c b/source/fatfs/ff.c new file mode 100644 index 0000000..41de2fc --- /dev/null +++ b/source/fatfs/ff.c @@ -0,0 +1,4694 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module 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. +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 32020 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp feature */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File access control feature */ +#if _FS_LOCK +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, directory (0:root) */ + WORD idx; /* Object ID 3, directory index */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS extend character conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +/*#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}*/ + +static +const unsigned short 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 +}; + +WCHAR ff_convert( /* Converted character, Returns zero on error */ +WCHAR src, /* Character code to be converted */ +UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + if(src < 0x80) + { /* ASCII */ + c = src; + } + else + { + if(dir) + { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + } + else + { /* Unicode to OEMCP */ + for(c = 0; c < 0x80; c++) + { + if(src == Tbl[c]) + break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + +WCHAR ff_wtoupper(WCHAR chr) +{ + if(chr >= 'a' && chr <= 'z') + return (chr - 32); + else + return chr; +} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN +#error Cannot use LFN feature without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Name status flags */ +#define NSFLAG 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */ +#define MIN_FAT16 4086U /* Minimum number of clusters as FAT16 */ +#define MIN_FAT32 65526U /* Minimum number of clusters as FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* x86 jump instruction (3) */ +#define BS_OEMName 3 /* OEM name (8) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ +#define BPB_NumFATs 16 /* Number of FAT copies (1) */ +#define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */ +#define BPB_TotSec16 19 /* Volume size [sector] (2) */ +#define BPB_Media 21 /* Media descriptor (1) */ +#define BPB_FATSz16 22 /* FAT size [sector] (2) */ +#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ +#define BPB_NumHeads 26 /* Number of heads (2) */ +#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ +#define BPB_TotSec32 32 /* Volume size [sector] (4) */ +#define BS_DrvNum 36 /* Physical drive number (2) */ +#define BS_BootSig 38 /* Extended boot signature (1) */ +#define BS_VolID 39 /* Volume serial number (4) */ +#define BS_VolLab 43 /* Volume label (8) */ +#define BS_FilSysType 54 /* File system type (1) */ +#define BPB_FATSz32 36 /* FAT size [sector] (4) */ +#define BPB_ExtFlags 40 /* Extended flags (2) */ +#define BPB_FSVer 42 /* File system version (2) */ +#define BPB_RootClus 44 /* Root directory first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ +#define BS_DrvNum32 64 /* Physical drive number (2) */ +#define BS_BootSig32 66 /* Extended boot signature (1) */ +#define BS_VolID32 67 /* Volume serial number (4) */ +#define BS_VolLab32 71 /* Volume label (8) */ +#define BS_FilSysType32 82 /* File system type (1) */ +#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ +#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ +#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ +#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ +#define MBR_Table 446 /* MBR: Partition table offset (2) */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define BS_55AA 510 /* Signature word (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* Lower case flag (1) */ +#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_LstAccDate 18 /* Last accessed date (2) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ +#define DIR_WrtTime 22 /* Modified time (2) */ +#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ +#define DIR_FileSize 28 /* File size (4) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ +#define LDIR_Attr 11 /* LFN attribute (1) */ +#define LDIR_Type 12 /* LFN type (1) */ +#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Must be zero (0) */ +#define SZ_DIRE 32 /* Size of a directory entry */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + + + + +/*------------------------------------------------------------*/ +/* Module private work area */ +/*------------------------------------------------------------*/ +/* Remark: Uninitialized variables with static duration are +/ guaranteed zero/null at start-up. If not, either the linker +/ or start-up routine being used is out of ANSI-C standard. +*/ + +#if _VOLUMES < 1 || _VOLUMES > 9 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non LFN feature */ +#define DEFINE_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() +#else +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN setting +#endif +#if _USE_LFN == 1 /* LFN feature with static working buffer */ +static WCHAR LfnBuf[_MAX_LFN + 1]; +#define DEFINE_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() +#elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ +#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() +#elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ +#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) +#else +#error Wrong _USE_LFN setting +#endif +#endif + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended characters */ +#endif + + + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof (int)) { + *(int*)d = *(int*)s; + d += sizeof (int); s += sizeof (int); + cnt -= sizeof (int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT +static +int lock_fs ( + FATFS* fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS* fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && + res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_LOCK + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + + /* The object has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->fs && + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->fs; + Files[i].clu = dp->sclust; + Files[i].idx = dp->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSINFO sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSINFO structure */ + mem_set(fs->win, 0, SS(fs)); + ST_WORD(fs->win + BS_55AA, 0xAA55); + ST_DWORD(fs->win + FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win + FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win + FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win + FSI_Nxt_Free, fs->last_clust); + /* Write it into the FSINFO sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */ + FATFS* fs, /* File system object */ + DWORD clst /* FAT index number (cluster number) to get the value */ +) +{ + UINT wc, bc; + BYTE *p; + DWORD val; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = clst & 1 ? wc >> 4 : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + val = LD_WORD(p); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val = LD_DWORD(p) & 0x0FFFFFFF; + break; + + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +#if !_FS_READONLY +FRESULT put_fat ( + FATFS* fs, /* File system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc++ % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + fs->wflag = 1; + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + fs->wflag = 1; + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + fs->wflag = 1; + break; + + default : + res = FR_INT_ERR; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_TRIM + DWORD scl = clst, ecl = clst, rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSINFO */ + fs->free_clust++; + fs->fsi_flag |= 1; + } +#if _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous clusters */ + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag |= 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +#if _USE_FASTSEEK +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (!ncl) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} +#endif /* _USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + DIR* dp, /* Pointer to directory object */ + UINT idx /* Index of directory table */ +) +{ + DWORD clst, sect; + UINT ic; + + + dp->index = (WORD)idx; /* Current index */ + clst = dp->sclust; /* Table start cluster (0:root) */ + if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dp->fs->dirbase; + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (idx >= dp->fs->n_rootdir) /* Is index out of range? */ + return FR_INT_ERR; + sect = dp->fs->dirbase; + } + else { /* Dynamic table (root-directory in FAT32 or sub-directory) */ + ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dp->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */ + return FR_INT_ERR; + idx -= ic; + } + sect = clust2sect(dp->fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!sect) return FR_INT_ERR; + dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE); /* Sector# of the directory entry */ + dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Ptr to the entry in the sector */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + UINT i; +#if !_FS_READONLY + UINT c; +#endif + + + i = dp->index + 1; + if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dp->fs) / SZ_DIRE))) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dp->fs, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */ +#if !_FS_READONLY + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */ + clst = create_chain(dp->fs, dp->clust); /* Stretch cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up stretched table */ + if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */ + mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */ + dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */ + for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */ + dp->fs->wflag = 1; + if (sync_window(dp->fs)) return FR_DISK_ERR; + dp->fs->winsect++; + } + dp->fs->winsect -= c; /* Rewind window offset */ +#else + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */ + return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(dp->fs, clst); + } + } + } + + dp->index = (WORD)i; /* Current index */ + dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Current entry in the window */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve directory entry */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT dir_alloc ( + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate (1-21) */ +) +{ + FRESULT res; + UINT n; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); + } + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir /* Pointer to the directory entry */ +) +{ + DWORD cl; + + cl = LD_WORD(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) + cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + BYTE* dir, /* Pointer to the directory entry */ + DWORD cl /* Value to be set */ +) +{ + ST_WORD(dir + DIR_FstClusLO, cl); + ST_WORD(dir + DIR_FstClusHI, cl >> 16); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + + +static +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR* lfnbuf, /* Pointer to the LFN to be compared */ + BYTE* dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last character has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all characters in the entry are checked */ + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE* dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last character has not been processed */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR* lfnbuf, /* Pointer to the LFN buffer */ + BYTE* dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding characters following last character */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR* dp /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dp->lfn) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; /* LFN start order */ + dp->lfn_idx = dp->index; /* Start index of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res; + BYTE a, c, *dir; +#if _USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dp->sect) { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; + dp->lfn_idx = dp->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dp->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; + + return res; +} +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT n, nent; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dp->fn; lfn = dp->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; + } + + if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */ + for (n = 0; lfn[n]; n++) ; + nent = (n + 25) / 13; + } else { /* Otherwise allocate an entry for an SFN */ + nent = 1; + } + res = dir_alloc(dp, nent); /* Allocate entries */ + + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->index - nent); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Sum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); + dp->fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ +#endif + + if (res == FR_OK) { /* Set SFN entry */ + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clean the entry */ + mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */ +#if _USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dp->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT i; + + i = dp->index; /* SFN index */ + res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dp, dp->index); + if (res == FR_OK) { + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +static +void get_fileinfo ( /* No return code */ + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + TCHAR *p, c; + BYTE *dir; +#if _USE_LFN + WCHAR w, *lfn; +#endif + + p = fno->fname; + if (dp->sect) { /* Get SFN */ + dir = dp->dir; + i = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */ +#if _USE_LFN + if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) + c += 0x20; /* To lower */ +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i])) + c = c << 8 | dir[i++]; + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#endif + *p++ = c; + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir + DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir + DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir + DIR_WrtTime); /* Time */ + } + *p = 0; /* Terminate SFN string by a \0 */ + +#if _USE_LFN + if (fno->lfname) { + i = 0; p = fno->lfname; + if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */ + lfn = dp->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (!w) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + p[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ + p[i++] = (TCHAR)w; + } + } + p[i] = 0; /* Terminate LFN string by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ +#if _USE_FIND && _FS_MINIMIZE <= 1 +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ + WCHAR chr; + +#if !_LFN_UNICODE + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ + if (IsDBCS1(chr) && IsDBCS2(**ptr)) /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#else + chr = ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif + return chr; +} + + +static +int pattern_matching ( /* Return value: 0:mismatched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (!pc) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + lfn = dp->lfn; + si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode character */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dp->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN character */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* DBC (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with deleted mark, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended character? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject nul string */ + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NSFLAG] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dp->sclust = 0; /* Strip it and start from the root directory */ + } else { /* No heading separator */ + dp->sclust = dp->fs->cdir; /* Start from the current directory */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dp->sclust = 0; /* Always start from the root directory */ +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + res = dir_sdi(dp, 0); + dp->dir = 0; + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the sagment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */ + dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + res = FR_OK; /* Ended at the root directroy. Function completed. */ + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + dir = dp->dir; /* Follow the sub-directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } + dp->sclust = ld_clust(dp->fs, dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const str[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = str[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) /* Load boot record */ + return 3; + + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + BYTE wmode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number from the path name */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; +#endif + /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ + if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + MBR_Table + i * SZ_PTE; + br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */ + } while (!LD2PT(vol) && fmt && ++i < 4); + } + if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found. Following code initializes the file system object */ + + if (LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */ + return FR_NO_FILESYSTEM; + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */ + return FR_NO_FILESYSTEM; + + fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZ_DIRE)) /* (Must be sector aligned) */ + return FR_NO_FILESYSTEM; + + tsect = LD_WORD(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win + BPB_TotSec32); + + nrsv = LD_WORD(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win + BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->last_clust = fs->free_clust = 0xFFFFFFFF; + + /* Get fsinfo if available */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */ + && LD_WORD(fs->win + BPB_FSInfo) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (LD_WORD(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 + && LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _FS_RPATH + fs->cdir = 0; /* Set current directory to root */ +#endif +#if _FS_LOCK /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + void* obj /* Pointer to the object FIL/DIR to check validity */ +) +{ + FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */ + + + if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id) + return FR_INVALID_OBJECT; + + ENTER_FF(fil->fs); /* Lock file system */ + + if (disk_status(fil->fs->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&fs, &path, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; +#if !_FS_READONLY + DWORD dw, cl; +#endif + + + if (!fp) return FR_INVALID_OBJECT; + fp->fs = 0; /* Clear file object */ + + /* Get logical drive number */ +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = find_volume(&dj.fs, &path, 0); +#endif + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Default directory itself */ + res = FR_INVALID_NAME; +#if _FS_LOCK + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_LOCK + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) /* Cannot create as new file */ + res = FR_EXIST; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = GET_FATTIME(); /* Created time */ + ST_DWORD(dir + DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir + DIR_FileSize, 0);/* size = 0 */ + cl = ld_clust(dj.fs, dir); /* Get start cluster */ + st_clust(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_LOCK + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + dir = dj.dir; + if (!dir) { /* Current directory itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->err = 0; /* Clear error flag */ + fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir + DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* Normal seek mode */ +#endif + fp->fs = dj.fs; /* Validate file object */ + fp->id = fp->fs->id; + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE csect, *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + } + if (clst < 2) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->dsect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = (const BYTE*)buff; + BYTE csect; + + + *bw = 0; /* Clear write byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fptr + btw < fp->fptr) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + if (clst == 0) /* When no cluster is allocated, */ + clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* Write-back sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ + if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector cache with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file change flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tm; + BYTE *dir; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ + /* Write-back dirty buffer */ +#if !_FS_TINY + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir + DIR_FileSize, fp->fsize); /* Update file size */ + st_clust(dir, fp->sclust); /* Update start cluster */ + tm = GET_FATTIME(); /* Update updated time */ + ST_DWORD(dir + DIR_WrtTime, tm); + ST_WORD(dir + DIR_LstAccDate, 0); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync_fs(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if !_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(fp); /* Lock volume */ + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = fp->fs; +#endif +#if _FS_LOCK + res = dec_lock(fp->lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + fp->fs = 0; /* Invalidate file object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = ld_clust(dj.fs, dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEFINE_NAMEBUF; + + + *buff = 0; + /* Get logical drive number */ + res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = len; /* Bottom of buffer (directory stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1); /* Get parent directory */ + if (res != FR_OK) break; + res = dir_read(&dj, 0); + if (res != FR_OK) break; + dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = buff; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + tp = fno.fname; +#if _USE_LFN + if (*buff) tp = buff; +#endif + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = tp[--n]; + buff[--i] = '/'; + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clst, bcs, nsect, ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->sclust; /* Top of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) + *tbl = 0; /* Terminate table */ + else + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fp->fs, fp->clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + } + + LEAVE_FF(fp->fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS* fs; + DEFINE_NAMEBUF; + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive number */ + res = find_volume(&fs, &path, 0); + if (res == FR_OK) { + dp->fs = fs; + INIT_BUF(*dp); + res = follow_path(dp, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dp->dir) { /* It is not the origin directory itself */ + if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */ + dp->sclust = ld_clust(fs, dp->dir); + else /* The object is a file */ + res = FR_NO_PATH; + } + if (res == FR_OK) { + dp->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK + if (res == FR_OK) { + if (dp->sclust) { + dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->lockid) + res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + + + res = validate(dp); + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = dp->fs; +#endif +#if _FS_LOCK + if (dp->lockid) /* Decrement sub-directory open counter */ + res = dec_lock(dp->lockid); + if (res == FR_OK) +#endif + dp->fs = 0; /* Invalidate directory object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEFINE_NAMEBUF; + + + res = validate(dp); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dp); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) { /* Reached end of directory */ + dp->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dp->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dp->fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find next file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ +#if _USE_LFN + if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break; /* Test for LFN if exist */ +#endif + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ + } + return res; + +} + + + +/*-----------------------------------------------------------------------*/ +/* Find first file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) + res = f_findnext(dp, fno); /* Find the first item */ + return res; +} + +#endif /* _USE_FIND */ + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } else { /* It is root directory */ + res = FR_INVALID_NAME; + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get logical drive number */ + res = find_volume(fatfs, &path, 0); + fs = *fatfs; + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if (fs->free_clust <= fs->n_fatent - 2) { + *nclst = fs->free_clust; + } else { + /* Get number of free clusters */ + fat = fs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(fs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < fs->n_fatent); + } else { + clst = fs->n_fatent; + sect = fs->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + fs->free_clust = n; + fs->fsi_flag |= 1; + *nclst = n; + } + } + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->err) { /* Check error */ + res = (FRESULT)fp->err; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->sclust); + fp->sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->n_fatent) { + res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } +#if !_FS_TINY + if (res == FR_OK && (fp->flag & FA__DIRTY)) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + res = FR_DISK_ERR; + else + fp->flag &= ~FA__DIRTY; + } +#endif + } + if (res != FR_OK) fp->err = (FRESULT)res; + } + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + BYTE *dir; + DWORD dclst = 0; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + if (res == FR_OK) { + dclst = ld_clust(dj.fs, dir); + if (dclst && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-directory ? */ +#if _FS_RPATH + if (dclst == dj.fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + mem_cpy(&sdj, &dj, sizeof (DIR)); /* Open the sub-directory */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item (excluding dot entries) */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? (cannot remove) */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? (can remove) */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync_fs(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tm = GET_FATTIME(); + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = sync_window(dj.fs); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir + DIR_WrtTime, tm); + st_clust(dir, dcl); + mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); /* Create ".." entry */ + dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + st_clust(dir + SZ_DIRE, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = sync_window(dj.fs); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir + DIR_WrtTime, tm); /* Created time */ + st_clust(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEFINE_NAMEBUF; + + + /* Get logical drive number of the source object */ + res = find_volume(&djo.fs, &path_old, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */ + res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */ + else + res = FR_INVALID_DRIVE; + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { +/* Start of critical section where any interruption can cause a cross-link */ + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djo.fs, dw); + dir = djo.fs->win + SZ_DIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(dir, djn.sclust); + djo.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync_fs(djo.fs); + } +/* End of critical section */ + } + } + } + } + FREE_BUF(); + } + + LEAVE_FF(djo.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir + DIR_WrtTime, fno->ftime); + ST_WORD(dir + DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + DIR dj; + UINT i, j; +#if _USE_LFN && _LFN_UNICODE + WCHAR w; +#endif + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is exist */ +#if _USE_LFN && _LFN_UNICODE + i = j = 0; + do { + w = (i < 11) ? dj.dir[i++] : ' '; + if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i])) + w = w << 8 | dj.dir[i++]; + label[j++] = ff_convert(w, 1); /* OEM -> Unicode */ + } while (j < 11); +#else + mem_cpy(label, dj.dir, 11); +#endif + j = 11; + do { + label[j] = 0; + if (!j) break; + } while (label[--j] == ' '); + } + if (res == FR_NO_FILE) { /* No label, return nul string */ + label[0] = 0; + res = FR_OK; + } + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(dj.fs, dj.fs->volbase); + if (res == FR_OK) { + i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; + *vsn = LD_DWORD(&dj.fs->win[i]); + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + DIR dj; + BYTE vn[11]; + UINT i, j, sl; + WCHAR w; + DWORD tm; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &label, 1); + if (res) LEAVE_FF(dj.fs, res); + + /* Create a volume label in directory form */ + vn[0] = 0; + for (sl = 0; label[sl]; sl++) ; /* Get name length */ + for ( ; sl && label[sl - 1] == ' '; sl--) ; /* Remove trailing spaces */ + if (sl) { /* Create volume label in directory form */ + i = j = 0; + do { +#if _USE_LFN && _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) + w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; +#if _USE_LFN + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (w >= 0x100) vn[j++] = (BYTE)(w >> 8); + vn[j++] = (BYTE)w; + } while (i < sl); + while (j < 11) vn[j++] = ' '; /* Fill remaining name field */ + if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + + /* Set volume label */ + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is found */ + if (vn[0]) { + mem_cpy(dj.dir, vn, 11); /* Change the volume label name */ + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + } else { + dj.dir[0] = DDEM; /* Remove the volume label */ + } + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } else { /* No volume label is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (vn[0]) { /* Create volume label as new */ + res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZ_DIRE); /* Set volume label */ + mem_cpy(dj.dir, vn, 11); + dj.dir[DIR_Attr] = AM_VOL; + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (available on only tiny cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + BYTE csect; + + + *bf = 0; /* Clear transfer byte counter */ + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->sclust : get_fat(fp->fs, fp->clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + if (move_window(fp->fs, sect) != FR_OK) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btf) rcnt = btf; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create file system on the logical drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ +#define N_FATS 1 /* Number of FATs (1 or 2) */ + + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Size of allocation unit in unit of byte or sector */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + int vol; + BYTE fmt, md, sys, *tbl, pdrv, part; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; +#if _USE_TRIM + DWORD eb[2]; +#endif + + + /* Check mounted drive and clear work area */ + if (sfd > 1) return FR_INVALID_PARAMETER; + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + fs = FatFs[vol]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ + + /* Get disk statics */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != _MIN_SS /* Get disk sector size */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) + return FR_DISK_ERR; +#endif + if (_MULTI_PARTITION && part) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; + if (LD_WORD(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = LD_DWORD(tbl + 8); /* Volume start sector */ + n_vol = LD_DWORD(tbl + 12); /* Volume size */ + } else { + /* Create a partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; /* Volume size */ + } + + if (au & (au - 1)) au = 0; + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ + if (!au) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT sub-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = (DWORD)N_ROOTDIR * SZ_DIRE / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + /* Determine system ID in the partition table */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (fmt == FS_FAT12 && n_vol < 0x10000) { + sys = 0x01; /* FAT12(<65536) */ + } else { + sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + } + } + + if (_MULTI_PARTITION && part) { + /* Update system ID in the partition table */ + tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; + tbl[4] = sys; + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to teh MBR */ + return FR_DISK_ERR; + md = 0xF8; + } else { + if (sfd) { /* No partition table (SFD) */ + md = 0xF0; + } else { /* Create partition table (FDISK) */ + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */ + tbl[1] = 1; /* Partition start head */ + tbl[2] = 1; /* Partition start sector */ + tbl[3] = 0; /* Partition start cylinder */ + tbl[4] = sys; /* System type */ + tbl[5] = 254; /* Partition end head */ + n = (b_vol + n_vol) / 63 / 255; + tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */ + tbl[7] = (BYTE)n; /* End cylinder */ + ST_DWORD(tbl + 8, 63); /* Partition start in LBA */ + ST_DWORD(tbl + 12, n_vol); /* Partition size in LBA */ + ST_WORD(fs->win + BS_55AA, 0xAA55); /* MBR signature */ + if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR */ + return FR_DISK_ERR; + md = 0xF8; + } + } + + /* Create BPB in the VBR */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl + BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl + BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */ + ST_WORD(tbl + BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl + BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl + BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + ST_WORD(tbl + BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl + BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl + BPB_HiddSec, b_vol); /* Hidden sectors */ + n = GET_FATTIME(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl + BS_VolID32, n); /* VSN */ + ST_DWORD(tbl + BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl + BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl + BPB_FSInfo, 1); /* FSINFO record offset (VBR + 1) */ + ST_WORD(tbl + BPB_BkBootSec, 6); /* Backup boot record offset (VBR + 6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl + BS_VolID, n); /* VSN */ + ST_WORD(tbl + BPB_FATSz16, n_fat); /* Number of sectors per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + ST_WORD(tbl + BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR + 6) */ + disk_write(pdrv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl + 4, 0xFFFFFFFF); + ST_DWORD(tbl + 8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */ + } + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : (UINT)n_dir; + do { + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_TRIM /* Erase data area if needed */ + { + eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; + disk_ioctl(pdrv, CTRL_TRIM, eb); + } +#endif + + /* Create FSINFO if needed */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl + FSI_LeadSig, 0x41615252); + ST_DWORD(tbl + FSI_StrucSig, 0x61417272); + ST_DWORD(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + ST_DWORD(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + ST_WORD(tbl + BS_55AA, 0xAA55); + disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */ + disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */ + } + + return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + + + +#if _MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create partition table on the physical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD szt[], /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine CHS in the table regardless of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x06; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + ST_DWORD(p + 8, s_part); /* Start sector in LBA */ + ST_DWORD(p + 12, sz_part); /* Partition size */ + + /* Next partition */ + b_cyl += p_cyl; + } + ST_WORD(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + + +#endif /* _MULTI_PARTITION */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL* fp; + int idx, nchr; + BYTE buf[64]; +} putbuff; + + +static +void putc_bfd ( + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + + i = pb->idx; /* Buffer write index (-1:error) */ + if (i < 0) return; + +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + putc_bfd(&pb, c); /* Put a character */ + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + while (*str) /* Put the string */ + putc_bfd(&pb, *str++); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT nw, i, j, w; + DWORD v; + TCHAR c, d, s[16], *p; + putbuff pb; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + s[i++] = d + '0'; + } while (v && i < sizeof s / sizeof s[0]); + if (f & 8) s[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do putc_bfd(&pb, s[--i]); while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/source/fatfs/ff.h b/source/fatfs/ff.h new file mode 100644 index 0000000..22f1810 --- /dev/null +++ b/source/fatfs/ff.h @@ -0,0 +1,350 @@ +/*---------------------------------------------------------------------------/ +/ 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 */ diff --git a/source/fatfs/ffconf.h b/source/fatfs/ffconf.h new file mode 100644 index 0000000..bd5ffa8 --- /dev/null +++ b/source/fatfs/ffconf.h @@ -0,0 +1,271 @@ +/*---------------------------------------------------------------------------/ +/ 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 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_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. +*/ + diff --git a/source/fatfs/history.txt b/source/fatfs/history.txt new file mode 100644 index 0000000..b101820 --- /dev/null +++ b/source/fatfs/history.txt @@ -0,0 +1,180 @@ +---------------------------------------------------------------------------- + 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) diff --git a/source/fatfs/integer.h b/source/fatfs/integer.h new file mode 100644 index 0000000..f254b2a --- /dev/null +++ b/source/fatfs/integer.h @@ -0,0 +1,33 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#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 diff --git a/source/fatfs/option/syscall.c b/source/fatfs/option/syscall.c new file mode 100644 index 0000000..8e25898 --- /dev/null +++ b/source/fatfs/option/syscall.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------*/ +/* 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 diff --git a/source/fatfs/sdmmc/common.h b/source/fatfs/sdmmc/common.h new file mode 100644 index 0000000..b4ab486 --- /dev/null +++ b/source/fatfs/sdmmc/common.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +#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 diff --git a/source/fatfs/sdmmc/delay.h b/source/fatfs/sdmmc/delay.h new file mode 100644 index 0000000..afad438 --- /dev/null +++ b/source/fatfs/sdmmc/delay.h @@ -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); diff --git a/source/fatfs/sdmmc/delay.s b/source/fatfs/sdmmc/delay.s new file mode 100644 index 0000000..b3baccd --- /dev/null +++ b/source/fatfs/sdmmc/delay.s @@ -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 diff --git a/source/fatfs/sdmmc/sdmmc.c b/source/fatfs/sdmmc/sdmmc.c new file mode 100644 index 0000000..f6a0f97 --- /dev/null +++ b/source/fatfs/sdmmc/sdmmc.c @@ -0,0 +1,467 @@ +// Copyright 2014 Normmatt +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common.h" + +#include "sdmmc.h" +#include "delay.h" + +//Uncomment to enable 32bit fifo support? +//not currently working +//#define DATA32_SUPPORT + +static struct mmcdevice handleNAND; +static struct mmcdevice handleSD; + +mmcdevice *getMMCDevice(int drive) +{ + if(drive==0) return &handleNAND; + return &handleSD; +} + +int __attribute__((noinline)) geterror(struct mmcdevice *ctx) +{ + //if(ctx->error == 0x4) return -1; + //else return 0; + return (ctx->error << 29) >> 31; +} + + +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); + } + +} + + +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); + +#ifdef DATA32_SUPPORT + if (readdata) + sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0x800); + if (writedata) + sdmmc_mask16(REG_SDDATACTL32, 0x800, 0x1000); +#else + sdmmc_mask16(REG_SDDATACTL32,0x1800,0); +#endif + + sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); + sdmmc_write16(REG_SDCMDARG1,args >> 16); + sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + + u32 size = ctx->size; + u16 *dataPtr = (u16*)ctx->data; + +#ifdef DATA32_SUPPORT + u32 *dataPtr32 = (u32*)ctx->data; +#endif + + bool useBuf = ( NULL != dataPtr ); + +#ifdef DATA32_SUPPORT + bool useBuf32 = (useBuf && (0 == (3 & ((u32)dataPtr)))); +#endif + + 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) { +#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; + } + } + } + + 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) { +#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; + } + } + } + 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); + } +} + +int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in) +{ + if (handleSD.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleSD); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); +#endif + + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handleSD.data = in; + handleSD.size = numsectors << 9; + sdmmc_send_command(&handleSD,0x52C19,sector_no); + return geterror(&handleSD); +} + +int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) +{ + if (handleSD.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleSD); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); +#endif + + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + handleSD.data = out; + handleSD.size = numsectors << 9; + sdmmc_send_command(&handleSD,0x33C12,sector_no); + return geterror(&handleSD); +} + + + +int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) +{ + if (handleNAND.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleNAND); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write32(REG_SDBLKCOUNT32,numsectors); +#else + sdmmc_write16(REG_SDBLKCOUNT,numsectors); +#endif + + handleNAND.data = out; + handleNAND.size = numsectors << 9; + sdmmc_send_command(&handleNAND,0x33C12,sector_no); + inittarget(&handleSD); + return geterror(&handleNAND); +} + +int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in) //experimental +{ + if (handleNAND.isSDHC == 0) + sector_no <<= 9; + inittarget(&handleNAND); + sdmmc_write16(REG_SDSTOP,0x100); + +#ifdef DATA32_SUPPORT + sdmmc_write32(REG_SDBLKCOUNT32,numsectors); +#else + sdmmc_write16(REG_SDBLKCOUNT,numsectors); +#endif + + handleNAND.data = in; + handleNAND.size = numsectors << 9; + sdmmc_send_command(&handleNAND,0x52C19,sector_no); + inittarget(&handleSD); + return geterror(&handleNAND); +} + +u32 calcSDSize(u8* csd, int type) +{ + u32 result = 0; + u8 temp = csd[0xE]; + //int temp3 = type; + + switch (type) { + case -1: + type = temp >> 6; + break; + case 0: + { + u32 temp = (csd[0x7] << 0x2 | csd[0x8] << 0xA | csd[0x6] >> 0x6 | (csd[0x9] & 0xF) << 0x10) & 0xFFF; + u32 temp2 = temp * (1 << (csd[0x9] & 0xF)); + u32 retval = temp2 * (1 << (((csd[0x4] >> 7 | csd[0x5] << 1) & 7) + 2)); + result = retval >> 9; + break; + } + case 1: + result = (((csd[0x7] & 0x3F) << 0x10 | csd[0x6] << 8 | csd[0x5]) + 1) << 0xA; + break; + default: + result = 0; + break; + } + + return result; +} + +void InitSD() +{ + //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; + + //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); + + *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 + *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 +#ifdef DATA32_SUPPORT + *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 +#else + *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 +#endif + *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; +#ifdef DATA32_SUPPORT + *(vu16*)0x10006100 &= 0xFFFFu; //SDDATACTL32 + *(vu16*)0x100060D8 &= 0xFFDFu; //SDDATACTL + *(vu16*)0x10006104 = 512; //SDBLKLEN32 +#else + *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 + *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL + *(vu16*)0x10006104 = 0; //SDBLKLEN32 +#endif + *(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 +#ifdef DATA32_SUPPORT + *(vu16*)0x10006024 = 0x20; + *(vu16*)0x10006028 = 0x40EE; +#else + *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 + *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE +#endif + *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL + *(vu16*)0x10006026 = 512; //SDBLKLEN + *(vu16*)0x10006008 = 0; //SDSTOP + + inittarget(&handleSD); +} + +int Nand_Init() +{ + 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 SD_Init() +{ + inittarget(&handleSD); + //ioDelay(0x3E8); + ioDelay(0xF000); + 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); + //do + //{ + // sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); + // sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); + // + //} + //while(!(handleSD.ret[0] & 0x80000000)); + + if(!((handleSD.ret[0] >> 30) & 1) || !temp) + temp2 = 0; + + handleSD.isSDHC = temp2; + //handleSD.isSDHC = (handleSD.ret[0] & 0x40000000); + + 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(); + Nand_Init(); + SD_Init(); +} diff --git a/source/fatfs/sdmmc/sdmmc.h b/source/fatfs/sdmmc/sdmmc.h new file mode 100644 index 0000000..bc067cb --- /dev/null +++ b/source/fatfs/sdmmc/sdmmc.h @@ -0,0 +1,171 @@ +// 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 { + u8* 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; + +/*int sdmmc_sdcard_init(); +void sdmmc_sdcard_readsector(uint32_t sector_no, void *out); +void sdmmc_sdcard_readsectors(uint32_t sector_no, uint32_t numsectors, void *out); +void sdmmc_sdcard_writesector(uint32_t sector_no, void *in); +void sdmmc_sdcard_writesectors(uint32_t sector_no, uint32_t numsectors, void *in); +void sdmmc_blktransferinit();*/ + +void sdmmc_sdcard_init(); +int sdmmc_sdcard_readsector(u32 sector_no, u8 *out); +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_sdcard_writesector(u32 sector_no, u8 *in); +int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, u8 *in); + +int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, u8 *in); + +mmcdevice *getMMCDevice(int drive); + +void InitSDMMC(); +int Nand_Init(); +int SD_Init(); + +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); +} diff --git a/source/firm.c b/source/firm.c new file mode 100644 index 0000000..0b020ad --- /dev/null +++ b/source/firm.c @@ -0,0 +1,90 @@ +/* +* firm.c +* by Reisyukaku +*/ + +#include "firm.h" +#include "patches.h" +#include "memory.h" +#include "types.h" +#include "fs.h" +#include "emunand.h" + +firmHeader *firmLocation = (firmHeader *)0x24000000; +const u32 firmSize = 0xF1000; +firmSectionHeader *section; + +void loadSplash(void){ + fileRead((u8 *)0x20047000, "/rei/splash.bin", 0x46500); //ghetto because address varies i think + while(((~*(unsigned *)0x10146000) & 0xFFF) == (1 << 3) ? 0 : 1); //Press start to boot +} + +void loadFirm(void){ + //Read FIRM from SD card and write to FCRAM + fileRead((u8*)firmLocation, "/rei/firmware.bin", firmSize); + section = firmLocation->section; +} + +void patchFirm(void){ + //Part1: Add emunand parsing code + u32 offset = 0; + u32 header = 0; + if(getEmunand(&offset, &header) == 1){ + fileRead((u8*)emuCode, "/rei/emunand/emunand.bin", 0); + u32 *pos_offset = memsearch((u8*)emuCode, "NAND", 0x218, 4); + u32 *pos_header = memsearch((u8*)emuCode, "NCSD", 0x218, 4); + memcpy((void *)pos_offset, (void *)offset, 4); + memcpy((void *)pos_header, (void *)header, 4); + } + //Part2: Add emunand hooks + memcpy((u8*)emuHook1, eh1, sizeof(eh1)); + memcpy((u8*)emuHook2, eh2, sizeof(eh2)); + memcpy((u8*)emuHook3, eh3, sizeof(eh3)); + memcpy((u8*)emuHook4, eh4, sizeof(eh4)); + + //Part3: Disable signature checks + memcpy((u8*)patch1, p1, sizeof(p1)); + memcpy((u8*)patch2, p2, sizeof(p2)); + + //Part4: Create arm9 thread + fileRead((u8*)threadCode, "/rei/thread/arm9.bin", 0); + memcpy((u8*)threadHook1, th1, sizeof(th1)); + memcpy((u8*)threadHook2, th2, sizeof(th2)); +} + +void launchFirm(void){ + //Set MPU + __asm__ ( + "msr cpsr_c, #0xDF\n\t" + "ldr r0, =0x10000035\n\t" + "ldr r4, =0x18000035\n\t" + "mcr p15, 0, r0, c6, c3, 0\n\t" + "mcr p15, 0, r4, c6, c4, 0\n\t" + "mrc p15, 0, r0, c2, c0, 0\n\t" + "mrc p15, 0, r4, c2, c0, 1\n\t" + "mrc p15, 0, r1, c3, c0, 0\n\t" + "mrc p15, 0, r2, c5, c0, 2\n\t" + "mrc p15, 0, r3, c5, c0, 3\n\t" + "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" + "mcr p15, 0, r4, c2, c0, 1\n\t" + "mcr p15, 0, r1, c3, c0, 0\n\t" + "mcr p15, 0, r2, c5, c0, 2\n\t" + "mcr p15, 0, r3, c5, c0, 3\n\t" + ::: "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 + ((void (*)())0x801B01C)(); +} \ No newline at end of file diff --git a/source/firm.h b/source/firm.h new file mode 100644 index 0000000..59f2ee2 --- /dev/null +++ b/source/firm.h @@ -0,0 +1,13 @@ +/* +* firm.h +* by Reisyukaku +*/ +#ifndef FIRM_INC +#define FIRM_INC + +void loadSplash(void); +void loadFirm(void); +void patchFirm(void); +void launchFirm(void); + +#endif \ No newline at end of file diff --git a/source/font.h b/source/font.h new file mode 100644 index 0000000..fb86a6f --- /dev/null +++ b/source/font.h @@ -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_ +//--------------------------------------------------------------------------------- diff --git a/source/fs.c b/source/fs.c new file mode 100644 index 0000000..9e59e98 --- /dev/null +++ b/source/fs.c @@ -0,0 +1,72 @@ +/* +* fs.c +*/ + +#include +#include "fs.h" +#include "fatfs/ff.h" + +static FATFS fs; + +int mountSD() +{ + if (f_mount(&fs, "0:", 1) != FR_OK) { + //printF("Failed to mount SD card!"); + return 1; + } + //printF("Mounted SD card"); + return 0; +} + +int unmountSD() +{ + 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; + u32 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; + } + + 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; +} + +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){ + FRESULT fr; + FIL fp; + u32 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; +} \ No newline at end of file diff --git a/source/fs.h b/source/fs.h new file mode 100644 index 0000000..0bee0e0 --- /dev/null +++ b/source/fs.h @@ -0,0 +1,16 @@ +/* +* fs.h +*/ + +#ifndef __fs_h__ +#define __fs_h__ + +#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); + +#endif diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..db9c386 --- /dev/null +++ b/source/main.c @@ -0,0 +1,18 @@ +/* +* main.c +* by Reisyukaku +* +* Minimalist CFW for N3DS +*/ + +#include "fs.h" +#include "firm.h" + +int main(){ + mountSD(); + loadSplash(); + loadFirm(); + patchFirm(); + launchFirm(); + return 0; +} \ No newline at end of file diff --git a/source/memory.c b/source/memory.c new file mode 100644 index 0000000..710ffcf --- /dev/null +++ b/source/memory.c @@ -0,0 +1,32 @@ +/* +* memory.c +* by Reisyukaku +*/ +#include "memory.h" + +void memcpy(u8 *dest, u8 *src, u32 size){ + for (u32 i = 0; i < size; i++) dest[i] = src[i]; +} + +void memcpy32(u32 *dest, u32 *src, u32 size){ + for (u32 i = 0; i < size; i++) dest[i] = src[i]; +} + +void memset(u8 *dest, u32 fill, u32 size){ + for (u32 i = 0; i < size; i++) dest[i] = fill; +} + +int memcmp(u8 *buf1, u8 *buf2, u32 size){ + for (u32 i = 0; i < size; i++) { + int cmp = buf1[i] - buf2[i]; + if (cmp != 0) return cmp; + } + return 0; +} + +u32 *memsearch(u8 *start_pos, u8 *search, u32 size, u32 size_search){ + for (u8 *pos = start_pos; pos <= start_pos + size - size_search; pos++) { + if (memcmp(pos, search, size_search) == 0) return (u32*)pos; + } + return NULL; +} \ No newline at end of file diff --git a/source/memory.h b/source/memory.h new file mode 100644 index 0000000..b5526e4 --- /dev/null +++ b/source/memory.h @@ -0,0 +1,16 @@ +/* +* memory.h +* by Reisyukaku +*/ +#ifndef MEM_INC +#define MEM_INC + +#include "types.h" + +void memcpy(u8 *dest, u8 *src, u32 size); +void memcpy32(u32 *dest, u32 *src, u32 size); +void memset(u8 *dest, u32 fill, u32 size); +int memcmp(u8 *buf1, u8 *buf2, u32 size); +u32 *memsearch(u8 *start_pos, u8 *search, u32 size, u32 size_search); + +#endif \ No newline at end of file diff --git a/source/patches.h b/source/patches.h new file mode 100644 index 0000000..c22a35f --- /dev/null +++ b/source/patches.h @@ -0,0 +1,62 @@ +/* +* patches.h +* by Reisyukaku +*/ +#ifndef PATCHES_INC +#define PATCHES_INC + +#include "types.h" + +#define FIRM 0x24000000 +#define KERNEL9 (FIRM + 0x66A00) +#define PROC9 (FIRM + 0x7D700) + +#define K9_ADDR 0x08006000 +#define P9_ADDR 0x08028000 + +/* +* Emunand +*/ +//Addresses to patch +u32 emuCode = KERNEL9 + (0x0801A4C0 - K9_ADDR); +u32 emuHook1 = KERNEL9 + (0x0801B3D4 - K9_ADDR); +u32 emuHook2 = PROC9 + (0x080282F8 - P9_ADDR); +u32 emuHook3 = PROC9 + (0x0807877E - P9_ADDR); +u32 emuHook4 = PROC9 + (0x080787BE - P9_ADDR); +//Patches +u8 eh1[0x2C] = { + 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 + }; +u8 eh2[0x14] = { + 0x03, 0x4A, 0x05, 0x21, 0x25, 0x20, 0x2F, 0xF0, 0xAB, 0xF8, 0x37, 0xF0, 0x6F, 0xFB, 0x70, 0xBD, + 0xC8, 0xA6, 0x01, 0x08 + }; //Sets Slot0x25KeyX +u8 eh3[0x0A] = {0x01, 0x4C, 0x20, 0x47, 0x00, 0x00, 0xC0, 0xA4, 0x01, 0x08}; //Branch to first emunand function +u8 eh4[0x0A] = {0x01, 0x4C, 0x20, 0x47, 0x00, 0x00, 0xB0, 0xA5, 0x01, 0x08}; //Branch to second emunand function + + +/* +* Sig checks +*/ +//Addresses to patch +u32 patch1 = PROC9 + (0x08063374 - P9_ADDR); +u32 patch2 = PROC9 + (0x0805D498 - P9_ADDR); +//Patches +u8 p1[2] = {0x00, 0x20}; +u8 p2[4] = {0x00, 0x20, 0x70, 0x47}; + + +/* +* Arm9 thread +*/ +//Addresses to patch +u32 threadCode = KERNEL9 + (0x0801A6E0 - K9_ADDR); +u32 threadHook1 = PROC9 + (0x080860B0 - P9_ADDR); +u32 threadHook2 = PROC9 + (0x080860E4 - P9_ADDR); +//Patches +u8 th1[4] = {0x2C, 0xF0, 0x9F, 0xE5}; +u8 th2[4] = {0xE0, 0xA6, 0x01, 0x08}; + +#endif \ No newline at end of file diff --git a/source/start.s b/source/start.s new file mode 100644 index 0000000..6ceab5f --- /dev/null +++ b/source/start.s @@ -0,0 +1,48 @@ +.section .text.start +.align 4 +.global _start +_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 + + @ 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 + + @ Undocumented: Fixes mounting of SDMC + ldr r0, =0x10000020 + mov r1, #0x340 + str r1, [r0] + + bl main + +.die: + b .die diff --git a/source/types.h b/source/types.h new file mode 100644 index 0000000..d0afc77 --- /dev/null +++ b/source/types.h @@ -0,0 +1,35 @@ +/* +* types.h +* by Reisyukaku +*/ +#ifndef TYPES_INC +#define TYPES_INC + +#include +#include + +//Common data types +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 \ No newline at end of file diff --git a/thread/3ds.ld b/thread/3ds.ld new file mode 100644 index 0000000..fc4ba24 --- /dev/null +++ b/thread/3ds.ld @@ -0,0 +1,24 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +/*0x080C3EE0*/ +ENTRY(_start) + +SECTIONS +{ + . = 0x0801A6E0; + start_addr = .; + .text.start : { *(.text.start) } + .text : { *(.text) *(.text*) } + .rodata : { *(.rodata) *(.rodata*) } + .data : { *(.data) *(.data*) } + .bss : { *(.bss) *(.bss*) } + /*. = ALIGN(32);*/ + /*.stack : { + stack_start = .; + . += 0x40; + . = ALIGN(32); + stack_end = .; + }*/ + total_size = . - start_addr; +} \ No newline at end of file diff --git a/thread/Makefile b/thread/Makefile new file mode 100644 index 0000000..34ffc88 --- /dev/null +++ b/thread/Makefile @@ -0,0 +1,16 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +SFLAGS=-c -mcpu=arm946e-s -march=armv5te -mlittle-endian -fshort-wchar +CFLAGS=$(SFLAGS) -std=c99 + +all: + $(CC) -g source/thread.c source/lib.c $(CFLAGS) + $(CC) -g source/_start.s source/FS.S -I source $(SFLAGS) + $(CC) -nostdlib -T 3ds.ld _start.o thread.o lib.o FS.o + $(OBJCOPY) -O binary a.out arm9.bin + rm -f *.o *.out + \ No newline at end of file diff --git a/thread/source/FS.h b/thread/source/FS.h new file mode 100644 index 0000000..f5261c2 --- /dev/null +++ b/thread/source/FS.h @@ -0,0 +1,9 @@ +#ifndef FS_H +#define FS_H +#include +extern unsigned int fopen9(void *handle, wchar_t* name, unsigned int flag); +extern void fwrite9(void* handle, unsigned int* bytesWritten, void* dst, unsigned int size); +extern void fread9(void* handle, unsigned int* bytesRead, void *src, unsigned int size); +extern void fclose9(void *handle); + +#endif \ No newline at end of file diff --git a/thread/source/FS.s b/thread/source/FS.s new file mode 100644 index 0000000..6f23e46 --- /dev/null +++ b/thread/source/FS.s @@ -0,0 +1,41 @@ +.text + +.thumb +.global fopen9 +.type fopen9, %function + fopen9: + push {r0-r6, lr} + ldr r4, =0x0805B015 + blx r4 + pop {r0-r6, pc} +.pool + +.thumb +.global fwrite9 +.type fwrite9, %function + fwrite9: + push {r4, lr} + ldr r4, =0x0805C379 + blx r4 + pop {r4, pc} +.pool + +.thumb +.global fread9 +.type fread9, %function + fread9: + push {r4, lr} + ldr r4, =0x0804D855 + blx r4 + pop {r4, pc} +.pool + +.thumb +.global fclose9 +.type fclose9, %function + fclose9: + push {r4, lr} + ldr r4, =0x08053C6D + blx r4 + pop {r4, pc} +.pool diff --git a/thread/source/_start.s b/thread/source/_start.s new file mode 100644 index 0000000..8955bcc --- /dev/null +++ b/thread/source/_start.s @@ -0,0 +1,21 @@ +.arm +.global thread +.global _start +_start: + push {r0-r12 , lr} + ldr r0, =0x08000c00 + mov r1, #0xA00 + mov r2, #0x0 + thread_stack_loop: + str r2, [r0], #0x4 + subs r1, r1, #4 + bgt thread_stack_loop + mov r0, #0x3F @ thread priority + ldr r1, =thread @ thread_addr + mov r2, #0x0 @ arg + ldr r3, =0x08000c00 @ StackTop + ldr r4, =0x1 + svc 0x8 + pop {r0-r12 , lr} + ldr r0, =0x80CB0A8 + ldr pc, =0x080860B4 \ No newline at end of file diff --git a/thread/source/lib.c b/thread/source/lib.c new file mode 100644 index 0000000..176e452 --- /dev/null +++ b/thread/source/lib.c @@ -0,0 +1,36 @@ +#include "lib.h" + +void *memset(void * ptr, int value, unsigned int num){ + unsigned char *p = ptr; + while (num) { + *p++ = value; + num--; + } + return ptr; +} + +int strcomp(char* s1, char* s2, unsigned int size){ + for(int i = 0; i < size; i++){ + if(s1[i] != s2[i]) return 0; + } + return 1; +} + +void strcopy(char* dest, char* source, unsigned int size){ + for(int i = 0; i < size*2; i++) dest[i] = source[i]; +} + +int memcmp(void* buf1, void* buf2, int size){ + int equal = 0; + for(int i = 0; i < size; i++){ + if(*((unsigned char*)buf1 + i) != *((unsigned char*)buf2 + i)){ + equal = i; + break; + } + } + return equal; +} + +unsigned isPressed(unsigned bitfield){ + return ((~*(unsigned *)0x10146000) & 0xFFF) == (bitfield & 0xFFF) ? 1 : 0; +} \ No newline at end of file diff --git a/thread/source/lib.h b/thread/source/lib.h new file mode 100644 index 0000000..ed2204d --- /dev/null +++ b/thread/source/lib.h @@ -0,0 +1,25 @@ +#ifndef LIB_H +#define LIB_H + +#define BUTTON_A (1 << 0) +#define BUTTON_B (1 << 1) +#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 BUTTON_R1 (1 << 8) +#define BUTTON_L1 (1 << 9) +#define BUTTON_X (1 << 10) +#define BUTTON_Y (1 << 11) +#define BUTTON_ZL (1 << 14) +#define BUTTON_ZR (1 << 15) + +void* memset(void * ptr, int value, unsigned int num); +int strcomp(char* s1, char*s2, unsigned int size); +void strcopy(char* dest, char* source, unsigned int size); +int memcmp(void* buf1, void* buf2, int size); +unsigned isPressed(unsigned bitfield); + +#endif \ No newline at end of file diff --git a/thread/source/thread.c b/thread/source/thread.c new file mode 100644 index 0000000..d75471b --- /dev/null +++ b/thread/source/thread.c @@ -0,0 +1,87 @@ +/* +* thread.c +* by Reisyukaku +*/ + +#include +#include +#include "thread.h" +#include "lib.h" +#include "FS.h" + +#define VRAM (unsigned char*)0x18000000 +#define FCRAM (unsigned char*)0x20000000 +#define FCRAM_EXT (unsigned char*)0x28000000 +#define ARM9_MEM (unsigned char*)0x8000000 +#define AXIWRAM (unsigned char*)0x1FF80000 +#define TOP_FRAME 0 +#define BOT_FRAME 1 + +unsigned char handle[32]; +unsigned char bmpHead[] = { + 0x42, 0x4D, 0x36, 0x65, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x65, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void memdump(void* filename, void* buf, unsigned int size){ + unsigned int br = 0; + memset(&handle, 0, 32); + fopen9(&handle, filename, 6); + fwrite9(&handle, &br, buf, size); + fclose9(&handle); + memset(VRAM+0x1E6000, 0xFF, 0x46500); +} + +void transpose (void * dst, const void * src, unsigned dim1, unsigned dim2, unsigned item_length) { + char * ptr_write; + const char * ptr_read; + unsigned x, y, z; + for (x = 0; x < dim1; x ++) for (y = 0; y < dim2; y ++) { + ptr_write = ((char *) dst) + item_length * (y * dim1 + x); + ptr_read = ((const char *) src) + item_length * (x * dim2 + y); + for (z = 0; z < item_length; z ++) *(ptr_write ++) = *(ptr_read ++); + } +} + +void screenShot(int frame){ + unsigned int br; + short width = frame == 0 ? 400 : 320; + short height = 240; + int frameOff = frame == 0 ? 0x1E6000 : 0x48F000; //<- Defaults + int length = frame == 0 ? 0x46500 : 0x38400; + memset(&handle, 0, 32); + fopen9(&handle, frame == 0 ? L"sdmc:/screen_top.bmp" : L"sdmc:/screen_bot.bmp", 6); + transpose(FCRAM+0xF80000, VRAM+frameOff, width, height, 3); + bmpHead[18] = frame == 0 ? 0x90 : 0x40; + fwrite9(&handle, &br, bmpHead, 0x36); + fwrite9(&handle, &br, FCRAM+0xF80000, length); + fclose9(&handle); + memset(VRAM+frameOff, 0xFF, 0x46500); +} + +void patches(void){ + //Change version string + for(int i = 0; i < 0x600000; i+=4){ + if(strcomp((void*)0x27B00000 - i, (void*)L"Ver.", 4)) strcopy((void*)0x27B00000 - i, (void*)L"\uE024Rei", 4); + } +} + +void thread(void){ + while(1){ + if(isPressed(BUTTON_SELECT | BUTTON_X)){ + screenShot(TOP_FRAME); + screenShot(BOT_FRAME); + } + if(isPressed(BUTTON_START)){ + memdump(L"sdmc:/AXIWRAM.bin", AXIWRAM, 0x00080000); + memdump(L"sdmc:/FCRAM.bin", FCRAM, 0x010000000); + } + patches(); + } + __asm("SVC 0x09"); +} diff --git a/thread/source/thread.h b/thread/source/thread.h new file mode 100644 index 0000000..5c47aec --- /dev/null +++ b/thread/source/thread.h @@ -0,0 +1,6 @@ +/* +* thread.h +* by Reisyukaku +*/ + +void thread(void); \ No newline at end of file