diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b91dba..a2f8410 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,12 @@ file(GLOB_RECURSE SOURCE_FILES file(GLOB_RECURSE SOURCE_FILES_ARM9 source/arm9/source/* ) +file(GLOB_RECURSE SOURCE_FILES_SCREENINIT + screeninit/source/* +) +file(GLOB_RECURSE SOURCE_FILES_LOADER + loader/source/* +) # Filter file(GLOB FILTER_FILES @@ -59,6 +65,30 @@ add_netload_target(CtrBootManager_3dslink CtrBootManager) add_citra_target(CtrBootManager_Citra CtrBootManager) #set_target_properties(CtrBootManager PROPERTIES COMPILE_FLAGS "-DCITRA") +################### +# Screen init +################### +add_executable(screeninit ${SOURCE_FILES_SCREENINIT}) +set(ASFLAGS "-mcpu=mpcore -mfloat-abi=hard") +set(CFLAGS "-Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math") +set_target_properties(screeninit PROPERTIES COMPILE_FLAGS ${CFLAGS}) +set_source_files_properties(screeninit/source/start.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp ${CFLAGS}") +set_target_properties(screeninit PROPERTIES LINK_FLAGS + "-nostdlib -nostartfiles -g -mthumb -mthumb-interwork -Wl,-Map,screeninit.map -T ${CMAKE_SOURCE_DIR}/screeninit/linker.ld ") +add_a9lh_target(screeninit_a9lh screeninit) + +################### +# FIRM loader +################### +add_executable(loader ${SOURCE_FILES_LOADER}) +set(ASFLAGS "-mcpu=mpcore -mfloat-abi=hard") +set(CFLAGS "-Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math") +set_target_properties(loader PROPERTIES COMPILE_FLAGS ${CFLAGS}) +set_source_files_properties(loader/source/start.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp ${CFLAGS}") +set_target_properties(loader PROPERTIES LINK_FLAGS + "-nostdlib -nostartfiles -g -mthumb -mthumb-interwork -Wl,-Map,loader.map -T ${CMAKE_SOURCE_DIR}/loader/linker.ld ") +add_a9lh_target(loader_a9lh loader) + ################### # CtrBootManager9 ################### diff --git a/README.md b/README.md index 5a33881..a6933ab 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,28 @@ CtrBootManager is a boot manager for some 3DS homebrew applications, like HBL and CFW. ##Dependencies -- devkitpro-4.5 at least (ctrulib-1.0 at least) +- devkitpro-4.5 at least (ctrulib-1.2.1 at least) - libconfig in portlibs (tried with v1.5) ##Building + ###The recommended way: 1. `mkdir build/` 2. `cd build/` 3. `cmake -DCMAKE_TOOLCHAIN_FILE=../DevkitArm3DS.cmake ../` - 4. `cmake --build . --target CtrBootManager_a9lh` + 4. `cmake --build . --target screeninit_a9lh` + 5. `bin2h -c screeninit < screeninit.bin > screeninit.h` + 6. `cmake --build . --target chainloader_a9lh` + 7. `bin2h -c chainloader < chainloader.bin > chainloader.h` + 8. `cmake --build . --target CtrBootManager_a9lh` Binaries should now be in the `build` folder. ##Credits + +###To original developer: + * Cpasjuste + ###For contributions to hb_menu: * smea : code * GEMISIS : code @@ -25,6 +34,23 @@ Binaries should now be in the `build` folder. * Arkhandar : graphics * dotjasp : graphics (regionfree icon) +###For screeninit code: + * Aurora Wright + * dark_samus + * bil1s + * Normmatt + * delebile + * tiniVi + +###For FIRM loader and CtrNAND reading code: + * Aurora Wright + * TuxSH + +###For movies support code example: + * Wolfvak + * d0k3 : compressed movies + ###Other credits: + Readme by gemarcano -This project also uses [Brahma](https://github.com/patois/Brahma). See its repository for its credits. \ No newline at end of file +This project also uses [Brahma](https://github.com/patois/Brahma). See its repository for its credits. diff --git a/build/chainloader.h b/build/chainloader.h new file mode 100644 index 0000000..2a4b7fd --- /dev/null +++ b/build/chainloader.h @@ -0,0 +1,12 @@ +const char chainloader[]={ +0x00,0xd0,0x1f,0xe5,0x45,0x00,0x00,0xea,0x00,0xb8,0xff,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0xa0,0xe3,0x00,0x00,0xa0,0xe3,0x00,0x20,0x81,0xe1,0x5e,0x2f,0x07,0xee,0x20 +,0x00,0x80,0xe2,0x01,0x0b,0x50,0xe3,0xfa,0xff,0xff,0x1a,0x01,0x11,0x81,0xe2,0x00,0x00,0x51,0xe3,0xf6,0xff,0xff,0x1a,0x9a,0x1f,0x07,0xee,0x15,0x1f,0x07,0xee,0x1e +,0xff,0x2f,0xe1,0xf0,0xb5,0xcf,0xb0,0x0f,0x00,0x69,0x46,0x00,0x28,0x12,0xdd,0x8b,0x26,0x3d,0x68,0x00,0x23,0x08,0xac,0x76,0x00,0x03,0xe0,0xe2,0x54,0x01,0x33,0xb3 +,0x42,0x02,0xd0,0xea,0x5c,0x00,0x2a,0xf8,0xd1,0x00,0x22,0x00,0x94,0xe2,0x54,0x69,0x46,0x02,0x28,0x18,0xd0,0x14,0x4e,0x14,0x4f,0xb5,0x68,0x73,0x68,0x32,0x68,0x00 +,0x2d,0x09,0xd0,0x12,0x4c,0xa4,0x46,0x5d,0x19,0x62,0x44,0x14,0x78,0x1c,0x70,0x01,0x33,0x01,0x32,0xab,0x42,0xf9,0xd1,0x30,0x36,0xbe,0x42,0xed,0xd1,0x0c,0x4a,0x93 +,0x68,0xd2,0x68,0x00,0xf0,0x38,0xf8,0x79,0x68,0x13,0x00,0x02,0xaa,0xcc,0x5c,0xd4,0x54,0x01,0x33,0x0c,0x2b,0xfa,0xd1,0xcc,0x5c,0xd4,0x54,0x01,0x33,0x18,0x2b,0xfa +,0xd1,0x69,0x46,0x01,0x92,0xd6,0xe7,0x40,0x10,0x00,0x20,0x00,0x11,0x00,0x20,0x00,0x10,0x00,0x20,0x00,0x40,0xa0,0xe1,0x01,0x50,0xa0,0xe1,0x02,0x60,0xa0,0xe1,0x03 +,0x70,0xa0,0xe1,0xc9,0xff,0xff,0xeb,0x10,0x0f,0x11,0xee,0x01,0x0a,0xc0,0xe3,0x04,0x00,0xc0,0xe3,0x01,0x00,0xc0,0xe3,0x10,0x0f,0x01,0xee,0x02,0x02,0xa0,0xe3,0x04 +,0x70,0x00,0xe5,0x04,0x00,0xa0,0xe1,0x05,0x10,0xa0,0xe1,0x00,0x20,0x1f,0xe5,0x16,0xff,0x2f,0xe1,0xef,0xbe,0x01,0x00,0x78,0x47,0xc0,0x46,0xec,0xff,0xff,0xea,0x00 +,0xc0,0x9f,0xe5,0x1c,0xff,0x2f,0xe1,0x45,0x90,0xff,0x01,0x00,0x00,0x00,0x00}; +const int chainloader_size=304; diff --git a/build/screeninit.h b/build/screeninit.h new file mode 100644 index 0000000..4c56f47 --- /dev/null +++ b/build/screeninit.h @@ -0,0 +1,32 @@ +const char screeninit[]={ +0xc0,0x01,0x0c,0xf1,0x01,0x00,0x00,0xea,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x9f,0xe5,0x1c,0xff,0x2f,0xe1,0x21,0xfc,0xff,0x24,0x00,0x00,0x00,0x00,0x30 +,0xb5,0x85,0xb0,0x69,0x46,0x81,0x4a,0x31,0xca,0x31,0xc1,0x12,0x68,0x80,0x48,0x0a,0x60,0x80,0x49,0x80,0x4a,0x12,0x68,0x08,0x60,0x01,0x20,0x6b,0x46,0x7f,0x49,0x7f +,0x4c,0x08,0x60,0x7f,0x4d,0x21,0x68,0x29,0x40,0x21,0x60,0x92,0x00,0xd4,0x58,0x7d,0x49,0x0c,0x60,0xd2,0x58,0x7d,0x4b,0x1a,0x60,0x7d,0x4b,0x7d,0x4a,0x13,0x60,0x7d +,0x4a,0x13,0x60,0xe1,0x22,0x7d,0x4b,0x52,0x00,0x1a,0x60,0x7c,0x4b,0xf1,0x3a,0x1a,0x60,0xc2,0x23,0x7b,0x49,0xff,0x33,0x0b,0x60,0x7b,0x49,0x0b,0x60,0x00,0x23,0xcf +,0x24,0x7a,0x49,0x0b,0x60,0x7a,0x49,0x0c,0x60,0x7a,0x49,0x0a,0x60,0x7a,0x49,0x7a,0x4a,0x11,0x60,0x80,0x21,0x7a,0x4a,0x49,0x02,0x11,0x60,0x9e,0x21,0x79,0x4a,0xff +,0x31,0x11,0x60,0x78,0x4a,0x9c,0x39,0xff,0x39,0x11,0x60,0xc9,0x22,0x77,0x4c,0x52,0x00,0x22,0x60,0x76,0x4c,0x22,0x60,0x76,0x4c,0x22,0x60,0x76,0x4a,0x10,0x60,0x76 +,0x4a,0x11,0x60,0x76,0x49,0x77,0x4a,0x11,0x60,0x77,0x4a,0x13,0x60,0x77,0x4a,0x77,0x49,0x13,0x60,0x77,0x4a,0x11,0x60,0x77,0x49,0x78,0x4a,0x11,0x60,0x78,0x49,0x78 +,0x4a,0x11,0x60,0x78,0x49,0x79,0x4a,0x11,0x60,0xc2,0x22,0x78,0x48,0x52,0x05,0x02,0x60,0x78,0x4c,0x78,0x48,0x04,0x60,0x78,0x4c,0x79,0x48,0x04,0x60,0xb4,0x24,0x78 +,0x48,0x03,0x60,0x78,0x48,0xa4,0x00,0x04,0x60,0x78,0x48,0x01,0x60,0x78,0x49,0x0a,0x60,0x78,0x4a,0x78,0x49,0x13,0x60,0x78,0x4a,0x79,0x48,0x84,0x46,0x0b,0x60,0x63 +,0x44,0x93,0x42,0xf9,0xd1,0xe1,0x22,0x76,0x4b,0x52,0x00,0x1a,0x60,0xd1,0x23,0x75,0x4a,0x13,0x60,0xc2,0x22,0x75,0x49,0xff,0x32,0x0a,0x60,0x74,0x49,0x0a,0x60,0xcd +,0x21,0x74,0x4a,0x11,0x60,0x74,0x4a,0x02,0x31,0x11,0x60,0x73,0x4a,0x13,0x60,0x49,0x4a,0x73,0x4b,0x1a,0x60,0x80,0x22,0x72,0x4b,0x52,0x02,0x1a,0x60,0x9e,0x22,0x71 +,0x4b,0xff,0x32,0x1a,0x60,0x52,0x23,0x70,0x4a,0x13,0x60,0xc9,0x22,0x70,0x49,0x52,0x00,0x0a,0x60,0x6f,0x49,0x0a,0x60,0x4f,0x21,0x6f,0x4a,0x11,0x60,0x6f,0x4a,0x01 +,0x31,0x11,0x60,0x6e,0x4a,0x13,0x60,0x6e,0x4a,0x6f,0x4b,0x1a,0x60,0x00,0x23,0x6e,0x4a,0x13,0x60,0x6e,0x4a,0x3f,0x39,0x11,0x60,0x6e,0x49,0x6e,0x4a,0x11,0x60,0x45 +,0x49,0x6e,0x4a,0x11,0x60,0x6e,0x49,0x6e,0x4a,0x11,0x60,0x6e,0x49,0x6f,0x4a,0x11,0x60,0x6f,0x49,0x6f,0x4a,0x11,0x60,0x6f,0x49,0x70,0x4a,0x11,0x60,0x47,0x49,0x6f +,0x4a,0x11,0x60,0xb4,0x21,0x6f,0x4a,0x13,0x60,0x6f,0x4a,0x89,0x00,0x11,0x60,0x6e,0x4a,0x6f,0x49,0x13,0x60,0x49,0x4a,0x49,0x48,0x84,0x46,0x0b,0x60,0x63,0x44,0x93 +,0x42,0xf9,0xd1,0x38,0x4b,0x39,0x4a,0x13,0x60,0xc2,0x22,0x38,0x49,0x52,0x05,0x0a,0x60,0x3e,0x49,0x0b,0x60,0x3e,0x49,0x0a,0x60,0x5b,0x4a,0x5b,0x49,0x5c,0x48,0x0a +,0x60,0x5c,0x49,0x08,0x60,0x63,0x49,0x0b,0x60,0x63,0x49,0x0b,0x60,0x63,0x4b,0x1a,0x60,0x00,0x23,0x62,0x4a,0x13,0x60,0x13,0x68,0x00,0x2b,0xfc,0xd0,0x13,0x68,0x00 +,0xf0,0xc0,0xf8,0x05,0xb0,0x30,0xbc,0x01,0xbc,0x00,0x47,0xa8,0xff,0xff,0x24,0x7f,0x00,0x01,0x00,0x00,0x12,0x14,0x10,0x08,0xfc,0xff,0x24,0x14,0x20,0x20,0x10,0x0c +,0x20,0x20,0x10,0xfe,0xff,0xfe,0xff,0x40,0x22,0x20,0x10,0x40,0x2a,0x20,0x10,0x3e,0x02,0x01,0x00,0x44,0x22,0x20,0x10,0x44,0x2a,0x20,0x10,0x00,0x04,0x40,0x10,0x04 +,0x04,0x40,0x10,0x08,0x04,0x40,0x10,0x0c,0x04,0x40,0x10,0x10,0x04,0x40,0x10,0x14,0x04,0x40,0x10,0x18,0x04,0x40,0x10,0xc1,0x01,0xc5,0x01,0x1c,0x04,0x40,0x10,0x20 +,0x04,0x40,0x10,0x24,0x04,0x40,0x10,0x28,0x04,0x40,0x10,0x2c,0x04,0x40,0x10,0x30,0x04,0x40,0x10,0x34,0x04,0x40,0x10,0x38,0x04,0x40,0x10,0x3c,0x04,0x40,0x10,0x92 +,0x01,0x96,0x01,0x40,0x04,0x40,0x10,0x44,0x04,0x40,0x10,0x48,0x04,0x40,0x10,0x90,0x01,0xf0,0x00,0x5c,0x04,0x40,0x10,0xd1,0x00,0xc1,0x01,0x60,0x04,0x40,0x10,0x02 +,0x00,0x92,0x01,0x64,0x04,0x40,0x10,0x00,0x00,0x30,0x18,0x68,0x04,0x40,0x10,0x6c,0x04,0x40,0x10,0x41,0x03,0x08,0x00,0x70,0x04,0x40,0x10,0x01,0x05,0x01,0x00,0x74 +,0x04,0x40,0x10,0x78,0x04,0x40,0x10,0x90,0x04,0x40,0x10,0x94,0x04,0x40,0x10,0x98,0x04,0x40,0x10,0x9c,0x04,0x40,0x10,0x84,0x04,0x40,0x10,0x00,0x01,0x01,0x01,0x01 +,0x01,0x01,0x00,0x00,0x05,0x40,0x10,0x04,0x05,0x40,0x10,0x08,0x05,0x40,0x10,0x0c,0x05,0x40,0x10,0x10,0x05,0x40,0x10,0x14,0x05,0x40,0x10,0x18,0x05,0x40,0x10,0x1c +,0x05,0x40,0x10,0x20,0x05,0x40,0x10,0x24,0x05,0x40,0x10,0x28,0x05,0x40,0x10,0x2c,0x05,0x40,0x10,0x30,0x05,0x40,0x10,0x34,0x05,0x40,0x10,0x38,0x05,0x40,0x10,0x3c +,0x05,0x40,0x10,0x94,0x01,0x98,0x01,0x40,0x05,0x40,0x10,0x44,0x05,0x40,0x10,0x48,0x05,0x40,0x10,0x40,0x01,0xf0,0x00,0x5c,0x05,0x40,0x10,0x60,0x05,0x40,0x10,0x52 +,0x00,0x92,0x01,0x64,0x05,0x40,0x10,0x00,0x65,0x34,0x18,0x68,0x05,0x40,0x10,0x00,0x65,0x44,0x18,0x6c,0x05,0x40,0x10,0x01,0x03,0x08,0x00,0x70,0x05,0x40,0x10,0x74 +,0x05,0x40,0x10,0x78,0x05,0x40,0x10,0x90,0x05,0x40,0x10,0x9c,0x05,0x40,0x10,0x84,0x05,0x40,0x10,0x00,0xfe,0xff,0x23,0x04,0xfe,0xff,0x23,0x08,0xfe,0xff,0x23,0xf8 +,0xff,0xff,0x1f,0x18,0x47,0xc0,0x46,0x5f,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x26,0x00,0x00,0x00}; +const int screeninit_size=952; diff --git a/chainloader/Makefile b/chainloader/Makefile new file mode 100644 index 0000000..6ab2a45 --- /dev/null +++ b/chainloader/Makefile @@ -0,0 +1,44 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/base_tools + +name := $(shell basename $(CURDIR)) + +dir_source := source +dir_build := build +dir_out := ../$(dir_build) + +ASFLAGS := -mcpu=arm946e-s +CFLAGS := -Wall -Wextra -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math +LDFLAGS := -nostartfiles -Wl,--nmagic + +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + +.PHONY: all +all: $(dir_out)/$(name).bin + +.PHONY: clean +clean: + @rm -rf $(dir_build) + +$(dir_out)/$(name).bin: $(dir_build)/$(name).elf + $(OBJCOPY) -S -O binary $< $@ + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ + +$(dir_build)/memory.o: CFLAGS += -O3 + +$(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) $< diff --git a/chainloader/linker.ld b/chainloader/linker.ld new file mode 100644 index 0000000..c5903f0 --- /dev/null +++ b/chainloader/linker.ld @@ -0,0 +1,21 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(_start) +SECTIONS +{ + . = 0x01FF9000; + + __start__ = ABSOLUTE(.); + + .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } + .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } + .data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); } + + . = ALIGN(4); + + __end__ = ABSOLUTE(.); + + __stack_top__ = 0x01FFB800; + __stack_bottom__ = 0x01FFA800; +} diff --git a/chainloader/source/cache.h b/chainloader/source/cache.h new file mode 100644 index 0000000..d5e03cc --- /dev/null +++ b/chainloader/source/cache.h @@ -0,0 +1,31 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include "types.h" + +void flushCaches(void); diff --git a/chainloader/source/cache.s b/chainloader/source/cache.s new file mode 100644 index 0000000..29e157f --- /dev/null +++ b/chainloader/source/cache.s @@ -0,0 +1,60 @@ +@ This file is part of Luma3DS +@ Copyright (C) 2016-2017 Aurora Wright, TuxSH +@ +@ This program is free software: you can redistribute it and/or modify +@ it under the terms of the GNU General Public License as published by +@ the Free Software Foundation, either version 3 of the License, or +@ (at your option) any later version. +@ +@ This program is distributed in the hope that it will be useful, +@ but WITHOUT ANY WARRANTY; without even the implied warranty of +@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +@ GNU General Public License for more details. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program. If not, see . +@ +@ Additional Terms 7.b and 7.c of GPLv3 apply to this file: +@ * Requiring preservation of specified reasonable legal notices or +@ author attributions in that material or in the Appropriate Legal +@ Notices displayed by works containing it. +@ * Prohibiting misrepresentation of the origin of that material, +@ or requiring that modified versions of such material be marked in +@ reasonable ways as different from the original version. + +.text +.arm +.align 4 + +.global flushCaches +.type flushCaches, %function +flushCaches: + @ Clean and flush both the data cache and instruction caches + + @ Adpated from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html , + @ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well + @ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has) + + @ Implemented in bootROM at addresses 0xffff0830 (DCache) and 0xffff0ab4 (ICache) + + mov r1, #0 @ segment counter + outer_loop: + mov r0, #0 @ line counter + + inner_loop: + orr r2, r1, r0 @ generate segment and line address + mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line + add r0, #0x20 @ increment to next line + cmp r0, #0x400 + bne inner_loop + + add r1, #0x40000000 + cmp r1, #0 + bne outer_loop + + mcr p15, 0, r1, c7, c10, 4 @ drain write buffer + + @ Flush instruction cache + mcr p15, 0, r1, c7, c5, 0 + + bx lr diff --git a/chainloader/source/firm.c b/chainloader/source/firm.c new file mode 100644 index 0000000..54842b3 --- /dev/null +++ b/chainloader/source/firm.c @@ -0,0 +1,42 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "firm.h" +#include "memory.h" +#include "cache.h" + +void disableMpuAndJumpToEntrypoints(int argc, char **argv, void *arm11Entry, void *arm9Entry); + +void launchFirm(Firm *firm, int argc, char **argv) +{ + //Copy FIRM sections to respective memory locations + for(u32 sectionNum = 0; sectionNum < 4; sectionNum++) + memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size); + + disableMpuAndJumpToEntrypoints(argc, argv, firm->arm9Entry, firm->arm11Entry); + + __builtin_unreachable(); +} diff --git a/chainloader/source/firm.h b/chainloader/source/firm.h new file mode 100644 index 0000000..0f9c84d --- /dev/null +++ b/chainloader/source/firm.h @@ -0,0 +1,50 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include "types.h" + +typedef struct __attribute__((packed)) +{ + u32 offset; + u8 *address; + u32 size; + u32 procType; + u8 hash[0x20]; +} FirmSection; + +typedef struct __attribute__((packed)) +{ + char magic[4]; + u32 reserved1; + u8 *arm11Entry; + u8 *arm9Entry; + u8 reserved2[0x30]; + FirmSection section[4]; +} Firm; + +void launchFirm(Firm *firm, int argc, char **argv); diff --git a/chainloader/source/main.c b/chainloader/source/main.c new file mode 100644 index 0000000..568a5f9 --- /dev/null +++ b/chainloader/source/main.c @@ -0,0 +1,59 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "memory.h" +#include "cache.h" +#include "firm.h" + +void main(int argc, char **argv) +{ + Firm *firm = (Firm *)0x20001000; + char *argvPassed[2], + absPath[24 + 255]; + struct fb fbs[2]; + + if(argc > 0) + { + u32 i; + for(i = 0; i < sizeof(absPath) - 1 && argv[0][i] != 0; i++) + absPath[i] = argv[0][i]; + absPath[i] = 0; + + argvPassed[0] = (char *)absPath; + } + + if(argc == 2) + { + struct fb *fbsrc = (struct fb *)argv[1]; + + memcpy(&fbs[0], &fbsrc[0], sizeof(struct fb));//fbs[0] = fbsrc[0]; + memcpy(&fbs[1], &fbsrc[1], sizeof(struct fb));//fbs[1] = fbsrc[1]; + + argvPassed[1] = (char *)&fbs; + } + + launchFirm(firm, argc, argvPassed); +} diff --git a/chainloader/source/memory.c b/chainloader/source/memory.c new file mode 100644 index 0000000..5f20707 --- /dev/null +++ b/chainloader/source/memory.c @@ -0,0 +1,40 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +/* +* memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c +*/ + +#include "memory.h" + +void memcpy(void *dest, const void *src, u32 size) +{ + u8 *destc = (u8 *)dest; + const u8 *srcc = (const u8 *)src; + + for(u32 i = 0; i < size; i++) + destc[i] = srcc[i]; +} diff --git a/chainloader/source/memory.h b/chainloader/source/memory.h new file mode 100644 index 0000000..2e120c4 --- /dev/null +++ b/chainloader/source/memory.h @@ -0,0 +1,35 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +/* +* memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c +*/ + +#pragma once + +#include "types.h" + +void memcpy(void *dest, const void *src, u32 size); diff --git a/chainloader/source/start.s b/chainloader/source/start.s new file mode 100644 index 0000000..60f149e --- /dev/null +++ b/chainloader/source/start.s @@ -0,0 +1,60 @@ +@ This file is part of Luma3DS +@ Copyright (C) 2016-2017 Aurora Wright, TuxSH +@ +@ This program is free software: you can redistribute it and/or modify +@ it under the terms of the GNU General Public License as published by +@ the Free Software Foundation, either version 3 of the License, or +@ (at your option) any later version. +@ +@ This program is distributed in the hope that it will be useful, +@ but WITHOUT ANY WARRANTY; without even the implied warranty of +@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +@ GNU General Public License for more details. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program. If not, see . +@ +@ Additional Terms 7.b and 7.c of GPLv3 apply to this file: +@ * Requiring preservation of specified reasonable legal notices or +@ author attributions in that material or in the Appropriate Legal +@ Notices displayed by works containing it. +@ * Prohibiting misrepresentation of the origin of that material, +@ or requiring that modified versions of such material be marked in +@ reasonable ways as different from the original version. +.arm + +.section .text.start +.align 4 +.global _start +_start: + ldr sp, =__stack_top__ + b main + +.text +.balign 4 +.global disableMpuAndJumpToEntrypoints +.type disableMpuAndJumpToEntrypoints, %function +disableMpuAndJumpToEntrypoints: + mov r4, r0 + mov r5, r1 + mov r6, r2 + mov r7, r3 + + bl flushCaches + + @ Disable caches / MPU + mrc p15, 0, r0, c1, c0, 0 @ read control register + bic r0, #(1<<12) @ - instruction cache disable + bic r0, #(1<<2) @ - data cache disable + bic r0, #(1<<0) @ - MPU disable + mcr p15, 0, r0, c1, c0, 0 @ write control register + + @ Set the ARM11 entrypoint + mov r0, #0x20000000 + str r7, [r0, #-4] + + @ Jump to the ARM9 entrypoint + mov r0, r4 + mov r1, r5 + ldr r2, =0x1BEEF + bx r6 diff --git a/chainloader/source/types.h b/chainloader/source/types.h new file mode 100644 index 0000000..c4565be --- /dev/null +++ b/chainloader/source/types.h @@ -0,0 +1,45 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2017 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include + +//Common data types +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; + +struct fb { + u8 *top_left; + u8 *top_right; + u8 *bottom; +} __attribute__((packed)); diff --git a/changelog.txt b/changelog.txt index 3f942f4..f695c56 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,195 @@ +2.5.2 + +Fix crash when booted from A9LH or B9S v1.0 (due to framebuffers always gotten from arguments) + +2.5.1 + +Boot9Strap v1.2 support + +2.5.0 + +SysNAND location supported (only for A9LH/B9S versions): +- Binaries from SysNAND can be started or added as boot entry from "File Browser" (press Select key to switch for SD card to SysNAND) +- CtrBootManager can be put on SysNAND (for example, as "nand:/boot.firm"): in this case, its configuration file become "nand:/CtrBootManager.cfg" +- When CtrBootManager is put on SysNAND, safe mode is triggered: it won't save its configuration, you must manually change "CtrBootManager.cfg" + +New pathes prefixes: +- "sdmc:/" targets the SD card +- "nand:/" targets the SysNAND +- "/" targets the drive where CtrBootManager is booted from + +2.4.0 + +Include B9S version named "CtrBootManager9.firm" +Support ".firm" files in A9LH/B9S versions +- Binary patches are supported for this new file type +- Offset is not supported for this new file type +- Remove useless 3DSX file detection for those versions + +2.3.2 + +Fix crash in the recently added feature to disable screen initialization for direct boot (due to swapbuffer call for splash screen) + +2.3.1 + +Add the possibility to disable screen initialization for direct boot (timeout of "0" and no recovery key pressed) +- In "[general]" category, write "screeninit=0;" + +Don't forget that if the chainloaded binary doesn't do any screen initalization, enabling this feature will make it crash! + +2.3.0 + +Add splash screen support, keywords "splashTop" and "splashBot" +- In "[general]" category for default behavior +- In "[entry]" categories for per-entry behavior (which override the default behavior) + +Add password feature, it can be configured from "Settings" menu (maximum of 16 keys supported) + +Password can be set through the new "[password]" category with the following properties: +- "keys": the password itself (integers list separated with ":") +- "attempts": the allowed attempts before considering that the password check is failed (0 means infinite) +- "failPath": loaded element if the password check has failed +- "failOffset": offset for loaded element if the password check has failed +- "failImgTop" and "failImgBot": splash screen when password check has failed (it could be the freeze scren if "failPath" is invalid/empty) + +2.2.0 + +Add "brightness" property for screen-init (only used if CtrBootManager must initialize the screen) + +The font used on bottom screen can have a different color ("font3" property in "[theme]" and "[animation]" categories). + +Add support for transparent background images (the background color will be displayed on transparent zones) +- "bgImgTopAlpha", "bgImgTop3DAlpha" and "bgImgBotAlpha" keywords in "[theme]" category to set a background image as transparent + +Add background images fade-in effect, activated by "bgImgTopFadeIn" and "bgImgBotFadeIn" properties in "[animation]" category + +Add the possibility to have more than 11 boot entries (but "File browser" entry add feature cannot put more than the previous entries rounded up to a 12 multiplier) + +Thumbnail support for each boot entry, new keywords in "[entry]" categories: +- "thumbTop", "thumbTop3D" and "thumbBot" for thumbnail path for each screen +- "thumbTopParams", "thumbTop3DParams" and "thumbBotParams" for thumbnail parameters, format "posX:posY:sizeX:sizeY:alpha" + +2.1.1 + +Some code refactoring to avoid confusion with "gfx" functions: fix 3DSX version crash on CFW start from MenuHax + +2.1.0 + +Fix loops count issue while reading the configuration file (always considered as infinite) +Fix uncompressed movie loops issue when the loop doesn't start from the begining and the stream type was file +3D movie/background image support for 3DSX version + +2.0.0 + +Fix some random issues with "compressed_memory" and "file" stream types with compressed movies +Documentation and Wiki: https://github.com/OperationNT414C/CtrBootManager/wiki/CtrBootManager-setup + +V10-3DSX + +Changes to support the 3DSX version with new animation and movie features +It seems 3DSX version has a bug with compressed movies when property "loopStreamType=compressed_memory" is active. + +V10 + +Remove "Anim" part from keywords in "[animation]" category (the category should be enough to avoid confusion) + +The "[movie]" category is removed, 2 new categories have been created to replace it: "[topMovie]" and "[bottomMovie]" +So keywords are renamed: +- "movieTop" and "movieBot" => "path" +- "movieTopCompress" and "movieBotCompress" => "compression" +- "movieTopLoop" and "movieBotLoop" => "loopCount" +- "movieTopLoopReverse" and "movieBotLoopReverse" => "loopReverse" +- "movieTopLoopStart" and "movieBotLoopStart" => "loopStartFrame" +- "movieTopTimeOnLoopStart" and "movieBotTimeOnLoopStart" => "delayOnLoopStart" +- "movieTopLoopEnd" and "movieBotLoopEnd" => "loopEndFrame" +- "movieTopTimeOnLoopEnd" and "movieBotTimeOnLoopEnd" => "delayOnLoopEnd" + +More explicit values has been added for some properties: +- For "compression" property: "QuickLZ" +- For "loopCount" property: "infinite" and "INFINITE" (set a negative value) +- For "loopReverse" property: "yes" and "YES" set a not null value, "no" and "NO" set a null value + +A new property "loopStreamType" has been added to manage how the loop is stored: +- "loopStreamType=file" (or "loopStreamType=0"): the movie file stays opened and loop will be permanently read from file +- "loopStreamType=memory" (or "loopStreamType=1"): the movie file is close and all frames required for loop are kept in memory +- "loopStreamType=compressed_memory" (or "loopStreamType=2"): only for compressed movies, the file content for loop is kept in memory and decompressed again on each loop +Now, the default loop stream mode is "file" to prevent any crash due memory usage. + +Add the CtrBootManager current version display in "Settings" menu + +V9 + +Movies support, new category "movie" and new keywords: +- "movieTop" and "movieBot": movie pathes +- "movieTopCompress" and "movieBotCompress": declare a movie as compressed (QuickLZ compression) +- "movieTopLoop" and "movieBotLoop": set the number of loops to do (negative means infinite) +- "movieTopLoopReverse" and "movieBotLoopReverse": if active, the loop will be played in reverse to ensure movie continuity +- "movieTopLoopStart" and "movieBotLoopStart": set up the first movie frame for loop +- "movieTopTimeOnLoopStart" and "movieBotTimeOnLoopStart": set up the "freeze time" on loop first frame +- "movieTopLoopEnd" and "movieBotLoopEnd": set up the last movie frame for loop (negative means the movie end frame) +- "movieTopTimeOnLoopEnd" and "movieBotTimeOnLoopEnd": set up the "freeze time" on loop last frame +WARNING: Loop is fully stored in memory as an uncompressed movie. So if one loop contains too much frames, you WILL crash. +To avoid this issue, please control carefully the loop number of frames particularly with compressed videos. +You can clamp your loop size with "movieXxxLoopStart" and "movieXxxLoopEnd" to avoid any issue. By default, the whole movie is put in loop. +For the top movie, the loop size will cost {(movieTopLoopEnd-movieTopLoopStart) * 288000} bytes. +For the bottom movie, the loop size will cost {(movieBotLoopEnd-movieBotLoopStart) * 230400} bytes. +Crashes occurs beyond 24MB, so do the maths! + +Add some space for CtrBootManager9 chainloading with binary patch: +<< patchMemSearch=2F61396C682E63666700; + patchMemOverwriteStr=/directory/other_a9lh.cfg; + patchOccurence=1; >> + +V8 +Rename "globalFadeIn" to "menuFadeIn" +Move all animations keywords under a new category "animation" +Add security checks to avoid configuration corruption if there are more than 11 entries or more than 4 patches for one entry +Add key left and right to speed up selection move in menus (particularly useful for File Browser) + +V7 +Add animation system for colors and fade in setup +New keywords added to support animations and fade in effect: +- "bgTop1Anim", "bgTop2Anim", "bgBottomAnim" (syntax example: "1:-1:FFFFFF") +- "highlightAnim", "bordersAnim", "font1Anim", "font2Anim" (syntax example: "1:-1:FFFFFF" or "1:-1:FFFFFF7F") +- "globalFadeIn" (syntax example: "1:-1" +Syntax for "*object*Anim" keyword is "objectAnim=X:Y:Z;" where: +- X as positive integer: the animation time in frame for one way (consider 2X for animation round-trip) +- Y as integer: the animation offset (if 2 animations has the same X value, this Y value will allow a shift between them) +- Z as a color in hexadecimal (3 bytes for color without transparency, mandatory for background; 4 bytes with transparency) +Syntax for fade in effect is "globalFadeIn=X:Y;" where: +- X as positive integer: the fade-in effect duration in frame +- Y as an integer: the time to wait before starting the fade-in effect (can be negative to start directly in the middle of the effect) + +V6 +Minor fix for background load +Default offset 0x12000 is also added if the binary is directly started for File Browser + +V5 +Update fatfs to fix File Browser illogical crashes (for example, if the background was not set to black) +Add a default offset at 0x12000 if a ".dat" file is added to entries through File Browser + +V4 +Displays the number of binary patches for current entry (if there is at least one patch) +Add the possibility to set transparency for borders and fonts +Fix a crash on start if no config file exists + +V3 +Add the possibility of binary patching, limited to 4 for each entry +New keywords for config file to support binary patching: +- "patchMemSearch" represents the searched pattern in hexadecimal values (730064006D0063003A002F00 => sdmc:/ with null value between each letter) +- "patchMemOverwrite" represents the values which will overwrite the found pattern (in hexadecimal values) +- "patchMemOverwriteStr" represents a string which will overwrite the pattern (it replaces "patchMemOverwrite" usage) +- "patchMemOverwriteWStr" represents a string with wide characters (null value between each character) which will overwrite the pattern (it replaces "patchMemOverwrite" usage) +- "patchOccurence" represents the occurence to modify: if the pattern can be found multiple times in the loaded binary file, only the indicated occurence will be overwritten (0 means all occurences will be overwritten) +A patch of an entry MUST always end with "patchOccurence" (it is like an entry which must always end with "key"). + +V2 +Add screen-init support + +V1 +Fix offset write issue and crash when a background was added +Add the possibility to set transparency for highlight + 1.0: add boot menu entry from the file manager (X) 1.0: remove boot menu entry from boot menu (X) 1.0: add a few more help/information to the bottom screen diff --git a/screeninit/Makefile b/screeninit/Makefile new file mode 100644 index 0000000..28ed64b --- /dev/null +++ b/screeninit/Makefile @@ -0,0 +1,47 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +CC := arm-none-eabi-gcc +AS := arm-none-eabi-as +LD := arm-none-eabi-ld +OC := arm-none-eabi-objcopy + +name := $(shell basename $(CURDIR)) + +dir_source := source +dir_build := build + +ASFLAGS := -mcpu=mpcore -mfloat-abi=hard +CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math +LDFLAGS := -nostdlib + +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + +.PHONY: all +all: $(name).bin + +.PHONY: clean +clean: + @rm -rf $(dir_build) + +$(name).bin: $(dir_build)/$(name).elf + $(OC) -S -O binary $< $@ + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ + +$(dir_build)/%.o: $(dir_source)/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(dir_build)/%.o: $(dir_source)/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) $(OUTPUT_OPTION) $< +include $(call rwildcard, $(dir_build), *.d) diff --git a/screeninit/linker.ld b/screeninit/linker.ld new file mode 100644 index 0000000..28ed64b --- /dev/null +++ b/screeninit/linker.ld @@ -0,0 +1,47 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +CC := arm-none-eabi-gcc +AS := arm-none-eabi-as +LD := arm-none-eabi-ld +OC := arm-none-eabi-objcopy + +name := $(shell basename $(CURDIR)) + +dir_source := source +dir_build := build + +ASFLAGS := -mcpu=mpcore -mfloat-abi=hard +CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math +LDFLAGS := -nostdlib + +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + +.PHONY: all +all: $(name).bin + +.PHONY: clean +clean: + @rm -rf $(dir_build) + +$(name).bin: $(dir_build)/$(name).elf + $(OC) -S -O binary $< $@ + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ + +$(dir_build)/%.o: $(dir_source)/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(dir_build)/%.o: $(dir_source)/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) $(OUTPUT_OPTION) $< +include $(call rwildcard, $(dir_build), *.d) diff --git a/screeninit/source/main.c b/screeninit/source/main.c new file mode 100644 index 0000000..0254a77 --- /dev/null +++ b/screeninit/source/main.c @@ -0,0 +1,109 @@ +#include "types.h" + +void main(void) +{ + const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26}; + u32 brightnessLevel = *(vu32 *)0x24FFFC08; + vu32 *const arm11 = (u32 *)0x1FFFFFF8; + + *(vu32 *)0x10141200 = 0x1007F; + *(vu32 *)0x10202014 = 0x00000001; + *(vu32 *)0x1020200C &= 0xFFFEFFFE; + *(vu32 *)0x10202240 = brightness[brightnessLevel]; + *(vu32 *)0x10202A40 = brightness[brightnessLevel]; + *(vu32 *)0x10202244 = 0x1023E; + *(vu32 *)0x10202A44 = 0x1023E; + + // Top screen + *(vu32 *)0x10400400 = 0x000001c2; + *(vu32 *)0x10400404 = 0x000000d1; + *(vu32 *)0x10400408 = 0x000001c1; + *(vu32 *)0x1040040c = 0x000001c1; + *(vu32 *)0x10400410 = 0x00000000; + *(vu32 *)0x10400414 = 0x000000cf; + *(vu32 *)0x10400418 = 0x000000d1; + *(vu32 *)0x1040041c = 0x01c501c1; + *(vu32 *)0x10400420 = 0x00010000; + *(vu32 *)0x10400424 = 0x0000019d; + *(vu32 *)0x10400428 = 0x00000002; + *(vu32 *)0x1040042c = 0x00000192; + *(vu32 *)0x10400430 = 0x00000192; + *(vu32 *)0x10400434 = 0x00000192; + *(vu32 *)0x10400438 = 0x00000001; + *(vu32 *)0x1040043c = 0x00000002; + *(vu32 *)0x10400440 = 0x01960192; + *(vu32 *)0x10400444 = 0x00000000; + *(vu32 *)0x10400448 = 0x00000000; + *(vu32 *)0x1040045C = 0x00f00190; + *(vu32 *)0x10400460 = 0x01c100d1; + *(vu32 *)0x10400464 = 0x01920002; + *(vu32 *)0x10400468 = 0x18300000; // Framebuffer top left first address + *(vu32 *)0x1040046C = 0x18400000; // Framebuffer top left second address + *(vu32 *)0x10400470 = 0x80341; + *(vu32 *)0x10400474 = 0x00010501; + *(vu32 *)0x10400478 = 0; // Framebuffer bottom select + *(vu32 *)0x10400490 = 0x000002D0; + *(vu32 *)0x10400494 = 0x18300000; // Framebuffer top right first address + *(vu32 *)0x10400498 = 0x18400000; // Framebuffer top right second address + *(vu32 *)0x1040049C = 0x00000000; + + // Disco register + for(u32 i = 0; i < 256; i++) + *(vu32 *)0x10400484 = 0x10101 * i; + + // Bottom screen + *(vu32 *)0x10400500 = 0x000001c2; + *(vu32 *)0x10400504 = 0x000000d1; + *(vu32 *)0x10400508 = 0x000001c1; + *(vu32 *)0x1040050c = 0x000001c1; + *(vu32 *)0x10400510 = 0x000000cd; + *(vu32 *)0x10400514 = 0x000000cf; + *(vu32 *)0x10400518 = 0x000000d1; + *(vu32 *)0x1040051c = 0x01c501c1; + *(vu32 *)0x10400520 = 0x00010000; + *(vu32 *)0x10400524 = 0x0000019d; + *(vu32 *)0x10400528 = 0x00000052; + *(vu32 *)0x1040052c = 0x00000192; + *(vu32 *)0x10400530 = 0x00000192; + *(vu32 *)0x10400534 = 0x0000004f; + *(vu32 *)0x10400538 = 0x00000050; + *(vu32 *)0x1040053c = 0x00000052; + *(vu32 *)0x10400540 = 0x01980194; + *(vu32 *)0x10400544 = 0x00000000; + *(vu32 *)0x10400548 = 0x00000011; + *(vu32 *)0x1040055C = 0x00f00140; + *(vu32 *)0x10400560 = 0x01c100d1; + *(vu32 *)0x10400564 = 0x01920052; + *(vu32 *)0x10400568 = 0x18300000 + 0x46500; // Framebuffer bottom first address + *(vu32 *)0x1040056C = 0x18400000 + 0x46500; // Framebuffer bottom second address + *(vu32 *)0x10400570 = 0x80301; + *(vu32 *)0x10400574 = 0x00010501; + *(vu32 *)0x10400578 = 0; // Framebuffer bottom select + *(vu32 *)0x10400590 = 0x000002D0; + *(vu32 *)0x1040059C = 0x00000000; + + // Disco register + for(u32 i = 0; i < 256; i++) + *(vu32 *)0x10400584 = 0x10101 * i; + + *(vu32 *)0x10400468 = 0x18300000; + *(vu32 *)0x1040046c = 0x18400000; + *(vu32 *)0x10400494 = 0x18300000; + *(vu32 *)0x10400498 = 0x18400000; + *(vu32 *)0x10400568 = 0x18346500; + *(vu32 *)0x1040056c = 0x18446500; + + //Set CakeBrah framebuffers + *((vu32 *)0x23FFFE00) = 0x18300000; + *((vu32 *)0x23FFFE04) = 0x18300000; + *((vu32 *)0x23FFFE08) = 0x18346500; + + //Clear ARM11 entry offset + *arm11 = 0; + + //Wait for the entry to be set + while(!*arm11); + + //Jump to it + ((void (*)())*arm11)(); +} diff --git a/screeninit/source/start.s b/screeninit/source/start.s new file mode 100644 index 0000000..40392d1 --- /dev/null +++ b/screeninit/source/start.s @@ -0,0 +1,10 @@ +.section .text.start +.align 4 +.global _start +_start: + @ Disable interrupts + CPSID aif + + b main + + .word 0 diff --git a/screeninit/source/types.h b/screeninit/source/types.h new file mode 100644 index 0000000..fb0cf31 --- /dev/null +++ b/screeninit/source/types.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +//Common data types +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; diff --git a/source/CakeBrah/source/brahma.c b/source/CakeBrah/source/brahma.c index 9677ce4..d112d3a 100644 --- a/source/CakeBrah/source/brahma.c +++ b/source/CakeBrah/source/brahma.c @@ -49,7 +49,7 @@ void redirect_codeflow (u32 *dst_addr, u32 *src_addr) { returns: 0 on failure, 1 on success */ s32 get_exploit_data (struct exploit_data *data) { u32 fversion = 0; - u8 isN3DS = 0; + bool isN3DS = 0; u32 i; s32 result = 0; u32 sysmodel = SYS_MODEL_NONE; diff --git a/source/anim.c b/source/anim.c new file mode 100644 index 0000000..b02ce36 --- /dev/null +++ b/source/anim.c @@ -0,0 +1,569 @@ +#include "anim.h" + +#define TOP_SCREEN_SIZE (400*240*3) +#define BOT_SCREEN_SIZE (320*240*3) + +#ifdef ARM9 +#include "memory.h" + +// Top compression required memory: 576000+28809 = 604809 +#define TOP_COMP_PTR (char*)PTR_MOVIE_COMP +#define TOP_COMP_PREV_FRAME_PTR (TOP_COMP_PTR+sizeof(comp_manager_s)) +#define TOP_COMP_COMP_FRAME_PTR (TOP_COMP_PREV_FRAME_PTR+TOP_SCREEN_SIZE) + +// Bottom compression required memory: 460800+23049 = 483849 +#define BOT_COMP_PTR (TOP_COMP_COMP_FRAME_PTR+TOP_SCREEN_SIZE+(TOP_SCREEN_SIZE/10)+9) +#define BOT_COMP_PREV_FRAME_PTR (BOT_COMP_PTR+sizeof(comp_manager_s)) +#define BOT_COMP_COMP_FRAME_PTR (BOT_COMP_PREV_FRAME_PTR+BOT_SCREEN_SIZE) + +// Total compression required memory: 1088658 = 0x109C92 +#else +#include +#endif + +#include "utility.h" + +void movieInit(movie_state_s* movie) +{ + movie->file = NULL; + movie->fileOffset = 0; + movie->comp = NULL; + movie->loopStream = NULL; + movie->curLoop = 0; + movie->loopsToDo = 0; + movie->loopStartFrameStream = NULL; + movie->loopEndFrameStream = NULL; +} + +void animInit() +{ +#ifdef ARM9 + anim = (anim_state_s*)PTR_ANIM; +#else + anim = malloc(sizeof(anim_state_s)); +#endif + + anim->timer = 0; + anim->active = 0; + + anim->topMovie.frameSize = TOP_SCREEN_SIZE; + movieInit(&anim->topMovie); +#ifndef ARM9 + anim->top3DMovie.frameSize = TOP_SCREEN_SIZE; + movieInit(&anim->top3DMovie); +#endif + anim->botMovie.frameSize = BOT_SCREEN_SIZE; + movieInit(&anim->botMovie); +} + +void movieLoopSetup(movie_state_s* movie) +{ + movie->loopFramesCount = movie->loopEndFrame - movie->loopStartFrame + + movie->loopTimeOnStartFrame + movie->loopTimeOnEndFrame; + + movie->loopStartFrameStream = movie->loopStream; + movie->loopEndFrameStream = movie->loopStream + movie->frameSize + *( ( MEMORY_STREAM == movie->loopStreamType ) ? (movie->loopEndFrame-movie->loopStartFrame) : 1 ); +} + + +#ifdef ARM9 +void movieSetup(movie_config_s* mvConf, movie_state_s* movie, void* filePtr, comp_manager_s* compPtr) +#else +void movieSetup(movie_config_s* mvConf, movie_state_s* movie) +#endif +{ + if ( mvConf->path[0] == '\0' ) + return; + +#ifdef ARM9 + movie->file = (void*)(filePtr); +#else + int fileHandleSize = getFileHandleSize(); + movie->file = malloc(fileHandleSize); +#endif + if ( fileHandleOpen(movie->file, mvConf->path) < 0 ) + { + #ifndef ARM9 + free(movie->file); + #endif + movie->file = NULL; + return; + } + + if ( mvConf->compressed ) + { + #ifdef ARM9 + movie->comp = compPtr; + #else + movie->comp = (comp_manager_s*)malloc(sizeof(comp_manager_s)); + movie->comp->frame_prev_read = (const char*)malloc(movie->frameSize); + movie->comp->frame_comp = (char*)malloc(movie->frameSize + (movie->frameSize/10) + 9); + #endif + movie->comp->frame_prev_write = (char*)movie->comp->frame_prev_read; + + memset(&movie->comp->state, 0, sizeof(qlz_state_decompress)); + memset(movie->comp->frame_prev_write, 0, movie->frameSize); + memset(movie->comp->frame_comp, 0, movie->frameSize + (movie->frameSize/10) + 9); + movie->comp->reverse = 0; + } + + movie->framesCount = getMovieFramesCount(movie->file, movie->frameSize, (NULL != movie->comp)); + if ( movie->framesCount <= 0 ) + { + fileHandleClose(movie->file); + #ifndef ARM9 + free(movie->file); + #endif + movie->file = NULL; + return; + } + + movie->curFrame = 0; + movie->curLoop = 0; + movie->loopsToDo = mvConf->loopCount; + if ( 0 == movie->loopsToDo ) + return; + + movie->loopStreamType = mvConf->loopStreamType; + if ( movie->loopStreamType == MEMORY_COMPRESSED_STREAM ) + { + if ( !mvConf->compressed ) + movie->loopStreamType = MEMORY_STREAM; + } + else if ( movie->loopStreamType != FILE_STREAM ) + movie->loopStreamType = MEMORY_STREAM; + + movie->loopReverse = mvConf->loopReverse; + + movie->loopTimeOnStartFrame = ( mvConf->loopTimeOnStartFrame > 0 ) ? mvConf->loopTimeOnStartFrame : 0; + movie->loopTimeOnEndFrame = ( mvConf->loopTimeOnEndFrame > 0 ) ? mvConf->loopTimeOnEndFrame : 0; + + movie->loopStartFrame = (mvConf->loopStartFrame < movie->framesCount) ? mvConf->loopStartFrame : (movie->framesCount-1); + if ( movie->loopStartFrame < 0 ) + movie->loopStartFrame = 0; + + movie->loopEndFrame = (mvConf->loopEndFrame < 0 || mvConf->loopEndFrame > movie->loopStartFrame) + ? mvConf->loopEndFrame : movie->loopStartFrame; + if ( movie->loopEndFrame < 0 || movie->loopEndFrame >= movie->framesCount ) + movie->loopEndFrame = movie->framesCount-1; + + switch ( movie->loopStreamType ) + { + case FILE_STREAM: + // We will keep file opened to stream next loops content (slower but avoid memory issues). + // But we still keep at least uncompressed loop first and last frame in memory. + movie->loopStreamSize = movie->frameSize*2 + ((movie->loopEndFrame-movie->loopStartFrame+2)*sizeof(int)); + break; + + case MEMORY_STREAM: + // Full uncompressed frames are kept in memory, file is closed. + movie->loopStreamSize = movie->frameSize * (movie->loopEndFrame-movie->loopStartFrame+1); + break; + + case MEMORY_COMPRESSED_STREAM: + // For compressed movie, we will keep compressed data in memory and close the file. + getMovieDataOffsetForFrames(movie->file, movie->frameSize, (NULL != movie->comp), + movie->loopStartFrame, movie->loopEndFrame, + NULL, &movie->loopStreamSize); + movie->loopStreamSize += movie->frameSize*2 + ((movie->loopEndFrame-movie->loopStartFrame+2)*sizeof(int)); + break; + } + +#ifdef ARM9 + movie->loopStream = (char*)memAlloc(movie->loopStreamSize); +#else + movie->loopStream = (char*)malloc(movie->loopStreamSize); +#endif + + if ( MEMORY_STREAM != movie->loopStreamType ) + *(int*)(movie->loopStream+movie->frameSize*2) = (movie->loopEndFrame-movie->loopStartFrame+1); + movieLoopSetup(movie); +} + +void animSetup() +{ + memcpy(anim->bgTop1, config->bgTop1, sizeof(u8[3])); + memcpy(anim->bgTop2, config->bgTop2, sizeof(u8[3])); + memcpy(anim->bgBot, config->bgBot, sizeof(u8[3])); + memcpy(anim->highlight, config->highlight, sizeof(u8[4])); + memcpy(anim->borders, config->borders, sizeof(u8[4])); + memcpy(anim->fntDef, config->fntDef, sizeof(u8[4])); + memcpy(anim->fntSel, config->fntSel, sizeof(u8[4])); + memcpy(anim->fntBot, config->fntBotActive ? config->fntBot : config->fntDef, sizeof(u8[4])); + anim->thumbFade = 0xFF; + anim->imgTopFade = 0xFF; + anim->imgBotFade = 0xFF; + +#ifdef ARM9 + comp_manager_s* topComp = (comp_manager_s*)PTR_MOVIE_COMP; + topComp->frame_prev_read = TOP_COMP_PREV_FRAME_PTR; + topComp->frame_comp = TOP_COMP_COMP_FRAME_PTR; + + comp_manager_s* botComp = (comp_manager_s*)BOT_COMP_PTR; + botComp->frame_prev_read = BOT_COMP_PREV_FRAME_PTR; + botComp->frame_comp = BOT_COMP_COMP_FRAME_PTR; + + int fileHandleSize = getFileHandleSize(); + movieSetup(&config->movieTop, &anim->topMovie, PTR_ANIM+sizeof(anim_state_s), topComp); + movieSetup(&config->movieBot, &anim->botMovie, PTR_ANIM+sizeof(anim_state_s)+fileHandleSize, botComp); +#else + movieSetup(&config->movieTop, &anim->topMovie); + movieSetup(&config->movieTop3D, &anim->top3DMovie); + if ( NULL != anim->top3DMovie.file && !gfxIs3D() ) + gfxSet3D(true); + movieSetup(&config->movieBot, &anim->botMovie); +#endif + + if ( config->bgTop1AnimTime > 0 || config->bgTop2AnimTime > 0 || config->bgBotAnimTime > 0 + || config->highlightAnimTime > 0 || config->bordersAnimTime > 0 + || config->fntDefAnimTime > 0 || config->fntSelAnimTime > 0 || config->fntBotAnimTime > 0 + || config->menuFadeInTime > 0 || config->menuFadeInTimeStart != 0 + || config->bgImgTopFadeInTime > 0 || config->bgImgTopFadeInTimeStart != 0 + || config->bgImgBotFadeInTime > 0 || config->bgImgBotFadeInTimeStart != 0 ) + { + anim->active = 1; + incrementAnimTime(); + } +} + +float computeMerge(int elapsedTime, int animTime, int animStart) +{ + int curTime = (elapsedTime+animStart) % (2*animTime); + int animStep = (curTime/animTime == 0) ? (curTime%animTime) : (animTime-curTime%animTime-1); + return (float)animStep / (float)animTime; +} + +void setMergedColor(float mergeVal, u8 color1[3], u8 color2[3], u8 finalColor[3]) +{ + float oneMinusMerge = 1.0f - mergeVal; + finalColor[0] = (u8)(oneMinusMerge*color1[0]+mergeVal*color2[0]); + finalColor[1] = (u8)(oneMinusMerge*color1[1]+mergeVal*color2[1]); + finalColor[2] = (u8)(oneMinusMerge*color1[2]+mergeVal*color2[2]); +} + +void setMergedColorAlpha(float mergeVal, u8 color1[4], u8 color2[4], u8 finalColor[4]) +{ + float oneMinusMerge = 1.0f - mergeVal; + finalColor[0] = (u8)(oneMinusMerge*color1[0]+mergeVal*color2[0]); + finalColor[1] = (u8)(oneMinusMerge*color1[1]+mergeVal*color2[1]); + finalColor[2] = (u8)(oneMinusMerge*color1[2]+mergeVal*color2[2]); + finalColor[3] = (u8)(oneMinusMerge*color1[3]+mergeVal*color2[3]); +} + +void computeMenuColor(u8 color[4], u8 colorAnim[4], int animTime, int animTimeStart, u8 finalColor[4]) +{ + if ( animTime > 0 ) + { + float merge = computeMerge(anim->timer, animTime, animTimeStart); + setMergedColorAlpha(merge, color, colorAnim, finalColor); + } + else if ( anim->timer > config->menuFadeInTimeStart ) + memcpy(finalColor, color, sizeof(u8[4])); +} + +void incrementAnimTime() +{ + if (!anim->active) + return; + + if ( config->bgTop1AnimTime > 0 ) + { + float merge = computeMerge(anim->timer, config->bgTop1AnimTime, config->bgTop1AnimTimeStart); + setMergedColor(merge, config->bgTop1, config->bgTop1AnimColor, anim->bgTop1); + } + + if ( config->bgTop2AnimTime > 0 ) + { + float merge = computeMerge(anim->timer, config->bgTop2AnimTime, config->bgTop2AnimTimeStart); + setMergedColor(merge, config->bgTop2, config->bgTop2AnimColor, anim->bgTop2); + } + + if ( config->bgBotAnimTime > 0 ) + { + float merge = computeMerge(anim->timer, config->bgBotAnimTime, config->bgBotAnimTimeStart); + setMergedColor(merge, config->bgBot, config->bgBotAnimColor, anim->bgBot); + } + + computeMenuColor(config->highlight, config->highlightAnimColor, + config->highlightAnimTime, config->highlightAnimTimeStart, anim->highlight); + + computeMenuColor(config->borders, config->bordersAnimColor, + config->bordersAnimTime, config->bordersAnimTimeStart, anim->borders); + + computeMenuColor(config->fntDef, config->fntDefAnimColor, + config->fntDefAnimTime, config->fntDefAnimTimeStart, anim->fntDef); + + computeMenuColor(config->fntSel, config->fntSelAnimColor, + config->fntSelAnimTime, config->fntSelAnimTimeStart, anim->fntSel); + + if ( config->fntBotActive ) + { + computeMenuColor(config->fntBot, config->fntBotAnimColor, + config->fntBotAnimTime, config->fntBotAnimTimeStart, anim->fntBot); + } + + bool checkStopAnim = false; + if ( anim->timer >= config->menuFadeInTimeStart ) + { + int fadeInTime = anim->timer - config->menuFadeInTimeStart; + if ( fadeInTime <= config->menuFadeInTime ) + { + float fadeIn = (config->menuFadeInTime > 0) ? (float)fadeInTime / (float)config->menuFadeInTime : 1.f; + + anim->highlight[3] = (u8)(fadeIn*(float)anim->highlight[3]); + anim->borders[3] = (u8)(fadeIn*(float)anim->borders[3]); + anim->fntDef[3] = (u8)(fadeIn*(float)anim->fntDef[3]); + anim->fntSel[3] = (u8)(fadeIn*(float)anim->fntSel[3]); + anim->fntBot[3] = (u8)(fadeIn*(float)anim->fntBot[3]); + anim->thumbFade = (u8)(fadeIn*255.f); + + checkStopAnim = ( fadeInTime == config->menuFadeInTime ); + } + } + else + { + anim->highlight[3] = 0x00; + anim->borders[3] = 0x00; + anim->fntDef[3] = 0x00; + anim->fntSel[3] = 0x00; + anim->fntBot[3] = 0x00; + anim->thumbFade = 0x00; + } + + if ( anim->timer >= config->bgImgTopFadeInTimeStart ) + { + int fadeInTime = anim->timer - config->bgImgTopFadeInTimeStart; + if ( fadeInTime <= config->bgImgTopFadeInTime ) + { + float fadeIn = (config->bgImgTopFadeInTime > 0) ? (float)fadeInTime / (float)config->bgImgTopFadeInTime : 1.f; + anim->imgTopFade = (u8)(fadeIn*255.f); + checkStopAnim = ( fadeInTime == config->bgImgTopFadeInTime ); + } + } + else + anim->imgTopFade = 0x00; + + if ( anim->timer >= config->bgImgBotFadeInTimeStart ) + { + int fadeInTime = anim->timer - config->bgImgBotFadeInTimeStart; + if ( fadeInTime <= config->bgImgBotFadeInTime ) + { + float fadeIn = (config->bgImgBotFadeInTime > 0) ? (float)fadeInTime / (float)config->bgImgBotFadeInTime : 1.f; + anim->imgBotFade = (u8)(fadeIn*255.f); + checkStopAnim = ( fadeInTime == config->bgImgBotFadeInTime ); + } + } + else + anim->imgBotFade = 0x00; + + if ( checkStopAnim && config->bgTop2AnimTime == 0 && config->bgBotAnimTime == 0 + && config->highlightAnimTime == 0 && config->bordersAnimTime == 0 + && config->fntDefAnimTime == 0 && config->fntSelAnimTime == 0 && config->fntBotAnimTime == 0 + && anim->timer >= config->menuFadeInTimeStart+config->menuFadeInTime + && anim->timer >= config->bgImgTopFadeInTimeStart+config->bgImgTopFadeInTime + && anim->timer >= config->bgImgBotFadeInTimeStart+config->bgImgBotFadeInTime ) + anim->active = 0; + + anim->timer++; +} + +int readMovie(u8* fb, movie_state_s* movie) +{ + if ( movie->curLoop == 0 ) + { + if ( NULL == movie->file ) + return -1; + + int prevOffset = movie->fileOffset; + + comp_manager_s* compPtr = movie->comp; + comp_manager_s compTmp; + if ( NULL != compPtr && MEMORY_COMPRESSED_STREAM == movie->loopStreamType && NULL != movie->loopStream + && movie->curFrame >= movie->loopStartFrame && movie->curFrame <= movie->loopEndFrame ) + { + int* loopFramesPtr = (int*)(movie->loopStream+movie->frameSize*2); + int* frameOffsets = loopFramesPtr+1; + compTmp = *movie->comp; + compTmp.frame_comp = (char*)loopFramesPtr + (*loopFramesPtr+1)*sizeof(int); + if ( movie->curFrame > movie->loopStartFrame ) + compTmp.frame_comp += prevOffset-frameOffsets[0]; + compPtr = &compTmp; + } + + int readMovieRes = readMovieFrame(movie->file, &movie->fileOffset, compPtr, (char*)fb, movie->frameSize); + if ( readMovieRes < 0 || movie->curFrame >= movie->framesCount-1 + || ( movie->loopReverse && movie->curFrame >= movie->loopEndFrame ) ) + { + if ( FILE_STREAM != movie->loopStreamType ) + { + fileHandleClose(movie->file); + #ifndef ARM9 + free(movie->file); + #endif + movie->file = NULL; + } + movie->curLoop = 1; + } + + if ( NULL != movie->loopStream && movie->curFrame >= movie->loopStartFrame && movie->curFrame <= movie->loopEndFrame ) + { + if ( MEMORY_STREAM != movie->loopStreamType ) + *(int*)(movie->loopStream+movie->frameSize*2+(movie->curFrame-movie->loopStartFrame+1)*sizeof(int)) = prevOffset; + + if ( MEMORY_COMPRESSED_STREAM == movie->loopStreamType && ( movie->curFrame == movie->loopEndFrame || readMovieRes < 0 ) ) + { + int* loopFramesPtr = (int*)(movie->loopStream+movie->frameSize*2); + int* frameOffsets = loopFramesPtr+1; + int remapOffset = (int)((char*)frameOffsets + (*loopFramesPtr)*sizeof(int) - frameOffsets[0]); + for ( int i = 0 ; i < *loopFramesPtr ; i++ ) + frameOffsets[i] += remapOffset; + } + + if ( movie->curFrame == movie->loopStartFrame ) + memcpy(movie->loopStartFrameStream, fb, movie->frameSize); + else if ( movie->curFrame == movie->loopEndFrame ) + memcpy(movie->loopEndFrameStream, fb, movie->frameSize); + else if ( MEMORY_STREAM == movie->loopStreamType ) + memcpy(movie->loopStream+(movie->curFrame-movie->loopStartFrame)*movie->frameSize, fb, movie->frameSize); + } + + movie->curFrame++; + if ( movie->curLoop > 0 ) + movie->curFrame = 0; + } + else if ( movie->loopsToDo < 0 || movie->curLoop <= movie->loopsToDo ) + { + if ( movie->loopReverse && movie->curLoop%2 == 1 ) + { + if ( movie->curFrame <= movie->loopTimeOnEndFrame ) + memcpy(fb, movie->loopEndFrameStream, movie->frameSize); + else if ( movie->curFrame >= movie->loopTimeOnEndFrame+movie->loopEndFrame-movie->loopStartFrame ) + memcpy(fb, movie->loopStartFrameStream, movie->frameSize); + else + { + int frameToGet = (movie->loopEndFrame-movie->loopStartFrame)-(movie->curFrame-movie->loopTimeOnEndFrame); + if ( MEMORY_STREAM == movie->loopStreamType ) + memcpy(fb, movie->loopStream + movie->frameSize*frameToGet, movie->frameSize); + else + { + if ( NULL != movie->comp ) + { + comp_manager_s compTmp = *movie->comp; + compTmp.reverse = 1; + if ( frameToGet == movie->loopEndFrame-movie->loopStartFrame-1 ) + compTmp.frame_prev_read = movie->loopEndFrameStream; + int offset = *(int*)(movie->loopStream + 2*movie->frameSize + (2+frameToGet)*sizeof(int)); + if ( MEMORY_COMPRESSED_STREAM == movie->loopStreamType ) + { + compTmp.frame_comp = (char*)offset; + readCompressedMovieFrameFromBuffer(&compTmp, (char*)fb, movie->frameSize ); + } + else + readMovieFrame(movie->file, &offset, &compTmp, (char*)fb, movie->frameSize); + } + else + { + int fileOffset = (frameToGet+movie->loopStartFrame)*movie->frameSize; + readMovieFrame(movie->file, &fileOffset, NULL, (char*)fb, movie->frameSize); + } + } + } + } + else + { + if ( movie->curFrame <= movie->loopTimeOnStartFrame ) + memcpy(fb, movie->loopStartFrameStream, movie->frameSize); + else if ( movie->curFrame >= movie->loopTimeOnStartFrame+movie->loopEndFrame-movie->loopStartFrame ) + memcpy(fb, movie->loopEndFrameStream, movie->frameSize); + else + { + int frameToGet = movie->curFrame-movie->loopTimeOnStartFrame; + if ( MEMORY_STREAM == movie->loopStreamType ) + memcpy(fb, movie->loopStream+movie->frameSize*frameToGet, movie->frameSize); + else + { + if ( NULL != movie->comp ) + { + comp_manager_s compTmp = *movie->comp; + if ( frameToGet == 1 ) + compTmp.frame_prev_read = movie->loopStartFrameStream; + int offset = *(int*)(movie->loopStream + 2*movie->frameSize + (1+frameToGet)*sizeof(int)); + if ( MEMORY_COMPRESSED_STREAM == movie->loopStreamType ) + { + compTmp.frame_comp = (char*)offset; + readCompressedMovieFrameFromBuffer(&compTmp, (char*)fb, movie->frameSize ); + } + else + readMovieFrame(movie->file, &offset, &compTmp, (char*)fb, movie->frameSize); + } + else + { + int fileOffset = (frameToGet+movie->loopStartFrame)*movie->frameSize; + readMovieFrame(movie->file, &fileOffset, NULL, (char*)fb, movie->frameSize); + } + } + } + } + + movie->curFrame++; + if ( movie->curFrame > movie->loopFramesCount ) + { + movie->curFrame = 0; + movie->curLoop++; + } + } + else + return -1; + + return 0; +} + +void animExit() +{ + if (anim) { + if (anim->topMovie.file) + fileHandleClose(anim->topMovie.file); + if (anim->botMovie.file) + fileHandleClose(anim->botMovie.file); +#ifndef ARM9 + if (anim->top3DMovie.file) + fileHandleClose(anim->top3DMovie.file); + + if (anim->topMovie.file) + free(anim->topMovie.file); + if (anim->topMovie.comp) + { + free(anim->topMovie.comp->frame_prev_write); // Same as frame_prev_read + free(anim->topMovie.comp->frame_comp); + free(anim->topMovie.comp); + } + if (anim->topMovie.loopStream) + free(anim->topMovie.loopStream); + + if (anim->top3DMovie.file) + free(anim->top3DMovie.file); + if (anim->top3DMovie.comp) + { + free(anim->top3DMovie.comp->frame_prev_write); // Same as frame_prev_read + free(anim->top3DMovie.comp->frame_comp); + free(anim->top3DMovie.comp); + } + if (anim->top3DMovie.loopStream) + free(anim->top3DMovie.loopStream); + + if (anim->botMovie.file) + free(anim->botMovie.file); + if (anim->botMovie.comp) + { + free(anim->botMovie.comp->frame_prev_write); // Same as frame_prev_read + free(anim->botMovie.comp->frame_comp); + free(anim->botMovie.comp); + } + if (anim->botMovie.loopStream) + free(anim->botMovie.loopStream); + free(anim); +#endif + anim = NULL; + } +} diff --git a/source/anim.h b/source/anim.h new file mode 100644 index 0000000..e00b92e --- /dev/null +++ b/source/anim.h @@ -0,0 +1,74 @@ +#ifndef _anim_h_ +#define _anim_h_ + +#include "config.h" +#include "movie.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int frameSize; + + void* file; + int fileOffset; + int curFrame; + int framesCount; + + comp_manager_s* comp; + + int curLoop; + int loopsToDo; + + int loopFramesCount; + + int loopStreamType; + char* loopStream; + int loopStreamSize; + int loopReverse; + + char* loopStartFrameStream; + int loopStartFrame; + int loopTimeOnStartFrame; + + char* loopEndFrameStream; + int loopEndFrame; + int loopTimeOnEndFrame; +} movie_state_s; + +int readMovie(u8* fb, movie_state_s* movie); + +typedef struct { + int timer; + int active; + + u8 bgTop1[3]; + u8 bgTop2[3]; + u8 bgBot[3]; + u8 highlight[4]; + u8 borders[4]; + u8 fntDef[4]; + u8 fntSel[4]; + u8 fntBot[4]; + u8 thumbFade; + u8 imgTopFade; + u8 imgBotFade; + + movie_state_s topMovie; + movie_state_s top3DMovie; + movie_state_s botMovie; +} anim_state_s; + +anim_state_s* anim; + +void animInit(); +void animSetup(); +void incrementAnimTime(); +void animExit(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/arm9/source/3dsheaders.h b/source/arm9/source/3dsheaders.h new file mode 100644 index 0000000..6e9e928 --- /dev/null +++ b/source/arm9/source/3dsheaders.h @@ -0,0 +1,144 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* Adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h +*/ + +#pragma once + +typedef struct __attribute__((packed)) +{ + u32 address; + u32 phyRegionSize; + u32 size; +} CodeSetInfo; + +typedef struct __attribute__((packed)) +{ + u32 saveDataSize[2]; + u32 jumpID[2]; + u8 reserved[0x30]; +} SystemInfo; + +typedef struct __attribute__((packed)) +{ + char appTitle[8]; + u8 reserved1[5]; + u8 flag; + u8 remasterVersion[2]; + CodeSetInfo textCodeSet; + u32 stackSize; + CodeSetInfo roCodeSet; + u8 reserved2[4]; + CodeSetInfo dataCodeSet; + u32 bssSize; + char depends[0x180]; + SystemInfo systemInfo; +} SystemControlInfo; + +typedef struct __attribute__((packed)) +{ + SystemControlInfo systemControlInfo; + u8 aci[0x200]; + u8 accessDescSig[0x100]; + u8 ncchPubKey[0x100]; + u8 aciLim[0x200]; +} ExHeader; + +typedef struct __attribute__((packed)) +{ + u8 sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256 + char magic[4]; //NCCH + u32 contentSize; //Media unit + u8 partitionId[8]; + u8 makerCode[2]; + u16 version; + u8 reserved1[4]; + u8 programID[8]; + u8 reserved2[0x10]; + u8 logoHash[0x20]; //Logo Region SHA-256 hash + char productCode[0x10]; + u8 exHeaderHash[0x20]; //Extended header SHA-256 hash + u32 exHeaderSize; //Extended header size + u32 reserved3; + u8 flags[8]; + u32 plainOffset; //Media unit + u32 plainSize; //Media unit + u32 logoOffset; //Media unit + u32 logoSize; //Media unit + u32 exeFsOffset; //Media unit + u32 exeFsSize; //Media unit + u32 exeFsHashSize; //Media unit + u32 reserved4; + u32 romFsOffset; //Media unit + u32 romFsSize; //Media unit + u32 romFsHashSize; //Media unit + u32 reserved5; + u8 exeFsHash[0x20]; //ExeFS superblock SHA-256 hash + u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash +} Ncch; + +typedef struct __attribute__((packed)) +{ + Ncch ncch; + ExHeader exHeader; +} Cxi; + +typedef struct __attribute__((packed)) +{ + char sigIssuer[0x40]; + u8 eccPubKey[0x3C]; + u8 version; + u8 caCrlVersion; + u8 signerCrlVersion; + u8 titleKey[0x10]; + u8 reserved1; + u8 ticketId[8]; + u8 consoleId[4]; + u8 titleId[8]; + u8 reserved2[2]; + u16 ticketTitleVersion; + u8 reserved3[8]; + u8 licenseType; + u8 ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles. + u8 reserved4[0x2A]; + u8 unk[4]; //eShop Account ID? + u8 reserved5; + u8 audit; + u8 reserved6[0x42]; + u8 limits[0x40]; + u8 contentIndex[0xAC]; +} Ticket; + +typedef struct __attribute__((packed)) +{ + u8 keyX[0x10]; + u8 keyY[0x10]; + u8 ctr[0x10]; + char size[8]; + u8 reserved[8]; + u8 ctlBlock[0x10]; + char magic[4]; + u8 reserved2[0xC]; + u8 slot0x16keyX[0x10]; +} Arm9Bin; diff --git a/source/arm9/source/bs-start.s b/source/arm9/source/bs-start.s index d8fa21e..986ec89 100644 --- a/source/arm9/source/bs-start.s +++ b/source/arm9/source/bs-start.s @@ -7,6 +7,11 @@ _start: @ Change the stack pointer mov sp, #0x27000000 + @ Store main arguments + mov r9, r0 + mov r10, r1 + mov r11, r2 + @ Give read/write access to all the memory regions ldr r5, =0x33333333 mcr p15, 0, r5, c5, c0, 2 @ write data access @@ -21,9 +26,7 @@ _start: ldr r5, =0x20000035 @ 20000000 128M ldr r6, =0x1FF00027 @ 1FF00000 1M ldr r7, =0x1800002D @ 18000000 8M - mov r10, #0x25 - mov r11, #0x25 - mov r12, #0x25 + mov r8, #0x25 mcr p15, 0, r0, c6, c0, 0 mcr p15, 0, r1, c6, c1, 0 mcr p15, 0, r2, c6, c2, 0 @@ -32,9 +35,9 @@ _start: mcr p15, 0, r5, c6, c5, 0 mcr p15, 0, r6, c6, c6, 0 mcr p15, 0, r7, c6, c7, 0 - 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 + mcr p15, 0, r8, c3, c0, 0 @ Write bufferable 0, 2, 5 + mcr p15, 0, r8, c2, c0, 0 @ Data cacheable 0, 2, 5 + mcr p15, 0, r8, c2, c0, 1 @ Inst cacheable 0, 2, 5 @ Enable caches mrc p15, 0, r4, c1, c0, 0 @ read control register @@ -55,7 +58,12 @@ _start: mov r1, #0x340 str r1, [r0] - bl main + @ Restore main arguments + mov r0, r9 + mov r1, r10 + mov r2, r11 + + bl startCtrBootManager .die: b .die diff --git a/source/arm9/source/common.c b/source/arm9/source/common.c new file mode 100644 index 0000000..c5af4c6 --- /dev/null +++ b/source/arm9/source/common.c @@ -0,0 +1,23 @@ +#include "common.h" + +u32 hexAtoi(const char *in, u32 digits) +{ + u32 res = 0; + char *tmp = (char *)in; + + for(u32 i = 0; i < digits && *tmp != 0; tmp++, i++) + res = (*tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0') + (res << 4); + + return res; +} + +u32 decAtoi(const char *in, u32 digits) +{ + u32 res = 0; + char *tmp = (char *)in; + + for(u32 i = 0; i < digits && *tmp != 0; tmp++, i++) + res = *tmp - '0' + res * 10; + + return res; +} diff --git a/source/arm9/source/common.h b/source/arm9/source/common.h index 43dd670..271f0a9 100644 --- a/source/arm9/source/common.h +++ b/source/arm9/source/common.h @@ -39,15 +39,17 @@ #define align(v,a) \ (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) -typedef enum -{ - GFX_TOP = 0, ///< Top screen - GFX_BOTTOM = 1 ///< Bottom screen -}gfxScreen_t; +#define CFG_SYSPROT9 (*(vu8 *)0x10000000) +#define CFG_BOOTENV (*(vu32 *)0x10010000) +#define CFG_UNITINFO (*(vu8 *)0x10010010) +#define CFG_TWLUNITINFO (*(vu8 *)0x10010014) +#define OTP_DEVCONSOLEID (*(vu64 *)0x10012000) +#define OTP_TWLCONSOLEID (*(vu64 *)0x10012100) +#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC) +#define PDN_SPI_CNT (*(vu32 *)0x101401C0) +#define ISN3DS (PDN_MPCORE_CFG == 7) +#define ISDEVUNIT (CFG_UNITINFO != 0) -typedef enum -{ - GFX_LEFT = 0, ///< Left eye framebuffer - GFX_RIGHT = 1,///< Right eye framebuffer -}gfx3dSide_t; +u32 hexAtoi(const char *in, u32 digits); +u32 decAtoi(const char *in, u32 digits); diff --git a/source/arm9/source/crypto.c b/source/arm9/source/crypto.c new file mode 100644 index 0000000..0a99833 --- /dev/null +++ b/source/arm9/source/crypto.c @@ -0,0 +1,500 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* Crypto libs from http://github.com/b1l1s/ctr +* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233 +* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c +* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c +*/ + +#include "crypto.h" + +//#include "memory.h" +#include "strings.h" +//#include "utils.h" +#include "fatfs/sdmmc/sdmmc.h" + +/**************************************************************** +* Crypto libs +****************************************************************/ + +/* original version by megazig */ + +#ifndef __thumb__ +#define BSWAP32(x) {\ + __asm__\ + (\ + "eor r1, %1, %1, ror #16\n\t"\ + "bic r1, r1, #0xFF0000\n\t"\ + "mov %0, %1, ror #8\n\t"\ + "eor %0, %0, r1, lsr #8\n\t"\ + :"=r"(x)\ + :"0"(x)\ + :"r1"\ + );\ +}; + +#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\ +__asm__\ + (\ + "adds %0, %4\n\t"\ + "addcss %1, %1, #1\n\t"\ + "addcss %2, %2, #1\n\t"\ + "addcs %3, %3, #1\n\t"\ + : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ + : "r"(u32_0)\ + : "cc"\ + );\ +} +#else +#define BSWAP32(x) {x = __builtin_bswap32(x);} + +#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\ +__asm__\ + (\ + "mov r4, #0\n\t"\ + "add %0, %0, %4\n\t"\ + "adc %1, %1, r4\n\t"\ + "adc %2, %2, r4\n\t"\ + "adc %3, %3, r4\n\t"\ + : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ + : "r"(u32_0)\ + : "cc", "r4"\ + );\ +} +#endif /*__thumb__*/ + +static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode) +{ + u32 *key32 = (u32 *)key; + *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; + + if(keyslot <= 3) + { + if((mode & AES_CNT_INPUT_ORDER) == AES_INPUT_REVERSED) + { + REGs_AESTWLKEYS[keyslot][keyType][0] = key32[3]; + REGs_AESTWLKEYS[keyslot][keyType][1] = key32[2]; + REGs_AESTWLKEYS[keyslot][keyType][2] = key32[1]; + REGs_AESTWLKEYS[keyslot][keyType][3] = key32[0]; + } + else + { + REGs_AESTWLKEYS[keyslot][keyType][0] = key32[0]; + REGs_AESTWLKEYS[keyslot][keyType][1] = key32[1]; + REGs_AESTWLKEYS[keyslot][keyType][2] = key32[2]; + REGs_AESTWLKEYS[keyslot][keyType][3] = key32[3]; + } + } + + else if(keyslot < 0x40) + { + *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE; + + REG_AESKEYFIFO[keyType] = key32[0]; + REG_AESKEYFIFO[keyType] = key32[1]; + REG_AESKEYFIFO[keyType] = key32[2]; + REG_AESKEYFIFO[keyType] = key32[3]; + } +} + +static void aes_use_keyslot(u8 keyslot) +{ + if(keyslot > 0x3F) + return; + + *REG_AESKEYSEL = keyslot; + *REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */ +} + +static void aes_setiv(const void *iv, u32 mode) +{ + const u32 *iv32 = (const u32 *)iv; + *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; + + //Word order for IV can't be changed in REG_AESCNT and always default to reversed + if(mode & AES_INPUT_NORMAL) + { + REG_AESCTR[0] = iv32[3]; + REG_AESCTR[1] = iv32[2]; + REG_AESCTR[2] = iv32[1]; + REG_AESCTR[3] = iv32[0]; + } + else + { + REG_AESCTR[0] = iv32[0]; + REG_AESCTR[1] = iv32[1]; + REG_AESCTR[2] = iv32[2]; + REG_AESCTR[3] = iv32[3]; + } +} + +static void aes_advctr(void *ctr, u32 val, u32 mode) +{ + u32 *ctr32 = (u32 *)ctr; + + int i; + if(mode & AES_INPUT_BE) + { + for(i = 0; i < 4; ++i) //Endian swap + BSWAP32(ctr32[i]); + } + + if(mode & AES_INPUT_NORMAL) + { + ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val); + } + else + { + ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val); + } + + if(mode & AES_INPUT_BE) + { + for(i = 0; i < 4; ++i) //Endian swap + BSWAP32(ctr32[i]); + } +} + +static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode) +{ + u32 *ctr32 = (u32 *)ctr; + int i; + if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN) + { + for(i = 0; i < 4; ++i) + BSWAP32(ctr32[i]); + } + + if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER) + { + u32 temp = ctr32[0]; + ctr32[0] = ctr32[3]; + ctr32[3] = temp; + + temp = ctr32[1]; + ctr32[1] = ctr32[2]; + ctr32[2] = temp; + } +} + +static void aes_batch(void *dst, const void *src, u32 blockCount) +{ + *REG_AESBLKCNT = blockCount << 16; + *REG_AESCNT |= AES_CNT_START; + + const u32 *src32 = (const u32 *)src; + u32 *dst32 = (u32 *)dst; + + u32 wbc = blockCount; + u32 rbc = blockCount; + + while(rbc) + { + if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) //There's space for at least 4 ints + { + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + wbc--; + } + + if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) //At least 4 ints available for read + { + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + rbc--; + } + } +} + +void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode) +{ + *REG_AESCNT = mode | + AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | + AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN | + AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE; + + u32 blocks; + while(blockCount != 0) + { + if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE + && (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE) + aes_setiv(iv, ivMode); + + blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount; + + //Save the last block for the next decryption CBC batch's iv + if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE) + { + memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); + aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); + } + + //Process the current batch + aes_batch(dst, src, blocks); + + //Save the last block for the next encryption CBC batch's iv + if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE) + { + memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); + aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); + } + + //Advance counter for CTR mode + else if((mode & AES_ALL_MODES) == AES_CTR_MODE) + aes_advctr(iv, blocks, ivMode); + + src += blocks * AES_BLOCK_SIZE; + dst += blocks * AES_BLOCK_SIZE; + blockCount -= blocks; + } +} + +static void sha_wait_idle() +{ + while(*REG_SHA_CNT & 1); +} + +void sha(void *res, const void *src, u32 size, u32 mode) +{ + sha_wait_idle(); + *REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; + + const u32 *src32 = (const u32 *)src; + int i; + while(size >= 0x40) + { + sha_wait_idle(); + for(i = 0; i < 4; ++i) + { + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + } + + size -= 0x40; + } + + sha_wait_idle(); + memcpy((void *)REG_SHA_INFIFO, src32, size); + + *REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND; + + while(*REG_SHA_CNT & SHA_FINAL_ROUND); + sha_wait_idle(); + + u32 hashSize = SHA_256_HASH_SIZE; + if(mode == SHA_224_MODE) + hashSize = SHA_224_HASH_SIZE; + else if(mode == SHA_1_MODE) + hashSize = SHA_1_HASH_SIZE; + + memcpy(res, (void *)REG_SHA_HASH, hashSize); +} + +/*****************************************************************/ + +__attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE]; +static u8 nandSlot; +static u32 fatStart; + +void ctrNandInit(void) +{ + __attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE], + shaSum[SHA_256_HASH_SIZE]; + + sdmmc_get_cid(1, (u32 *)cid); + sha(shaSum, cid, sizeof(cid), SHA_256_MODE); + memcpy(nandCtr, shaSum, sizeof(nandCtr)); + + if(ISN3DS) + { + __attribute__((aligned(4))) u8 keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; + aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + + nandSlot = 0x05; + fatStart = 0x5CAD7; + } + else + { + nandSlot = 0x04; + fatStart = 0x5CAE5; + } +} + +int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf) +{ + __attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)]; + memcpy(tmpCtr, nandCtr, sizeof(nandCtr)); + aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Read only SYSNAND + int result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf); + + //Decrypt + aes_use_keyslot(nandSlot); + aes(outbuf, outbuf, sectorCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + return result; +} + +int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf) +{ + u8 *buffer = (u8 *)0x23000000; + u32 bufferSize = 0xF00000; + + __attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)]; + memcpy(tmpCtr, nandCtr, sizeof(nandCtr)); + aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(nandSlot); + + int result = 0; + for(u32 tempSector = 0; tempSector < sectorCount && !result; tempSector += bufferSize / 0x200) + { + u32 tempCount = (bufferSize / 0x200) < (sectorCount - tempSector) ? (bufferSize / 0x200) : (sectorCount - tempSector); + + memcpy(buffer, inbuf + (tempSector * 0x200), tempCount * 0x200); + + //Encrypt + aes(buffer, buffer, tempCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Write + result = sdmmc_nand_writesectors(tempSector + sector + fatStart, tempCount, buffer); + } + + return result; +} + +bool decryptExeFs(Cxi *cxi) +{ + if(memcmp(cxi->ncch.magic, "NCCH", 4) != 0) return false; + + u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200; + u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200; + __attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0}; + + for(u32 i = 0; i < 8; i++) + ncchCtr[7 - i] = cxi->ncch.partitionId[i]; + ncchCtr[8] = 2; + + aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x2C); + aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + return memcmp(cxi, "FIRM", 4) == 0; +} + +bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize) +{ + if(memcmp(ticket->sigIssuer, "Root", 4) != 0) return false; + + __attribute__((aligned(4))) const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C}; + __attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE], + cetkIv[AES_BLOCK_SIZE] = {0}; + memcpy(titleKey, ticket->titleKey, sizeof(titleKey)); + memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId)); + + aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x3D); + aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + __attribute__((aligned(4))) u8 ncchIv[AES_BLOCK_SIZE] = {0}; + + aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x16); + aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + return decryptExeFs(cxi); +} + +void kernel9Loader(Arm9Bin *arm9Section) +{ + //Determine the kernel9loader version + u32 k9lVersion; + switch(arm9Section->magic[3]) + { + case 0xFF: + k9lVersion = 0; + break; + case '1': + k9lVersion = 1; + break; + default: + k9lVersion = 2; + } + + u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800); + if(*startOfArm9Bin == 0x47704770 || *startOfArm9Bin == 0xB0862000) return; //Already decrypted + + //Set 0x11 keyslot + __attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = { + {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8}, + {0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99} + }, + key2s[2][AES_BLOCK_SIZE] = { + {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}, + {0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25} + }; + + aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + + u8 arm9BinSlot = k9lVersion == 0 ? 0x15 : 0x16; + + //Set keyX + __attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE]; + aes_use_keyslot(0x11); + aes(keyX, k9lVersion == 0 ? arm9Section->keyX : arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(arm9BinSlot, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Set keyY + __attribute__((aligned(4))) u8 keyY[AES_BLOCK_SIZE]; + memcpy(keyY, arm9Section->keyY, sizeof(keyY)); + aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Set CTR + __attribute__((aligned(4))) u8 arm9BinCtr[AES_BLOCK_SIZE]; + memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr)); + + //Decrypt ARM9 binary + aes_use_keyslot(arm9BinSlot); + aes(startOfArm9Bin, startOfArm9Bin, decAtoi(arm9Section->size, sizeof(arm9Section->size)) / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + //if(*startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000) error("Failed to decrypt the ARM9 binary."); +} + +void computePinHash(u8 *outbuf, const u8 *inbuf) +{ + __attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE], + cipherText[AES_BLOCK_SIZE]; + + sdmmc_get_cid(1, (u32 *)cid); + aes_use_keyslot(0x04); //Console-unique keyslot whose keys are set by the ARM9 bootROM + aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE); +} diff --git a/source/arm9/source/crypto.h b/source/arm9/source/crypto.h new file mode 100644 index 0000000..ddbae83 --- /dev/null +++ b/source/arm9/source/crypto.h @@ -0,0 +1,118 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* Crypto libs from http://github.com/b1l1s/ctr +* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233 +* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c +* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c +*/ + +#pragma once + +#include "common.h" +#include "firm.h" +#include "3dsheaders.h" + +/**************************AES****************************/ +#define REG_AESCNT ((vu32 *)0x10009000) +#define REG_AESBLKCNT ((vu32 *)0x10009004) +#define REG_AESWRFIFO ((vu32 *)0x10009008) +#define REG_AESRDFIFO ((vu32 *)0x1000900C) +#define REG_AESKEYSEL ((vu8 *)0x10009010) +#define REG_AESKEYCNT ((vu8 *)0x10009011) +#define REG_AESCTR ((vu32 *)0x10009020) + +#define REG_AESKEYFIFO ((vu32 *)0x10009100) +#define REG_AESKEYXFIFO ((vu32 *)0x10009104) +#define REG_AESKEYYFIFO ((vu32 *)0x10009108) + +#define REGs_AESTWLKEYS (*((vu32 (*)[4][3][4])0x10009040)) + +#define AES_CCM_DECRYPT_MODE (0u << 27) +#define AES_CCM_ENCRYPT_MODE (1u << 27) +#define AES_CTR_MODE (2u << 27) +#define AES_CTR_MODE (2u << 27) +#define AES_CBC_DECRYPT_MODE (4u << 27) +#define AES_CBC_ENCRYPT_MODE (5u << 27) +#define AES_ECB_DECRYPT_MODE (6u << 27) +#define AES_ECB_ENCRYPT_MODE (7u << 27) +#define AES_ALL_MODES (7u << 27) + +#define AES_CNT_START 0x80000000 +#define AES_CNT_INPUT_ORDER 0x02000000 +#define AES_CNT_OUTPUT_ORDER 0x01000000 +#define AES_CNT_INPUT_ENDIAN 0x00800000 +#define AES_CNT_OUTPUT_ENDIAN 0x00400000 +#define AES_CNT_FLUSH_READ 0x00000800 +#define AES_CNT_FLUSH_WRITE 0x00000400 + +#define AES_INPUT_BE (AES_CNT_INPUT_ENDIAN) +#define AES_INPUT_LE 0 +#define AES_INPUT_NORMAL (AES_CNT_INPUT_ORDER) +#define AES_INPUT_REVERSED 0 + +#define AES_BLOCK_SIZE 0x10 + +#define AES_KEYCNT_WRITE (1 << 0x7) +#define AES_KEYNORMAL 0 +#define AES_KEYX 1 +#define AES_KEYY 2 + +/**************************SHA****************************/ +#define REG_SHA_CNT ((vu32 *)0x1000A000) +#define REG_SHA_BLKCNT ((vu32 *)0x1000A004) +#define REG_SHA_HASH ((vu32 *)0x1000A040) +#define REG_SHA_INFIFO ((vu32 *)0x1000A080) + +#define SHA_CNT_STATE 0x00000003 +#define SHA_CNT_UNK2 0x00000004 +#define SHA_CNT_OUTPUT_ENDIAN 0x00000008 +#define SHA_CNT_MODE 0x00000030 +#define SHA_CNT_ENABLE 0x00010000 +#define SHA_CNT_ACTIVE 0x00020000 + +#define SHA_HASH_READY 0x00000000 +#define SHA_NORMAL_ROUND 0x00000001 +#define SHA_FINAL_ROUND 0x00000002 + +#define SHA_OUTPUT_BE SHA_CNT_OUTPUT_ENDIAN +#define SHA_OUTPUT_LE 0 + +#define SHA_256_MODE 0 +#define SHA_224_MODE 0x00000010 +#define SHA_1_MODE 0x00000020 + +#define SHA_256_HASH_SIZE (256 / 8) +#define SHA_224_HASH_SIZE (224 / 8) +#define SHA_1_HASH_SIZE (160 / 8) + +void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode); +void sha(void *res, const void *src, u32 size, u32 mode); + +void ctrNandInit(void); +int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); +int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf); +bool decryptExeFs(Cxi *cxi); +bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize); +void kernel9Loader(Arm9Bin *arm9Section); +void computePinHash(u8 *outbuf, const u8 *inbuf); diff --git a/source/arm9/source/fatfs/history.txt b/source/arm9/source/fatfs/00history.txt similarity index 70% rename from source/arm9/source/fatfs/history.txt rename to source/arm9/source/fatfs/00history.txt index b101820..2d71a3d 100644 --- a/source/arm9/source/fatfs/history.txt +++ b/source/arm9/source/fatfs/00history.txt @@ -3,73 +3,98 @@ ---------------------------------------------------------------------------- R0.00 (February 26, 2006) + Prototype. + R0.01 (April 29, 2006) - First stable version. + + The first release. + 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) @@ -80,12 +105,16 @@ R0.07 (April 01, 2009) 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. @@ -93,7 +122,9 @@ R0.07c (June 21, 2009) 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. @@ -101,7 +132,9 @@ R0.07e (November 03, 2009) 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) @@ -110,36 +143,48 @@ R0.08 (May 15, 2010) 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) @@ -148,10 +193,12 @@ R0.10 (October 02, 2013) 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. + Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. + R0.10a (January 15, 2014) + Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID) Added a configuration option of minimum sector size. (_MIN_SS) 2nd argument of f_rename() can have a drive number and it will be ignored. @@ -161,12 +208,16 @@ R0.10a (January 15, 2014) 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) + Fixed LFN entry is not deleted when 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) @@ -174,7 +225,64 @@ R0.10c (November 09, 2014) Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08) + R0.11 (February 09, 2015) + Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND) Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c) Fixed _FS_NORTC option does not work properly. (appeared at R0.10c) + + + +R0.11a (September 05, 2015) + + Fixed wrong media change can lead a deadlock at thread-safe configuration. + Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE) + Removed some code pages actually not exist on the standard systems. (_CODE_PAGE) + Fixed errors in the case conversion teble of code page 437 and 850 (ff.c). + Fixed errors in the case conversion teble of Unicode (cc*.c). + + + +R0.12 (April 12, 2016) + + Added support for exFAT file system. (_FS_EXFAT) + Added f_expand(). (_USE_EXPAND) + Changed some members in FINFO structure and behavior of f_readdir(). + Added an option _USE_CHMOD. + Removed an option _WORD_ACCESS. + Fixed errors in the case conversion table of Unicode (cc*.c). + + + +R0.12a (July 10, 2016) + + Added support for creating exFAT volume with some changes of f_mkfs(). + Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. + f_forward() is available regardless of _FS_TINY. + Fixed f_mkfs() creates wrong volume. (appeared at R0.12) + Fixed wrong memory read in create_name(). (appeared at R0.12) + Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. + + + +R0.12b (September 04, 2016) + + Made f_rename() be able to rename objects with the same name but case. + Fixed an error in the case conversion teble of code page 866. (ff.c) + Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) + Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) + Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) + Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) + Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) + Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) + + + +R0.12c (March 04, 2017) + + Improved write throughput at the fragmented file on the exFAT volume. + Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN. + Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12) + Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c) + diff --git a/source/arm9/source/fatfs/00readme.txt b/source/arm9/source/fatfs/00readme.txt index c46ac5f..dfa51ed 100644 --- a/source/arm9/source/fatfs/00readme.txt +++ b/source/arm9/source/fatfs/00readme.txt @@ -1,21 +1,21 @@ -FatFs Module Source Files R0.11 +FatFs Module Source Files R0.12c FILES - 00readme.txt This file. - history.txt Revision history. - ffconf.h Configuration file for FatFs module. - ff.h Common include file for FatFs and application module. - 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. + 00readme.txt This file. + 00history.txt Revision history. + ff.c FatFs module. + ffconf.h Configuration file of FatFs module. + ff.h Common include file for FatFs and application module. + diskio.h Common include file for FatFs and disk I/O module. + diskio.c An example of glue function to attach existing disk I/O module to FatFs. + integer.h Integer type definitions for FatFs. + option Optional external modules. Low level disk I/O module is not included in this archive because the FatFs - module is only a generic file system layer and not depend on any specific - storage device. You have to provide a low level disk I/O module that written - to control the target storage device. + module is only a generic file system layer and it does not depend on any specific + storage device. You have to provide a low level disk I/O module written to + control the storage device that attached to the target system. diff --git a/source/arm9/source/fatfs/diskio.c b/source/arm9/source/fatfs/diskio.c index 9e379e4..4dd504c 100644 --- a/source/arm9/source/fatfs/diskio.c +++ b/source/arm9/source/fatfs/diskio.c @@ -9,7 +9,11 @@ #include "diskio.h" /* FatFs lower layer API */ #include "sdmmc/sdmmc.h" +#include "../crypto.h" +/* Definitions of physical drive number for each media */ +#define SDCARD 0 +#define CTRNAND 1 /*-----------------------------------------------------------------------*/ /* Get Drive Status */ @@ -30,12 +34,26 @@ DSTATUS disk_status ( /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( - __attribute__((unused)) BYTE pdrv /* Physical drive nmuber to identify the drive */ ) { - sdmmc_sdcard_init(); - return RES_OK; + DSTATUS ret; + static u32 sdmmcInitResult = 4; + + if(sdmmcInitResult == 4) sdmmcInitResult = sdmmc_sdcard_init(); + + if(pdrv == CTRNAND) + { + if(!(sdmmcInitResult & 1)) + { + ctrNandInit(); + ret = 0; + } + else ret = STA_NOINIT; + } + else ret = (!(sdmmcInitResult & 2)) ? 0 : STA_NOINIT; + + return ret; } @@ -45,18 +63,14 @@ DSTATUS disk_initialize ( /*-----------------------------------------------------------------------*/ DRESULT disk_read ( - __attribute__((unused)) BYTE pdrv, /* Physical drive nmuber to identify the drive */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to read */ ) { - if (sdmmc_sdcard_readsectors(sector, count, buff)) { - return RES_PARERR; - } - - return RES_OK; + return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) || + (pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR; } @@ -67,18 +81,14 @@ DRESULT disk_read ( #if _USE_WRITE DRESULT disk_write ( - __attribute__((unused)) BYTE pdrv, /* Physical drive nmuber to identify the drive */ - const BYTE *buff, /* Data to be written */ + const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address in LBA */ UINT count /* Number of sectors to write */ ) { - if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { - return RES_PARERR; - } - - return RES_OK; + return ((pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) || + (pdrv == CTRNAND && !ctrNandWrite(sector, count, buff))) ? RES_OK : RES_PARERR; } #endif diff --git a/source/arm9/source/fatfs/ff.c b/source/arm9/source/fatfs/ff.c index 41de2fc..eaf9d85 100644 --- a/source/arm9/source/fatfs/ff.c +++ b/source/arm9/source/fatfs/ff.c @@ -1,10 +1,12 @@ /*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.11 (C)ChaN, 2015 +/ FatFs - Generic FAT file system module R0.12c / /-----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. / -/ Copyright (C) 2015, ChaN, all right reserved. +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: / / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. @@ -17,7 +19,7 @@ #include "ff.h" /* Declarations of FatFs API */ -#include "diskio.h" /* Declarations of disk I/O functions */ +#include "diskio.h" /* Declarations of device I/O functions */ /*-------------------------------------------------------------------------- @@ -26,64 +28,12 @@ ---------------------------------------------------------------------------*/ -#if _FATFS != 32020 /* Revision ID */ +#if _FATFS != 68300 /* 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 */ +/* DBCS code ranges and SBCS upper conversion tables */ #if _CODE_PAGE == 932 /* Japanese Shift-JIS */ #define _DF1S 0x81 /* DBC 1st byte range 1 start */ @@ -121,215 +71,196 @@ typedef struct { #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) */ +#elif _CODE_PAGE == 437 /* U.S. */ #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 _EXCVT {0x80,0x9A,0x45,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,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 == 720 /* Arabic */ #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 _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 == 737 /* Greek */ #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 _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,0xEF,0xF5,0xF0,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 == 771 /* KBL */ #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 _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,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic */ #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 _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 /* Latin 1 */ #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 _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 */ #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 _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,0xAC, \ + 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 */ #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 _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 */ #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 _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,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,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,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 860 /* Portuguese */ #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 _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,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 == 861 /* Icelandic */ #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 _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,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 == 862 /* Hebrew */ #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 _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,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 == 863 /* Canadian-French */ #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 _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,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 == 864 /* Arabic */ #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 _EXCVT {0x80,0x9A,0x45,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,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 == 865 /* Nordic */ #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 _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,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 == 866 /* Russian */ #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 _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,0x94,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 == 869 /* Greek 2 */ #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} +#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,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,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,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ -#if _USE_LFN -#error Cannot use LFN feature without valid code page. +#if _USE_LFN != 0 +#error Cannot enable LFN without valid code page. #endif #define _DF1S 0 @@ -344,7 +275,7 @@ WCHAR ff_wtoupper(WCHAR chr) #define IsLower(c) (((c)>='a')&&((c)<='z')) #define IsDigit(c) (((c)>='0')&&((c)<='9')) -#if _DF1S /* Code page is DBCS */ +#if _DF1S != 0 /* 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)) @@ -366,138 +297,300 @@ WCHAR ff_wtoupper(WCHAR chr) #endif /* _DF1S */ -/* Name status flags */ -#define NSFLAG 11 /* Index of name status byte in fn[] */ +/* Additional file attribute bits for internal use */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Additional file access control and file status flags for internal use */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Name status flags in fn[] */ +#define NSFLAG 11 /* Index of the name status byte */ #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 */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* Limits and boundaries */ +#define MAX_DIR 0x200000 /* Max size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */ +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ + + +/* FatFs refers the FAT structure as simple byte array instead of structure member +/ because the C structure is not binary compatible between different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* Error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* File system type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ +#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ +#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* exFAT: General secondary flags (WORD) */ +#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ -/* 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 MBR_Table 446 /* MBR: Offset of partition table in the MBR */ #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 */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + + +/* Post process after fatal error on file operation */ +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* 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 + + +/* Definitions of volume - partition conversion */ +#if _MULTI_PARTITION +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#else +#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 + + +/* 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 */ +#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 lock controls */ +#if _FS_LOCK != 0 +#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, containing directory (0:root) */ + DWORD ofs; /* Object ID 3, offset in the directory */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + + + +/*-------------------------------------------------------------------------- + Module Private Work Area -/*------------------------------------------------------------*/ -/* 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. -*/ +---------------------------------------------------------------------------*/ + +/* Remark: Variables defined here without initial value shall be guaranteed +/ zero/null at start-up. If not, the linker option or start-up routine is +/ not compliance with C standard. */ -#if _VOLUMES < 1 || _VOLUMES > 9 +#if _VOLUMES < 1 || _VOLUMES > 10 #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 +#if _FS_RPATH != 0 && _VOLUMES >= 2 static BYTE CurrVol; /* Current drive */ #endif -#if _FS_LOCK +#if _FS_LOCK != 0 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 _USE_LFN == 0 /* Non-LFN configuration */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#else /* LFN configuration */ #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) +#error Wrong _MAX_LFN value +#endif +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) + +#if _USE_LFN == 1 /* LFN enabled with static working buffer */ +#if _FS_EXFAT +static BYTE DirBuf[MAXDIRB(_MAX_LFN)]; /* Directory entry block scratchpad buffer */ +#endif +static WCHAR LfnBuf[_MAX_LFN + 1]; /* LFN enabled with static working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[MAXDIRB(_MAX_LFN)]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() #else -#error Wrong _USE_LFN setting +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif + +#elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + MAXDIRB(_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) #endif + +#else +#error Wrong _USE_LFN setting + #endif +#endif /* else _USE_LFN == 0 */ #ifdef _EXCVT -static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended characters */ +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ #endif @@ -512,6 +605,85 @@ static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended chara ---------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if _FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if _FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !_FS_READONLY */ + + + /*-----------------------------------------------------------------------*/ /* String functions */ /*-----------------------------------------------------------------------*/ @@ -522,39 +694,39 @@ 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); + if (cnt) { + do { + *d++ = *s++; + } while (--cnt); } -#endif - while (cnt--) - *d++ = *s++; } -/* Fill memory */ +/* Fill memory block */ static void mem_set (void* dst, int val, UINT cnt) { BYTE *d = (BYTE*)dst; - while (cnt--) + do { *d++ = (BYTE)val; + } while (--cnt); } -/* Compare memory to memory */ +/* Compare memory block */ static -int mem_cmp (const void* dst, const void* src, UINT cnt) { +int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */ const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; int r = 0; - while (cnt-- && (r = *d++ - *s++) == 0) ; + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + return r; } /* Check if chr is contained in the string */ static -int chk_chr (const char* str, int chr) { +int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ while (*str && *str != chr) str++; return *str; } @@ -562,16 +734,16 @@ int chk_chr (const char* str, int chr) { +#if _FS_REENTRANT /*-----------------------------------------------------------------------*/ /* 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); + return (fs && ff_req_grant(fs->sobj)) ? 1 : 0; } @@ -581,23 +753,19 @@ void unlock_fs ( FRESULT res /* Result code to be returned */ ) { - if (fs && - res != FR_NOT_ENABLED && - res != FR_INVALID_DRIVE && - res != FR_INVALID_OBJECT && - res != FR_TIMEOUT) { + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { ff_rel_grant(fs->sobj); } } -#endif +#endif +#if _FS_LOCK != 0 /*-----------------------------------------------------------------------*/ /* File lock control functions */ /*-----------------------------------------------------------------------*/ -#if _FS_LOCK static FRESULT chk_lock ( /* Check if the file can be accessed */ @@ -610,15 +778,16 @@ FRESULT chk_lock ( /* Check if the file can be accessed */ /* 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; + if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; } else { /* Blank entry */ be = 1; } } - if (i == _FS_LOCK) /* The object is not opened */ + 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; @@ -645,17 +814,17 @@ UINT inc_lock ( /* Increment object open counter and returns its index (0:Intern 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 (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) 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].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; Files[i].ctr = 0; } @@ -679,9 +848,9 @@ FRESULT dec_lock ( /* Decrement object open counter */ 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 */ + if (n > 0) n--; /* Decrement read mode open count */ Files[i].ctr = n; - if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ res = FR_OK; } else { res = FR_INT_ERR; /* Invalid index nunber */ @@ -701,8 +870,8 @@ void clear_lock ( /* Clear lock entries of the volume */ if (Files[i].fs == fs) Files[i].fs = 0; } } -#endif +#endif /* _FS_LOCK != 0 */ @@ -711,8 +880,8 @@ void clear_lock ( /* Clear lock entries of the volume */ /*-----------------------------------------------------------------------*/ #if !_FS_READONLY static -FRESULT sync_window ( - FATFS* fs /* File system object */ +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs /* File system object */ ) { DWORD wsect; @@ -740,9 +909,9 @@ FRESULT sync_window ( static -FRESULT move_window ( - FATFS* fs, /* File system object */ - DWORD sector /* Sector number to make appearance in the fs->win[] */ +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ ) { FRESULT res = FR_OK; @@ -766,12 +935,13 @@ FRESULT move_window ( +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Synchronize file system and strage device */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + static -FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */ +FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ FATFS* fs /* File system object */ ) { @@ -780,38 +950,37 @@ FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */ res = sync_window(fs); if (res == FR_OK) { - /* Update FSINFO sector if needed */ + /* Update FSInfo sector if needed */ if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { - /* Create FSINFO structure */ + /* 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 */ + 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_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* 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; + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; } return res; } -#endif +#endif /*-----------------------------------------------------------------------*/ /* Get sector# from cluster# */ /*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ -DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ +static +DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ FATFS* fs, /* File system object */ DWORD clst /* Cluster# to be converted */ ) @@ -827,19 +996,19 @@ DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ /*-----------------------------------------------------------------------*/ /* 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 */ +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ ) { UINT wc, bc; - BYTE *p; DWORD val; + FATFS *fs = obj->fs; - if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ val = 1; /* Internal error */ } else { @@ -852,21 +1021,46 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluste 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); + 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); + val = ld_word(fs->win + clst * 2 % SS(fs)); 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; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; break; - +#if _FS_EXFAT + case FS_EXFAT : + if (obj->objsize) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ + if (cofs <= clen) { + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ + break; + } + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (obj->n_frag != 0) { /* Is it on the growing edge? */ + val = 0x7FFFFFFF; /* Generate EOC */ + } else { + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + } + break; + } + } + /* go to default */ +#endif default: val = 1; /* Internal error */ } @@ -878,209 +1072,405 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluste +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* 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 */ +static +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding 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; + FRESULT res = FR_INT_ERR; - } else { + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { - case FS_FAT12 : + case FS_FAT12 : /* Bitfield items */ 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 = 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 = fs->win + bc % SS(fs); *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); fs->wflag = 1; break; - case FS_FAT16 : + case FS_FAT16 : /* WORD aligned items */ 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); + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); fs->wflag = 1; break; - case FS_FAT32 : + case FS_FAT32 : /* DWORD aligned items */ +#if _FS_EXFAT + case FS_EXFAT : +#endif 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); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); fs->wflag = 1; break; - - default : - res = FR_INT_ERR; } } - return res; } + #endif /* !_FS_READONLY */ +#if _FS_EXFAT && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* FAT handling - Remove a cluster chain */ +/* exFAT: Accessing FAT and Allocation Bitmap */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT remove_chain ( - FATFS* fs, /* File system object */ - DWORD clst /* Cluster# to remove a chain from */ + +/*--------------------------------------*/ +/* Find a contiguous free cluster block */ +/*--------------------------------------*/ + +static +DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = SS(fs); + } + if (!bv) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ + } else { + scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*----------------------------------------*/ +/* Set/Clear a block of allocation bitmap */ +/*----------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Fill the first fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_first_frag ( + _FDID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + + +/*---------------------------------------------*/ +/* Fill the last fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_last_frag ( + _FDID* obj, /* Pointer to the corresponding object */ + DWORD lcl, /* Last cluster of the fragment */ + DWORD term /* Value to set the last FAT entry */ ) { FRESULT res; + + while (obj->n_frag > 0) { /* Create the last chain on the FAT */ + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); + if (res != FR_OK) return res; + obj->n_frag--; + } + return FR_OK; +} + +#endif /* _FS_EXFAT && !_FS_READONLY */ + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + _FDID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:an entire chain) */ +) +{ + FRESULT res = FR_OK; DWORD nxt; + FATFS *fs = obj->fs; +#if _FS_EXFAT || _USE_TRIM + DWORD scl = clst, ecl = clst; +#endif #if _USE_TRIM - DWORD scl = clst, ecl = clst, rt[2]; + DWORD rt[2]; #endif - if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ - res = FR_INT_ERR; + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ - } 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; + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if _FS_EXFAT || _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; } +#endif #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; - } + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ #endif - clst = nxt; /* Next cluster */ + scl = ecl = nxt; } - } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ - return res; -} +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Does the object have no chain? */ + obj->stat = 0; /* Change the object status 'initial' */ + } else { + if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */ + clst = obj->sclust; /* Follow the chain to check if it gets contiguous */ + while (clst != pclst) { + nxt = get_fat(obj, clst); + if (nxt < 2) return FR_INT_ERR; + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; + if (nxt != clst + 1) break; /* Not contiguous? */ + clst++; + } + if (clst == pclst) { /* Has the chain got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } + } + } #endif + return FR_OK; +} /*-----------------------------------------------------------------------*/ -/* FAT handling - Stretch or Create a cluster chain */ +/* FAT handling - Stretch a chain or Create a new 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. */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ ) { DWORD cs, ncl, scl; FRESULT res; + FATFS *fs = obj->fs; - if (clst == 0) { /* Create a new chain */ - scl = fs->last_clust; /* Get suggested start point */ - if (!scl || scl >= fs->n_fatent) scl = 1; + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Get suggested cluster to start from */ + if (scl == 0 || 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 */ + else { /* Stretch current chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid FAT 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 */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous' */ + } else { /* It is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + if (obj->stat != 2) { /* Is the file non-contiguous? */ + if (ncl == clst + 1) { /* Is the cluster next to previous one? */ + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */ + } else { /* New fragment */ + if (obj->n_frag == 0) obj->n_frag = 1; + res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */ + if (res == FR_OK) obj->n_frag = 1; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + 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(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ + if (ncl == scl) return 0; /* No free cluster */ + } + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ } - 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; - } + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; } else { - ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */ } - return ncl; /* Return new cluster number or error code */ + return ncl; /* Return new cluster number or error status */ } + #endif /* !_FS_READONLY */ +#if _USE_FASTSEEK /*-----------------------------------------------------------------------*/ /* 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# */ + FSIZE_t ofs /* File offset to be converted to cluster# */ ) { DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; tbl = fp->cltbl + 1; /* Top of CLMT */ - cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + cl = (DWORD)(ofs / SS(fs) / 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 (ncl == 0) 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 */ @@ -1091,42 +1481,43 @@ DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ /*-----------------------------------------------------------------------*/ static -FRESULT dir_sdi ( +FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to directory object */ - UINT idx /* Index of directory table */ + DWORD ofs /* Offset of directory table */ ) { - DWORD clst, sect; - UINT ic; + DWORD csz, clst; + FATFS *fs = dp->obj.fs; - 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 */ + if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ 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; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } 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 (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, 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; + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; } - sect = clust2sect(dp->fs, clst); + dp->sect = clust2sect(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 */ + dp->clust = clst; /* Current cluster# */ + if (!dp->sect) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ return FR_OK; } @@ -1139,64 +1530,63 @@ FRESULT dir_sdi ( /*-----------------------------------------------------------------------*/ static -FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ +FRESULT dir_next ( /* FR_OK(0):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; + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; #if !_FS_READONLY - UINT c; + UINT n; #endif + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ - 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 (ofs % SS(fs) == 0) { /* 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; + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; 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 ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* 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 (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ 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++; + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + /* Clean-up the stretched table */ + if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ + for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ + fs->wflag = 1; + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; } - dp->fs->winsect -= c; /* Rewind window offset */ + fs->winsect -= n; /* Restore 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 */ + if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ #endif } - dp->clust = clst; /* Initialize data for new cluster */ - dp->sect = clust2sect(dp->fs, clst); + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(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 */ + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ return FR_OK; } @@ -1204,58 +1594,66 @@ FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Directory handling - Reserve directory entry */ +/* Directory handling - Reserve a block of directory entries */ /*-----------------------------------------------------------------------*/ -#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 dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ ) { FRESULT res; UINT n; + FATFS *fs = dp->obj.fs; res = dir_sdi(dp, 0); if (res == FR_OK) { n = 0; do { - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ +#if _FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif 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); + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ } + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ return res; } -#endif + +#endif /* !_FS_READONLY */ /*-----------------------------------------------------------------------*/ -/* Directory handling - Load/Store start cluster number */ +/* FAT: 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 ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ ) { DWORD cl; - cl = LD_WORD(dir + DIR_FstClusLO); - if (fs->fs_type == FS_FAT32) - cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } return cl; } @@ -1264,128 +1662,143 @@ DWORD ld_clust ( #if !_FS_READONLY static void st_clust ( - BYTE* dir, /* Pointer to the directory entry */ + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ DWORD cl /* Value to be set */ ) { - ST_WORD(dir + DIR_FstClusLO, cl); - ST_WORD(dir + DIR_FstClusHI, cl >> 16); + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } } #endif - -/*-----------------------------------------------------------------------*/ -/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN +#if _USE_LFN != 0 +/*------------------------------------------------------------------------*/ +/* FAT-LFN: LFN handling */ +/*------------------------------------------------------------------------*/ 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 */ +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN 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 */ +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the 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 */ + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; } else { - if (uc != 0xFFFF) return 0; /* Check filler */ + 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; + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ - return 1; /* The part of LFN matched */ + return 1; /* The part of LFN matched */ } - +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ static -int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ - WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */ - BYTE* dir /* Pointer to the directory entry */ +int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ ) { UINT i, s; WCHAR wc, uc; - i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ - 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 */ + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { 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; + return 1; /* The part of LFN is valid */ } +#endif #if !_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ 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 */ +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ ) { UINT i, s; WCHAR wc; - dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Chksum] = sum; /* Set checksum */ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ dir[LDIR_Type] = 0; - ST_WORD(dir + LDIR_FstClusLO, 0); + st_word(dir + LDIR_FstClusLO, 0); - i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + i = (ord - 1) * 13; /* Get offset in the LFN working 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 */ + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ } while (++s < 13); - if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* Bottom LFN part is the start of LFN sequence */ + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ } -#endif -#endif - +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ +#if _USE_LFN != 0 && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Create numbered name */ +/* FAT-LFN: Create a Numbered SFN */ /*-----------------------------------------------------------------------*/ -#if _USE_LFN + static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ @@ -1402,7 +1815,7 @@ void gen_numname ( mem_cpy(dst, src, 11); - if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sr = seq; while (*lfn) { /* Create a CRC */ wc = *lfn++; @@ -1418,7 +1831,7 @@ void gen_numname ( /* itoa (hexdecimal) */ i = 7; do { - c = (seq % 16) + '0'; + c = (BYTE)((seq % 16) + '0'); if (c > '9') c += 7; ns[i--] = c; seq /= 16; @@ -1432,378 +1845,747 @@ void gen_numname ( j++; } } - do { - dst[j++] = (i < 8) ? ns[i++] : ' '; - } while (j < 8); + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* _USE_LFN != 0 && !_FS_READONLY */ + + + +#if _USE_LFN != 0 +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +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 /* _USE_LFN != 0 */ + + + +#if _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = ff_wtoupper(chr); /* File name needs to be ignored case */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xdir_info ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + UINT di, si; + WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif + + /* Get file name */ + di = 0; +#if _LFN_UNICODE + for (si = SZDIRE * 2; di < dirb[XDIR_NumName]; si += 2, di++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow --> inaccessible object name */ + fno->fname[di] = w; /* Store it */ + } +#else + for (si = SZDIRE * 2, nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ff_convert(ld_word(dirb + si), 0); /* Get a character and Unicode -> OEM */ + if (_DF1S && w >= 0x100) { /* Is it a double byte char? (always false at SBCS cfg) */ + fno->fname[di++] = (char)(w >> 8); /* Put 1st byte of the DBC */ + } + if (w == 0 || di >= _MAX_LFN) { di = 0; break; } /* Invalid char or buffer overflow --> inaccessible object name */ + fno->fname[di++] = (char)w; + } +#endif + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate file name */ + + fno->altname[0] = 0; /* No SFN */ + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ +) +{ + FRESULT res; + UINT i, sz_ent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; + mem_cpy(dirb + 0, dp->dir, SZDIRE); + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; + mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; + + /* Load C1 entries */ + i = SZDIRE * 2; /* C1 offset */ + do { + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; + if (i < MAXDIRB(_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + } while ((i += SZDIRE) < sz_ent); + + /* Sanity check (do it when accessible object name) */ + if (i <= MAXDIRB(_MAX_LFN)) { + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + } + return FR_OK; +} + + +#if !_FS_READONLY || _FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_dir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const _FDID* obj /* Object with its containing directory information */ +) +{ + FRESULT res; + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->obj.n_frag = 0; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; } #endif +#if !_FS_READONLY +/*-----------------------------------------------*/ +/* exFAT: Store the directory block to the media */ +/*-----------------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + /* Store the set of directory to the volume */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ -/*-----------------------------------------------------------------------*/ -/* Calculate sum of an SFN */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN static -BYTE sum_sfn ( - const BYTE* dir /* Pointer to the SFN entry */ +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the nul terminated file name */ ) { - BYTE sum = 0; - UINT n = 11; + UINT i; + BYTE nb, nc; + WCHAR chr; - do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); - return sum; + + /* Create 85+C0 entry */ + mem_set(dirb, 0, 2 * SZDIRE); + dirb[XDIR_Type] = 0x85; + dirb[XDIR_Type + SZDIRE] = 0xC0; + + /* Create C1 entries */ + nc = 0; nb = 1; chr = 1; i = SZDIRE * 2; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ + st_word(dirb + i, chr); /* Store it */ + } while ((i += 2) % SZDIRE != 0); + nb++; + } while (lfn[nc]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nc; /* Set name length */ + dirb[XDIR_NumSec] = nb; /* Set block length */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ } -#endif +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT /*-----------------------------------------------------------------------*/ -/* Directory handling - Find an object in the directory */ +/* Read an object from the directory */ /*-----------------------------------------------------------------------*/ static -FRESULT dir_find ( - DIR* dp /* Pointer to the directory object linked to the file name */ +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ ) { - FRESULT res; - BYTE c, *dir; -#if _USE_LFN - BYTE a, ord, sum; + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if _USE_LFN != 0 + BYTE ord = 0xFF, sum = 0xFF; #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); + while (dp->sect) { + res = move_window(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 */ + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { + res = FR_NO_FILE; break; /* Reached to end of the directory */ + } +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ } - /* 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; + break; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if _USE_LFN != 0 /* LFN configuration */ + if (c == DDEM || 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 an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; } - } 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; + if (c != DDEM && 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 */ - } while (res == FR_OK); + if (res != FR_OK) break; + } + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ return res; } +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ /*-----------------------------------------------------------------------*/ -/* Read an object from the directory */ +/* Directory handling - Find an object in 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 dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ ) { FRESULT res; - BYTE a, c, *dir; -#if _USE_LFN - BYTE ord = 0xFF, sum = 0xFF; + FATFS *fs = dp->obj.fs; + BYTE c; +#if _USE_LFN != 0 + BYTE a, ord, sum; #endif - res = FR_NO_FILE; - while (dp->sect) { - res = move_window(dp->fs, dp->sect); + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ +#if _MAX_LFN < 255 + if (fs->dirbuf[XDIR_NumName] > _MAX_LFN) continue; /* Skip comparison if inaccessible object name */ +#endif + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT12/16/32 volume */ +#if _USE_LFN != 0 + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); if (res != FR_OK) break; - dir = dp->dir; /* Ptr to the directory entry of current index */ - c = dir[DIR_Name]; + c = dp->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; +#if _USE_LFN != 0 /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } 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; + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } - /* 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; + if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } #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; + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ #endif - res = dir_next(dp, 0); /* Next entry */ - if (res != FR_OK) break; - } - - if (res != FR_OK) dp->sect = 0; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); return res; } -#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* 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 */ +FRESULT dir_register ( /* FR_OK:succeeded, 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; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + DIR dj; + + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + + if (dp->obj.stat & 4) { /* Has the directory been stretched? */ + dp->obj.stat &= ~4; + res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */ + if (res != FR_OK) return res; + if (dp->obj.sclust != 0) { /* Is it a sub directory? */ + res = load_obj_dir(&dj, &dp->obj); /* Load the object status */ + if (res != FR_OK) return res; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + } + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT12/16/32 volume */ + mem_cpy(sn, dp->fn, 12); 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 */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { - gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + gen_numname(dp->fn, sn, fs->lfnbuf, 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; + dp->fn[NSFLAG] = sn[NSFLAG]; } - 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; - } + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ res = dir_alloc(dp, nent); /* Allocate entries */ - if (res == FR_OK && --nent) { /* Set LFN entry if needed */ - res = dir_sdi(dp, dp->index - nent); + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); if (res == FR_OK) { - sum = sum_sfn(dp->fn); /* Sum value of the SFN tied to the LFN */ + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ - res = move_window(dp->fs, dp->sect); + res = move_window(fs, dp->sect); if (res != FR_OK) break; - fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); - dp->fs->wflag = 1; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + 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); + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(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 + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if _USE_LFN != 0 dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ #endif - dp->fs->wflag = 1; + fs->wflag = 1; } } return res; } -#endif /* !_FS_READONLY */ +#endif /* !_FS_READONLY */ +#if !_FS_READONLY && _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Remove an object from the directory */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY && !_FS_MINIMIZE + static -FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ +FRESULT dir_remove ( /* FR_OK:Succeeded, 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; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + DWORD last = dp->dptr; - 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 */ + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ if (res == FR_OK) { do { - res = move_window(dp->fs, dp->sect); + res = move_window(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 */ + /* Mark an entry 'deleted' */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; + } else { /* On the FAT12/16/32 volume */ + dp->dir[DIR_Name] = DDEM; + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then 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); + + res = move_window(fs, dp->sect); 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; - } + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; } #endif return res; } -#endif /* !_FS_READONLY */ +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*-----------------------------------------------------------------------*/ /* 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; + UINT i, j; + TCHAR c; + DWORD tm; +#if _USE_LFN != 0 + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; #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 = '?'; + + fno->fname[0] = 0; /* Invaidate file info */ + if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ + +#if _USE_LFN != 0 /* LFN configuration */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xdir_info(fs->dirbuf, fno); + return; + } else #endif + { /* On the FAT12/16/32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { 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 at SBCS cfg) */ + fno->fname[i++] = (char)(w >> 8); + } #endif - *p++ = c; + if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ + fno->fname[i++] = (TCHAR)w; + } + fno->fname[i] = 0; /* Terminate the LFN */ } - 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); + + i = j = 0; + lfv = fno->fname[i]; /* LFN is exist if non-zero */ + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) { /* Insert a . if extension is exist */ + if (!lfv) fno->fname[j] = '.'; + fno->altname[j++] = '.'; + } +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { + c = c << 8 | dp->dir[i++]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; #endif - if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ - p[i++] = (TCHAR)w; + fno->altname[j] = c; + if (!lfv) { + if (IsUpper(c) && (dp->dir[DIR_NTres] & ((i >= 9) ? NS_EXT : NS_BODY))) { + c += 0x20; /* To lower */ } + fno->fname[j] = c; } - p[i] = 0; /* Terminate LFN string by a \0 */ + j++; + } + if (!lfv) { + fno->fname[j] = 0; + if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ + } + fno->altname[j] = 0; /* Terminate the SFN */ + +#else /* Non-LFN configuration */ + i = j = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ + fno->fname[j++] = c; } + fno->fname[j] = 0; #endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); } -#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ +#if _USE_FIND && _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ /* 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 */ ) { +#if !_LFN_UNICODE 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 */ + if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; + } #endif return chr; +#else + return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif } static -int pattern_matching ( /* Return value: 0:mismatched, 1:matched */ +int pattern_matching ( /* 0:not matched, 1:matched */ const TCHAR* pat, /* Matching pattern */ const TCHAR* nam, /* String to be tested */ int skip, /* Number of pre-skip chars (number of ?s) */ @@ -1834,78 +2616,76 @@ int pattern_matching ( /* Return value: 0:mismatched, 1:matched */ 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) */ + if (pc == 0) 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 */ +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ /*-----------------------------------------------------------------------*/ -/* Pick a segment and create the object name in directory form */ +/* Pick a top segment and create the object name in directory form */ /*-----------------------------------------------------------------------*/ static -FRESULT create_name ( +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ 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 */ +#if _USE_LFN != 0 /* 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; + p = *path; lfn = dp->obj.fs->lfnbuf; 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 (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long 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 */ + 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; + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ 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] == '.')) { + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ +#if _FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ lfn[di] = 0; - for (i = 0; i < 11; i++) + for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ 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 */ + while (di) { /* Snip off trailing spaces and dots if exist */ w = lfn[di - 1]; if (w != ' ' && w != '.') break; di--; } - if (!di) return FR_INVALID_NAME; /* Reject nul string */ - lfn[di] = 0; /* LFN is created */ + if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ /* Create SFN in directory form */ mem_set(dp->fn, ' ', 11); @@ -1913,7 +2693,7 @@ FRESULT create_name ( if (si) cf |= NS_LOSS | NS_LFN; while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ - b = i = 0; ni = 8; + i = b = 0; ni = 8; for (;;) { w = lfn[si++]; /* Get an LFN character */ if (!w) break; /* Break on end of the LFN */ @@ -1941,7 +2721,7 @@ FRESULT create_name ( cf |= NS_LFN; /* Force create LFN entry */ } - if (_DF1S && w >= 0x100) { /* DBC (always false at SBCS cfg) */ + if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */ if (i >= ni - 1) { cf |= NS_LOSS | NS_LFN; i = ni; continue; } @@ -1962,11 +2742,10 @@ FRESULT create_name ( 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 (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, 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 ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ 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) */ @@ -1977,17 +2756,16 @@ FRESULT create_name ( return FR_OK; -#else /* Non-LFN configuration */ - BYTE b, c, d, *sfn; +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE 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; + p = *path; sfn = dp->fn; mem_set(sfn, ' ', 11); - si = i = b = 0; ni = 8; -#if _FS_RPATH + si = i = 0; ni = 8; +#if _FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ for (;;) { c = (BYTE)p[si++]; @@ -1995,62 +2773,51 @@ FRESULT create_name ( 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 */ + *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 the 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 <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + 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) */ + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ #endif #endif } - if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ d = (BYTE)p[si++]; /* Get 2nd byte */ - if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ - return FR_INVALID_NAME; + if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ 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; - } - } + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ 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) */ + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ - sfn[NSFLAG] = c; /* Store NT flag, File name is created */ + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; -#endif +#endif /* _USE_LFN != 0 */ } @@ -2067,36 +2834,53 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ ) { FRESULT res; - BYTE *dir, ns; + BYTE ns; + _FDID *obj = &dp->obj; + FATFS *fs = obj->fs; -#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 */ +#if _FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + obj->sclust = fs->cdir; /* Start from current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + obj->sclust = 0; /* Start from root directory */ + } +#if _FS_EXFAT + obj->n_frag = 0; /* Invalidate last fragment counter of the object */ +#if _FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ + DIR dj; + + obj->c_scl = fs->cdc_scl; + obj->c_size = fs->cdc_size; + obj->c_ofs = fs->cdc_ofs; + res = load_obj_dir(&dj, obj); + if (res != FR_OK) return res; + obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; } -#else - if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ - path++; - dp->sclust = 0; /* Always start from the root directory */ +#endif #endif if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; 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 */ + res = dir_find(dp); /* Find an object with the segment 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 (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ - res = FR_OK; /* Ended at the root directroy. Function completed. */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; } else { /* Could not find the object */ if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ } @@ -2104,11 +2888,23 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ 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 */ + /* Get into the sub-directory */ + if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } - dp->sclust = ld_clust(dp->fs, dir); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + obj->c_scl = obj->sclust; + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + } else +#endif + { + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } } } @@ -2131,7 +2927,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ UINT i; int vol = -1; #if _STR_VOLUME_ID /* Find string drive id */ - static const char* const str[] = {_VOLUME_STRS}; + static const char* const volid[] = {_VOLUME_STRS}; const char *sp; char c; TCHAR tc; @@ -2142,7 +2938,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ 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'; + 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; @@ -2153,7 +2949,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ else { /* No numeric drive number, find string drive id */ i = 0; tt++; do { - sp = str[i]; tp = *path; + sp = volid[i]; tp = *path; do { /* Compare a string drive id with path name */ c = *sp++; tc = *tp++; if (IsLower(tc)) tc -= 0x20; @@ -2167,7 +2963,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ #endif return vol; } -#if _FS_RPATH && _VOLUMES >= 2 +#if _FS_RPATH != 0 && _VOLUMES >= 2 vol = CurrVol; /* Current drive */ #else vol = 0; /* Drive 0 */ @@ -2184,24 +2980,24 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ /*-----------------------------------------------------------------------*/ static -BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */ +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ FATFS* fs, /* File system object */ - DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ + DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR 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; + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ - 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; + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed here even if the sector size is >512) */ - return 1; + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } +#if _FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; +#endif + return 2; } @@ -2213,9 +3009,9 @@ BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a b 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 */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + BYTE mode /* !=0: Check write protection for write access */ ) { BYTE fmt, *pt; @@ -2227,7 +3023,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ UINT i; - /* Get logical drive number from the path name */ + /* Get logical drive number */ *rfs = 0; vol = get_ldnumber(path); if (vol < 0) return FR_INVALID_DRIVE; @@ -2239,11 +3035,13 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ ENTER_FF(fs); /* Lock the volume */ *rfs = fs; /* Return pointer to the file system object */ + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ 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 */ + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ return FR_WRITE_PROTECTED; + } return FR_OK; /* The file system object is valid */ } } @@ -2254,122 +3052,174 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ 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 */ + 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 */ + } + if (!_FS_READONLY && mode && (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; + } +#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) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; #endif - /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + + /* Find an FAT partition on the drive. Supports only generic partitioning rules, 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; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; } - i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ if (i) i--; - do { /* Find an FAT volume */ + 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); + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4); } - if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */ - return FR_NO_FILESYSTEM; + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found (bsect). Following code initializes the file system object */ + +#if _FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + } + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; #if !_FS_READONLY - /* Initialize cluster allocation information */ - fs->last_clust = fs->free_clust = 0xFFFFFFFF; + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* _FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + + /* 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 (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) 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)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ - /* Get fsinfo if available */ - fs->fsi_flag = 0x80; +#if !_FS_READONLY + /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + 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 (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 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); + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); #endif #if (_FS_NOFSINFO & 2) == 0 - fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); #endif + } } +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */ #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 */ +#if _FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ #endif -#if _FS_LOCK /* Clear file lock semaphores */ +#if _FS_LOCK != 0 /* Clear file lock semaphores */ clear_lock(fs); #endif - return FR_OK; } @@ -2381,32 +3231,32 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ /*-----------------------------------------------------------------------*/ static -FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ - void* obj /* Pointer to the object FIL/DIR to check validity */ +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ + FATFS** fs /* Pointer to pointer to the owner file system object to return */ ) { - 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; + FRESULT res; - return FR_OK; + if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { + *fs = 0; + res = FR_INVALID_OBJECT; /* The object is invalid */ + } else { + *fs = obj->fs; /* Owner file sytem object */ + ENTER_FF(obj->fs); /* Lock file system */ + res = FR_OK; /* Valid object */ + } + return res; } -/*-------------------------------------------------------------------------- +/*--------------------------------------------------------------------------- - Public Functions + Public Functions (FatFs API) ---------------------------------------------------------------------------*/ +----------------------------------------------------------------------------*/ @@ -2417,7 +3267,7 @@ FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ 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 */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ ) { FATFS *cfs; @@ -2426,12 +3276,13 @@ FRESULT f_mount ( const TCHAR *rp = path; + /* Get logical drive number */ vol = get_ldnumber(&rp); if (vol < 0) return FR_INVALID_DRIVE; cfs = FatFs[vol]; /* Pointer to fs object */ if (cfs) { -#if _FS_LOCK +#if _FS_LOCK != 0 clear_lock(cfs); #endif #if _FS_REENTRANT /* Discard sync object of the current volume */ @@ -2450,7 +3301,7 @@ FRESULT f_mount ( 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 */ + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ LEAVE_FF(fs, res); } @@ -2469,125 +3320,193 @@ FRESULT f_open ( { FRESULT res; DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + FATFS *fs; #if !_FS_READONLY - DWORD dw, cl; + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; #endif + DEF_NAMBUF 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 + /* Get logical drive */ + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + res = find_volume(&path, &fs, mode); if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); 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 */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; -#if _FS_LOCK - else + } +#if _FS_LOCK != 0 + 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 + if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */ +#if _FS_LOCK != 0 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) */ + if (dj.obj.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 (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ } } 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); + dw = GET_FATTIME(); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.n_frag = 0; + /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ + fs->dirbuf[XDIR_CrtTime10] = 0; + st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ + st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ + st_qword(fs->dirbuf + XDIR_FileSize, 0); + st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Clean directory info */ + st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + cl = ld_clust(fs, dj.dir); /* Get cluster chain */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + + if (cl) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } } } } } else { /* Open an existing file */ - if (res == FR_OK) { /* Follow succeeded */ - if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + if (res == FR_OK) { /* Following succeeded */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ res = FR_NO_FILE; } else { - if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + if ((mode & FA_WRITE) && (dj.obj.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; + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if _FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->obj.lockid) res = FR_INT_ERR; #endif } - -#else /* R/O configuration */ - if (res == FR_OK) { /* Follow succeeded */ - dir = dj.dir; - if (!dir) { /* Current directory itself */ +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ res = FR_INVALID_NAME; } else { - if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + if (dj.obj.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 _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.n_frag = 0; + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } #if _USE_FASTSEEK - fp->cltbl = 0; /* Normal seek mode */ + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } #endif - fp->fs = dj.fs; /* Validate file object */ - fp->id = fp->fs->id; } + + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); } @@ -2598,96 +3517,95 @@ FRESULT f_open ( /*-----------------------------------------------------------------------*/ 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 */ + 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; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *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; + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - 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 % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ - clst = fp->sclust; /* Follow from the origin */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ } else { /* Middle or end of the file */ #if _USE_FASTSEEK - if (fp->cltbl) + if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else + } else #endif - clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + { + clst = get_fat(&fp->obj, 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); + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(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 = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; - cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + cc = btr / SS(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 (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(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)); + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(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)); + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } #endif #endif - rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + rcnt = SS(fs) * cc; /* Number of bytes transferred */ continue; } #if !_FS_TINY - if (fp->dsect != sect) { /* Load data sector if not in cache */ + if (fp->sect != 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; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ } #endif - fp->dsect = sect; + fp->sect = sect; } - rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ - if (rcnt > btr) rcnt = btr; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #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 */ + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else - mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } @@ -2700,117 +3618,116 @@ FRESULT f_read ( FRESULT f_write ( FIL* fp, /* Pointer to the file object */ - const void *buff, /* Pointer to the data to be written */ + 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; + FATFS *fs; DWORD clst, sect; - UINT wcnt, cc; + UINT wcnt, cc, csect; const BYTE *wbuff = (const BYTE*)buff; - BYTE csect; *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - 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 */ + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } 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? */ + wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* 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 */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ #if _USE_FASTSEEK - if (fp->cltbl) + if (fp->cltbl) { clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else + } else #endif - clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + { + clst = create_chain(&fp->obj, 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); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(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 (fp->obj.sclust == 0) fp->obj.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); + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ #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; + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); sect += csect; - cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + cc = btw / SS(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 (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(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; + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + 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; + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; } #endif #endif - wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + wcnt = SS(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; + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + 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); + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); } #endif - fp->dsect = sect; + fp->sect = sect; } - wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ - if (wcnt > btw) wcnt = btw; + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #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; + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; #else - mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->flag |= FA__DIRTY; + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the 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 */ + fp->flag |= FA_MODIFIED; /* Set file change flag */ - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } @@ -2825,39 +3742,71 @@ FRESULT f_sync ( ) { FRESULT res; + FATFS *fs; DWORD tm; BYTE *dir; +#if _FS_EXFAT + DIR dj; + DEF_NAMBUF +#endif - - res = validate(fp); /* Check validity of the object */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res == FR_OK) { - if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ - /* Write-back dirty buffer */ + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ #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; + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~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); + tm = GET_FATTIME(); /* Modified time */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */ + if (res == FR_OK) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } } } } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ @@ -2870,27 +3819,26 @@ FRESULT f_sync ( /*-----------------------------------------------------------------------*/ FRESULT f_close ( - FIL *fp /* Pointer to the file object to be closed */ + FIL* fp /* Pointer to the file object to be closed */ ) { FRESULT res; - + FATFS *fs; #if !_FS_READONLY res = f_sync(fp); /* Flush cached data */ if (res == FR_OK) #endif { - res = validate(fp); /* Lock volume */ + res = validate(&fp->obj, &fs); /* 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 _FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ if (res == FR_OK) #endif - fp->fs = 0; /* Invalidate file object */ + { + fp->obj.fs = 0; /* Invalidate file object */ + } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif @@ -2902,11 +3850,11 @@ FRESULT f_close ( +#if _FS_RPATH >= 1 /*-----------------------------------------------------------------------*/ /* Change Current Directory or Current Drive, Get Current Directory */ /*-----------------------------------------------------------------------*/ -#if _FS_RPATH >= 1 #if _VOLUMES >= 2 FRESULT f_chdrive ( const TCHAR* path /* Drive number */ @@ -2915,10 +3863,11 @@ FRESULT f_chdrive ( int vol; + /* Get logical drive number */ vol = get_ldnumber(&path); if (vol < 0) return FR_INVALID_DRIVE; - CurrVol = (BYTE)vol; + CurrVol = (BYTE)vol; /* Set it as current volume */ return FR_OK; } @@ -2931,29 +3880,48 @@ FRESULT f_chdir ( { FRESULT res; DIR dj; - DEFINE_NAMEBUF; - + FATFS *fs; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + /* Get logical drive */ + res = find_volume(&path, &fs, 0); if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); 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 */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif } else { - if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ - dj.fs->cdir = ld_clust(dj.fs, dj.dir); - else + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { res = FR_NO_PATH; /* Reached but a file */ + } } } + FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } @@ -2965,51 +3933,47 @@ FRESULT f_getcwd ( { FRESULT res; DIR dj; + FATFS *fs; UINT i, n; DWORD ccl; TCHAR *tp; FILINFO fno; - DEFINE_NAMEBUF; + DEF_NAMBUF *buff = 0; - /* Get logical drive number */ - res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ + /* Get logical drive */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ if (res == FR_OK) { - INIT_BUF(dj); + dj.obj.fs = fs; + INIT_NAMBUF(fs); 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 (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ 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; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(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(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; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; } - while (n) buff[--i] = tp[--n]; - buff[--i] = '/'; } tp = buff; if (res == FR_OK) { @@ -3026,11 +3990,12 @@ FRESULT f_getcwd ( } } *tp = 0; - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } + #endif /* _FS_RPATH >= 2 */ #endif /* _FS_RPATH >= 1 */ @@ -3043,70 +4008,72 @@ FRESULT f_getcwd ( FRESULT f_lseek ( FIL* fp, /* Pointer to the file object */ - DWORD ofs /* File pointer from top of file */ + FSIZE_t ofs /* File pointer from top of file */ ) { FRESULT res; - DWORD clst, bcs, nsect, ifptr; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t 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); + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) res = (FRESULT)fp->err; +#if _FS_EXFAT && !_FS_READONLY + if (res == FR_OK && fs->fs_type == FS_EXFAT) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } +#endif + if (res != FR_OK) LEAVE_FF(fs, res); #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 */ + cl = fp->obj.sclust; /* Origin 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); + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(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 */ + } while (cl < fs->n_fatent); /* Repeat until end of chain */ } *fp->cltbl = ulen; /* Number of items used */ - if (ulen <= tlen) + if (ulen <= tlen) { *tbl = 0; /* Terminate table */ - else + } 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; + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ 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 */ + dsc = clust2sect(fs, fp->clust); + if (!dsc) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* 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; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ #endif - fp->dsect = dsc; + fp->sect = dsc; } } } @@ -3115,81 +4082,82 @@ FRESULT f_lseek ( /* Normal Seek */ { - if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ -#if !_FS_READONLY - && !(fp->flag & FA_WRITE) +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ #endif - ) ofs = fp->fsize; - + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } ifptr = fp->fptr; fp->fptr = nsect = 0; if (ofs) { - bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + bcs = (DWORD)fs->csize * SS(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 */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(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 */ + clst = fp->obj.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; + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; } #endif fp->clust = clst; } if (clst != 0) { while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; #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; + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; 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); + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(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 (ofs % SS(fs)) { + nsect = clust2sect(fs, clst); /* Current sector */ + if (!nsect) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); } } } - if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ + if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* 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; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ #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; + fp->sect = nsect; } -#endif } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } @@ -3205,45 +4173,59 @@ FRESULT f_opendir ( ) { FRESULT res; - FATFS* fs; - DEFINE_NAMEBUF; + FATFS *fs; + _FDID *obj; + DEF_NAMBUF if (!dp) return FR_INVALID_OBJECT; - /* Get logical drive number */ - res = find_volume(&fs, &path, 0); + /* Get logical drive */ + obj = &dp->obj; + res = find_volume(&path, &fs, 0); if (res == FR_OK) { - dp->fs = fs; - INIT_BUF(*dp); + obj->fs = fs; + INIT_NAMBUF(fs); 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 */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (obj->attr & AM_DIR) { /* This object is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Get containing directory inforamation */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + obj->sclust = ld_clust(fs, dp->dir); /* Get object allocation info */ + } + } else { /* This object is a file */ res = FR_NO_PATH; + } } if (res == FR_OK) { - dp->id = fs->id; + obj->id = fs->id; res = dir_sdi(dp, 0); /* Rewind directory */ -#if _FS_LOCK +#if _FS_LOCK != 0 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; + if (obj->sclust) { + obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; } else { - dp->lockid = 0; /* Root directory need not to be locked */ + obj->lockid = 0; /* Root directory need not to be locked */ } } #endif } } + FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; } - if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ + if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ LEAVE_FF(fs, res); } @@ -3260,19 +4242,20 @@ FRESULT f_closedir ( ) { FRESULT res; + FATFS *fs; - res = validate(dp); + res = validate(&dp->obj, &fs); /* Check validity of the file object */ 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 _FS_LOCK != 0 + if (dp->obj.lockid) { /* Decrement sub-directory open counter */ + res = dec_lock(dp->obj.lockid); + } if (res == FR_OK) #endif - dp->fs = 0; /* Invalidate directory object */ + { + dp->obj.fs = 0; /* Invalidate directory object */ + } #if _FS_REENTRANT unlock_fs(fs, FR_OK); /* Unlock volume */ #endif @@ -3293,40 +4276,34 @@ FRESULT f_readdir ( ) { FRESULT res; - DEFINE_NAMEBUF; + FATFS *fs; + DEF_NAMBUF - res = validate(dp); /* Check validity of the object */ + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ if (res == FR_OK) { if (!fno) { res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { - INIT_BUF(*dp); + INIT_NAMBUF(fs); 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_NO_FILE) res = FR_OK; /* Ignore end of directory */ 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; - } + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ } - FREE_BUF(); + FREE_NAMBUF(); } } - - LEAVE_FF(dp->fs, res); + LEAVE_FF(fs, res); } #if _USE_FIND /*-----------------------------------------------------------------------*/ -/* Find next file */ +/* Find Next File */ /*-----------------------------------------------------------------------*/ FRESULT f_findnext ( @@ -3340,19 +4317,18 @@ FRESULT f_findnext ( 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 */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if _USE_LFN != 0 && _USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ #endif - if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ } return res; - } /*-----------------------------------------------------------------------*/ -/* Find first file */ +/* Find First File */ /*-----------------------------------------------------------------------*/ FRESULT f_findfirst ( @@ -3367,8 +4343,9 @@ FRESULT f_findfirst ( dp->pat = pattern; /* Save pointer to pattern string */ res = f_opendir(dp, path); /* Open the target directory */ - if (res == FR_OK) + if (res == FR_OK) { res = f_findnext(dp, fno); /* Find the first item */ + } return res; } @@ -3388,25 +4365,25 @@ FRESULT f_stat ( { FRESULT res; DIR dj; - DEFINE_NAMEBUF; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(dj.obj.fs); 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 */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(dj.obj.fs, res); } @@ -3424,55 +4401,75 @@ FRESULT f_getfree ( { FRESULT res; FATFS *fs; - DWORD n, clst, sect, stat; + DWORD nfree, clst, sect, stat; UINT i; - BYTE fat, *p; + BYTE *p; + _FDID obj; - /* Get logical drive number */ - res = find_volume(fatfs, &path, 0); - fs = *fatfs; + /* Get logical drive */ + res = find_volume(&path, &fs, 0); 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; + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full cluster scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; } else { /* Get number of free clusters */ - fat = fs->fs_type; - n = 0; - if (fat == FS_FAT12) { - clst = 2; + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ + clst = 2; obj.fs = fs; do { - stat = get_fat(fs, clst); + stat = get_fat(&obj, clst); if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } if (stat == 1) { res = FR_INT_ERR; break; } - if (stat == 0) n++; + if (stat == 0) nfree++; } 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); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; + sect = fs->database; + i = 0; + do { + if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Sector alighed FAT entries */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } } - fs->free_clust = n; - fs->fsi_flag |= 1; - *nclst = n; + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ } } + LEAVE_FF(fs, res); } @@ -3488,55 +4485,49 @@ FRESULT f_truncate ( ) { FRESULT res; + FATFS *fs; 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); - } + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */ + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); } + } + fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA_MODIFIED; #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; + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; } -#endif } - if (res != FR_OK) fp->err = (FRESULT)res; +#endif + if (res != FR_OK) ABORT(fs, res); } - LEAVE_FF(fp->fs, res); + LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ -/* Delete a File or Directory */ +/* Delete a File/Directory */ /*-----------------------------------------------------------------------*/ FRESULT f_unlink ( @@ -3545,60 +4536,86 @@ FRESULT f_unlink ( { FRESULT res; DIR dj, sdj; - BYTE *dir; DWORD dclst = 0; - DEFINE_NAMEBUF; + FATFS *fs; +#if _FS_EXFAT + _FDID obj; +#endif + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + 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 */ + } +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ #endif if (res == FR_OK) { /* The object is accessible */ - dir = dj.dir; - if (!dir) { + if (dj.fn[NSFLAG] & NS_NONAME) { res = FR_INVALID_NAME; /* Cannot remove the origin directory */ } else { - if (dir[DIR_Attr] & AM_RDO) + if (dj.obj.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? */ +#if _FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); + obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ +#if _FS_RPATH != 0 + if (dclst == 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); + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); 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) */ + res = dir_read(&sdj, 0); /* Read an item */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ } } } } 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); + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ +#if _FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } @@ -3614,186 +4631,243 @@ FRESULT f_mkdir ( { FRESULT res; DIR dj; - BYTE *dir, n; - DWORD dsc, dcl, pcl, tm = GET_FATTIME(); - DEFINE_NAMEBUF; + FATFS *fs; + BYTE *dir; + UINT n; + DWORD dsc, dcl, pcl, tm; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); 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)) + 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 */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); 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) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); 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); + dsc = clust2sect(fs, dcl); + dir = fs->win; + mem_set(dir, 0, SS(fs)); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; + st_clust(fs, dir + SZDIRE, pcl); + } + for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + fs->winsect = dsc++; + fs->wflag = 1; + res = sync_window(fs); if (res != FR_OK) break; - mem_set(dir, 0, SS(dj.fs)); + mem_set(dir, 0, SS(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 */ + if (res == FR_OK) { + res = dir_register(&dj); /* Register the object to the directoy */ + } + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } } 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); + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } /*-----------------------------------------------------------------------*/ -/* Change Attribute */ +/* Rename a File/Directory */ /*-----------------------------------------------------------------------*/ -FRESULT f_chmod ( - const TCHAR* path, /* Pointer to the file path */ - BYTE attr, /* Attribute bits */ - BYTE mask /* Attribute mask to change */ +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ ) { FRESULT res; - DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + DIR djo, djn; + FATFS *fs; + BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; + DWORD dw; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + get_ldnumber(&path_new); /* Snip drive number of new name off */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ 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; + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if _FS_LOCK != 0 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); + res = chk_lock(&djo, 2); + } +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + 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) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); +/* Start of critical section where an interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT12/FAT16/FAT32 */ + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + 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) { + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(fs, ld_clust(fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { +/* Start of critical section where an interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } } +/* End of the critical section */ } + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ +#if _USE_CHMOD && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Rename File/Directory */ +/* Change Attribute */ /*-----------------------------------------------------------------------*/ -FRESULT f_rename ( - const TCHAR* path_old, /* Pointer to the object to be renamed */ - const TCHAR* path_new /* Pointer to the new name */ +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ ) { - FRESULT res; - DIR djo, djn; - BYTE buf[21], *dir; - DWORD dw; - DEFINE_NAMEBUF; + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF - /* Get logical drive number of the source object */ - res = find_volume(&djo.fs, &path_old, 1); + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; 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); + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else #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 */ - } - } + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); } } - FREE_BUF(); + FREE_NAMBUF(); } - LEAVE_FF(djo.fs, res); + LEAVE_FF(fs, res); } @@ -3810,45 +4884,44 @@ FRESULT f_utime ( { FRESULT res; DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; + FATFS *fs; + DEF_NAMBUF - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; if (res == FR_OK) { - INIT_BUF(dj); + INIT_NAMBUF(fs); 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 && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ 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); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); } } + FREE_NAMBUF(); } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } -#endif /* !_FS_READONLY */ -#endif /* _FS_MINIMIZE == 0 */ -#endif /* _FS_MINIMIZE <= 1 */ -#endif /* _FS_MINIMIZE <= 2 */ - +#endif /* _USE_CHMOD && !_FS_READONLY */ #if _USE_LABEL /*-----------------------------------------------------------------------*/ -/* Get volume label */ +/* Get Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( @@ -3859,63 +4932,90 @@ FRESULT f_getlabel ( { FRESULT res; DIR dj; - UINT i, j; -#if _USE_LFN && _LFN_UNICODE + FATFS *fs; + UINT si, di; +#if _LFN_UNICODE || _FS_EXFAT WCHAR w; #endif - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); + /* Get logical drive */ + res = find_volume(&path, &fs, 0); /* Get volume label */ if (res == FR_OK && label) { - dj.sclust = 0; /* Open root directory */ + dj.obj.fs = fs; dj.obj.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); + res = dir_read(&dj, 1); /* Find a volume label entry */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + w = ld_word(dj.dir + XDIR_Label + si * 2); +#if _LFN_UNICODE + label[di++] = w; #else - mem_cpy(label, dj.dir, 11); + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) w = '?'; /* Replace wrong character */ + if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); + label[di++] = (char)w; #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; + } + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ + do { +#if _LFN_UNICODE + w = (si < 11) ? dj.dir[si++] : ' '; + if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { + w = w << 8 | dj.dir[si++]; + } + label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ +#else + label[di++] = dj.dir[si++]; +#endif + } while (di < 11); + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } } } + if (res == FR_NO_FILE) { /* No label entry and 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); + res = move_window(fs, fs->volbase); if (res == FR_OK) { - i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; - *vsn = LD_DWORD(&dj.fs->win[i]); + switch (fs->fs_type) { + case FS_EXFAT: + di = BPB_VolIDEx; break; + + case FS_FAT32: + di = BS_VolID32; break; + + default: + di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); } } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } #if !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Set volume label */ +/* Set Volume Label */ /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( @@ -3924,84 +5024,115 @@ FRESULT f_setlabel ( { FRESULT res; DIR dj; - BYTE vn[11]; - UINT i, j, sl; + FATFS *fs; + BYTE dirvn[22]; + UINT i, j, slen; WCHAR w; - DWORD tm; + static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; - /* 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); + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + dj.obj.fs = fs; + + /* Get length of given volume label */ + for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ + w = label[i++]; +#if !_LFN_UNICODE + if (IsDBCS1(w)) { + w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } + w = ff_convert(w, 1); +#endif + if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + j, w); j += 2; + } + slen = j; + } else +#endif + { /* On the FAT12/16/32 volume */ + for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ + if (slen) { /* Is there a volume label to be set? */ + dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ + do { +#if _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); + w = (BYTE)label[i++]; + if (IsDBCS1(w)) { + w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } +#if _USE_LFN != 0 + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); #else - if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ #ifdef _EXCVT - if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ + 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) */ + 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) */ + if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); + dirvn[j++] = (BYTE)w; + } while (i < slen); + while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } } /* Set volume label */ - dj.sclust = 0; /* Open root directory */ + dj.obj.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); + res = dir_read(&dj, 1); /* Get volume label entry */ + if (res == FR_OK) { + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { - dj.dir[0] = DDEM; /* Remove the volume label */ + if (slen) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } } - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } else { /* No volume label is found or error */ + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry 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 (slen) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ 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); + mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); } } } } } - LEAVE_FF(dj.fs, res); + LEAVE_FF(fs, res); } #endif /* !_FS_READONLY */ @@ -4009,10 +5140,100 @@ FRESULT f_setlabel ( +#if _USE_EXPAND && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { /* Is it allocated now? */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_EXPAND && !_FS_READONLY */ + + + +#if _USE_FORWARD /*-----------------------------------------------------------------------*/ -/* Forward data to the stream directly (available on only tiny cfg) */ +/* Forward data to the stream directly */ /*-----------------------------------------------------------------------*/ -#if _USE_FORWARD && _FS_TINY FRESULT f_forward ( FIL* fp, /* Pointer to the file object */ @@ -4022,48 +5243,59 @@ FRESULT f_forward ( ) { FRESULT res; - DWORD remain, clst, sect; - UINT rcnt; - BYTE csect; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - 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; + remain = fp->obj.objsize - 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 */ + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes 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? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* 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->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(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 = clust2sect(fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(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); +#if _FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (!rcnt) ABORT(fs, FR_INT_ERR); } - LEAVE_FF(fp->fs, FR_OK); + LEAVE_FF(fs, FR_OK); } #endif /* _USE_FORWARD */ @@ -4071,249 +5303,457 @@ FRESULT f_forward ( #if _USE_MKFS && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Create file system on the logical drive */ +/* Create an FAT/exFAT volume */ /*-----------------------------------------------------------------------*/ -#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 */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit (cluster) [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ ) { - 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; + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ UINT i; - DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ - DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ - FATFS *fs; + int vol; DSTATUS stat; -#if _USE_TRIM - DWORD eb[2]; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; #endif /* Check mounted drive and clear work area */ - if (sfd > 1) return FR_INVALID_PARAMETER; - vol = get_ldnumber(&path); + vol = get_ldnumber(&path); /* Get target logical drive */ if (vol < 0) return FR_INVALID_DRIVE; - fs = FatFs[vol]; - if (!fs) return FR_NOT_ENABLED; - fs->fs_type = 0; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume */ pdrv = LD2PD(vol); /* Physical drive */ - part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ - /* Get disk statics */ + /* Check physical drive status */ 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; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if _MAX_SS != _MIN_SS /* Get sector size of the medium if variable sector size cfg. */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; #endif - if (_MULTI_PARTITION && part) { + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + if (!szb_buf) return FR_MKFS_ABORTED; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { /* 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 */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get 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 */ + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ - 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; - } + /* Pre-determine the FAT type */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* go to next case */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ - /* 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; + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } - /* 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 +#endif /* _FS_EXFAT */ + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ } else { - sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ } - } - 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; + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); } - } - /* 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); + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); } - 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 */ + + /* Determine system ID in the partition table */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ } 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) */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ } 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; + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } } } - /* 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); + /* Update partition information */ + if (_MULTI_PARTITION && part != 0) { /* Created in the existing partition */ + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ + } else { /* Created as a new single partition */ + if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + } } -#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) */ - } + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; - return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + return FR_OK; } @@ -4325,7 +5765,7 @@ FRESULT f_mkfs ( FRESULT f_fdisk ( BYTE pdrv, /* Physical drive number */ - const DWORD szt[], /* Pointer to the size table for each partitions */ + const DWORD* szt, /* Pointer to the size table for each partitions */ void* work /* Pointer to the working buffer */ ) { @@ -4340,7 +5780,7 @@ FRESULT f_fdisk ( 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 */ + /* Determine the CHS without any consideration of the drive geometry */ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; if (n == 256) n--; e_hd = n - 1; @@ -4351,7 +5791,7 @@ FRESULT f_fdisk ( 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; + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */ if (!p_cyl) continue; s_part = (DWORD)sz_cyl * b_cyl; sz_part = (DWORD)sz_cyl * p_cyl; @@ -4361,30 +5801,29 @@ FRESULT f_fdisk ( } else { s_hd = 0; } - e_cyl = b_cyl + p_cyl - 1; + e_cyl = b_cyl + p_cyl - 1; /* End cylinder */ 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[4] = 0x07; /* 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 */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Number of sectors */ /* Next partition */ b_cyl += p_cyl; } - ST_WORD(p, 0xAA55); + 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 */ @@ -4409,24 +5848,24 @@ TCHAR* f_gets ( while (n < len - 1) { /* Read characters until buffer gets filled */ -#if _USE_LFN && _LFN_UNICODE +#if _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 */ + if (c < 0xE0) { /* Two-byte sequence (0x80-0x7FF) */ f_read(fp, s, 1, &rc); if (rc != 1) break; c = (c & 0x1F) << 6 | (s[0] & 0x3F); - if (c < 0x80) c = '?'; + if (c < 0x80) c = '?'; /* Reject invalid code range */ } else { - if (c < 0xF0) { /* Three-byte sequence */ + if (c < 0xF0) { /* Three-byte sequence (0x800-0xFFFF) */ f_read(fp, s, 2, &rc); if (rc != 2) break; c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); - if (c < 0x800) c = '?'; + if (c < 0x800) c = '?'; /* Reject invalid code range */ } else { /* Reject four-byte sequence */ c = '?'; } @@ -4476,14 +5915,14 @@ TCHAR* f_gets ( /*-----------------------------------------------------------------------*/ typedef struct { - FIL* fp; - int idx, nchr; - BYTE buf[64]; + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ } putbuff; static -void putc_bfd ( +void putc_bfd ( /* Buffered write with code conversion */ putbuff* pb, TCHAR c ) @@ -4492,13 +5931,14 @@ void putc_bfd ( int i; - if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ + if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ putc_bfd(pb, '\r'); + } - i = pb->idx; /* Buffer write index (-1:error) */ + i = pb->idx; /* Write index of pb->buf[] */ if (i < 0) return; -#if _USE_LFN && _LFN_UNICODE +#if _LFN_UNICODE #if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ if (c < 0x80) { /* 7-bit */ pb->buf[i++] = (BYTE)c; @@ -4537,6 +5977,31 @@ void putc_bfd ( } +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + 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; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + int f_putc ( TCHAR c, /* A character to be output */ @@ -4544,18 +6009,11 @@ int f_putc ( ) { 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; + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); } @@ -4571,19 +6029,11 @@ int f_puts ( ) { 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; + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); } @@ -4600,15 +6050,14 @@ int f_printf ( ) { va_list arp; + putbuff pb; BYTE f, r; - UINT nw, i, j, w; + UINT i, j, w; DWORD v; - TCHAR c, d, s[16], *p; - putbuff pb; + TCHAR c, d, str[32], *p; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; + putc_init(&pb, fp); va_start(arp, fmt); @@ -4648,17 +6097,23 @@ int f_printf ( 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; } @@ -4673,21 +6128,20 @@ int f_printf ( 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++] = '-'; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof str[0]); + if (f & 8) str[i++] = '-'; j = i; d = (f & 1) ? '0' : ' '; while (!(f & 2) && j++ < w) putc_bfd(&pb, d); - do putc_bfd(&pb, s[--i]); while (i); + do { + putc_bfd(&pb, str[--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; + return putc_flush(&pb); } #endif /* !_FS_READONLY */ diff --git a/source/arm9/source/fatfs/ff.h b/source/arm9/source/fatfs/ff.h index 22f1810..a2bc3c0 100644 --- a/source/arm9/source/fatfs/ff.h +++ b/source/arm9/source/fatfs/ff.h @@ -1,11 +1,13 @@ -/*---------------------------------------------------------------------------/ -/ 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. +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12c / +/-----------------------------------------------------------------------------/ / -/ Copyright (C) 2015, ChaN, all right reserved. +/ Copyright (C) 2017, ChaN, all right reserved. / +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / @@ -13,11 +15,11 @@ / 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 */ +#define _FATFS 68300 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -25,6 +27,7 @@ extern "C" { #include "integer.h" /* Basic integer types */ #include "ffconf.h" /* FatFs configuration options */ + #if _FATFS != _FFCONF #error Wrong configuration file (ffconf.h). #endif @@ -39,21 +42,14 @@ typedef struct { BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ } PARTITION; extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ -#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ -#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ - -#else /* Single partition configuration */ -#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ -#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ - #endif /* Type of path name strings on FatFs API */ -#if _LFN_UNICODE /* Unicode string */ -#if !_USE_LFN +#if _LFN_UNICODE /* Unicode (UTF-16) string */ +#if _USE_LFN == 0 #error _LFN_UNICODE must be 0 at non-LFN cfg. #endif #ifndef _INC_TCHAR @@ -61,14 +57,25 @@ 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 + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; #endif @@ -76,60 +83,88 @@ typedef char TCHAR; /* File system object structure (FATFS) */ typedef struct { - BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE fs_type; /* File system type (0:N/A) */ 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 n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ WORD id; /* File system mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ #if _MAX_SS != _MIN_SS - WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */ + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if _FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer */ #endif #if _FS_REENTRANT _SYNC_t sobj; /* Identifier of sync object */ #endif #if !_FS_READONLY - DWORD last_clust; /* Last allocated cluster */ - DWORD free_clust; /* Number of free clusters */ + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ #endif -#if _FS_RPATH +#if _FS_RPATH != 0 DWORD cdir; /* Current directory start cluster (0:root) */ +#if _FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif #endif - DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */ - DWORD fsize; /* 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 n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ DWORD winsect; /* Current sector appearing in the win[] */ BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ } FATFS; +/* Object ID and allocation information (_FDID) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if _FS_EXFAT + DWORD n_cont; /* Size of first fragment, clusters - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0 and non-directory object) */ +#endif +#if _FS_LOCK != 0 + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} _FDID; + + + /* File object structure (FIL) */ typedef struct { - 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 */ + _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File 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) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ #if !_FS_READONLY DWORD dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ #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[]) */ + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ #endif #if !_FS_TINY BYTE buf[_MAX_SS]; /* File private data read/write window */ @@ -141,23 +176,17 @@ typedef struct { /* 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) */ + _FDID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ 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) */ + DWORD sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if _USE_LFN != 0 + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ #endif #if _USE_FIND - const TCHAR* pat; /* Pointer to the name matching pattern */ + const TCHAR* pat; /* Pointer to the name matching pattern */ #endif } DIR; @@ -166,14 +195,15 @@ typedef struct { /* 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 */ + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if _USE_LFN != 0 + TCHAR altname[13]; /* Altenative file name */ + TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ #endif } FILINFO; @@ -196,11 +226,11 @@ typedef enum { 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_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ 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_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ } FRESULT; @@ -211,12 +241,11 @@ typedef enum { 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_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the 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 */ @@ -226,28 +255,31 @@ 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_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ FRESULT f_chdir (const TCHAR* path); /* Change current directory */ FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ -FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ 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_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) #define f_tell(fp) ((fp)->fptr) -#define f_size(fp) ((fp)->fsize) +#define f_size(fp) ((fp)->obj.objsize) #define f_rewind(fp) f_lseek((fp), 0) #define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) #ifndef EOF #define EOF (-1) @@ -265,7 +297,7 @@ DWORD get_fattime (void); #endif /* Unicode support functions */ -#if _USE_LFN /* Unicode - OEM code conversion */ +#if _USE_LFN != 0 /* Unicode - OEM code conversion */ WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ #if _USE_LFN == 3 /* Memory functions */ @@ -289,60 +321,39 @@ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ /* Flags and offset address */ -/* File access control and file status flags (FIL.flag) */ - +/* File access mode and open method flags (3rd argument of f_open) */ #define FA_READ 0x01 -#define FA_OPEN_EXISTING 0x00 - -#if !_FS_READONLY #define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 #define FA_CREATE_NEW 0x04 #define FA_CREATE_ALWAYS 0x08 #define FA_OPEN_ALWAYS 0x10 -#define FA__WRITTEN 0x20 -#define FA__DIRTY 0x40 -#endif +#define FA_OPEN_APPEND 0x30 +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) -/* FAT sub type (FATFS.fs_type) */ +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 +/* Filesystem type (FATFS.fs_type) */ #define FS_FAT12 1 #define FS_FAT16 2 #define FS_FAT32 3 +#define FS_EXFAT 4 - -/* File attribute bits for directory entry */ - +/* File attribute bits for directory entry (FILINFO.fattrib) */ #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 diff --git a/source/arm9/source/fatfs/ffconf.h b/source/arm9/source/fatfs/ffconf.h index 4b863c6..b199767 100644 --- a/source/arm9/source/fatfs/ffconf.h +++ b/source/arm9/source/fatfs/ffconf.h @@ -1,21 +1,13 @@ /*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module configuration file R0.11 (C)ChaN, 2015 +/ FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ -#define _FFCONF 32020 /* Revision ID */ +#define _FFCONF 68300 /* Revision ID */ /*---------------------------------------------------------------------------/ -/ Functions and Buffer Configurations +/ Function 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(), @@ -27,8 +19,8 @@ /* 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. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 3: f_lseek() function is removed in addition to 2. */ @@ -42,9 +34,9 @@ / 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_FIND 1 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ #define _USE_MKFS 0 @@ -52,7 +44,16 @@ #define _USE_FASTSEEK 0 -/* This option switches fast seek feature. (0:Disable or 1:Enable) */ +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ #define _USE_LABEL 0 @@ -61,8 +62,7 @@ #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. */ +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ /*---------------------------------------------------------------------------/ @@ -73,50 +73,57 @@ /* 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) +/ 1 - ASCII (No support of extended character. Non-LFN cfg. only) +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) */ #define _USE_LFN 2 #define _MAX_LFN 255 -/* The _USE_LFN option switches the LFN feature. +/* The _USE_LFN switches the support of long file name (LFN). / -/ 0: Disable LFN feature. _MAX_LFN has no effect. +/ 0: Disable support of LFN. _MAX_LFN has no effect. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 2: Enable LFN with dynamic working buffer on the STACK. / 3: Enable LFN with dynamic working buffer on the HEAP. / -/ 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. +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. / When use stack for the working buffer, take care on stack overflow. When use heap / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree(), must be added to the project. */ #define _LFN_UNICODE 0 -/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) -/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE -/ to 1. This option also affects behavior of string I/O functions. */ +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ -#define _STRF_ENCODE 0 -/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / / 0: ANSI/OEM @@ -124,30 +131,29 @@ / 2: UTF-16BE / 3: UTF-8 / -/ When _LFN_UNICODE is 0, this option has no effect. */ +/ This option has no effect when _LFN_UNICODE == 0. */ -#define _FS_RPATH 0 -/* This option configures relative path feature. +#define _FS_RPATH 1 +/* This option configures support of relative path. / -/ 0: Disable relative path feature and remove related functions. -/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available. +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. / 2: f_getcwd() function is available in addition to 1. -/ -/ 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 _VOLUMES 2 +/* Number of volumes (logical drives) to be used. (1-10) */ #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. +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each / logical drives. Number of items must be equal to _VOLUMES. Valid characters for @@ -155,26 +161,27 @@ #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. */ +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ #define _MIN_SS 512 #define _MAX_SS 512 /* These options configure the range of sector size to be supported. (512, 1024, -/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ 2048 or 4096) Always set both 512 for most systems, generic memory card and / harddisk. But a larger value may be required for on-board flash memory and some / type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured -/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the -/ disk_ioctl() function. */ +/ to variable sector size and GET_SECTOR_SIZE command needs to be implemented to +/ the disk_ioctl() function. */ #define _USE_TRIM 0 -/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) -/ To enable Trim feature, also CTRL_TRIM command should be implemented to the +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the / disk_ioctl() function. */ @@ -195,40 +202,53 @@ / System Configurations /---------------------------------------------------------------------------*/ +#define _FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ + + #define _FS_NORTC 1 -#define _NORTC_MON 2 +#define _NORTC_MON 1 #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, +#define _NORTC_YEAR 2017 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, / _NORTC_MDAY and _NORTC_YEAR have no effect. -/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ +/ 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 +/* The option _FS_LOCK switches file lock function to control duplicated file open / and illegal operation to open objects. This option must be 0 when _FS_READONLY / is 1. / -/ 0: Disable file lock feature. To avoid volume corruption, application program +/ 0: Disable file lock function. To avoid volume corruption, application program / should avoid illegal open, remove and rename to the open objects. -/ >0: Enable file lock feature. The value defines how many files/sub-directories +/ >0: Enable file lock function. The value defines how many files/sub-directories / can be opened simultaneously under file lock control. Note that the file -/ lock feature is independent of re-entrancy. */ +/ lock control 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 +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / and f_fdisk() function, are always not re-entrant. Only file/directory access -/ to the same volume is under control of this feature. +/ to the same volume is under control of this function. / / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. / 1: Enable re-entrancy. Also user provided synchronization handlers, @@ -238,34 +258,11 @@ / / The _FS_TIMEOUT defines timeout period in unit of time tick. / The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, -/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be -/ included somewhere in the scope of ff.c. */ +/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + +/* #include // O/S definitions */ -#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. -*/ +/*--- End of configuration options ---*/ diff --git a/source/arm9/source/fatfs/integer.h b/source/arm9/source/fatfs/integer.h index f254b2a..9ce7865 100644 --- a/source/arm9/source/fatfs/integer.h +++ b/source/arm9/source/fatfs/integer.h @@ -9,25 +9,30 @@ #include #include +typedef unsigned __int64 QWORD; + #else /* Embedded platform */ -/* This type MUST be 8 bit */ +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* This type MUST be 8-bit */ typedef unsigned char BYTE; -/* These types MUST be 16 bit */ +/* 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 */ +/* These types MUST be 32-bit */ typedef long LONG; typedef unsigned long DWORD; +/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ +typedef unsigned long long QWORD; + #endif #endif diff --git a/source/arm9/source/fatfs/option/ccsbcs.c b/source/arm9/source/fatfs/option/ccsbcs.c new file mode 100644 index 0000000..e2762dc --- /dev/null +++ b/source/arm9/source/fatfs/option/ccsbcs.c @@ -0,0 +1,388 @@ +/*------------------------------------------------------------------------*/ +/* Unicode - Local code bidirectional converter (C)ChaN, 2015 */ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. +/ 720 Arabic +/ 737 Greek +/ 771 KBL +/ 775 Baltic +/ 850 Latin 1 +/ 852 Latin 2 +/ 855 Cyrillic +/ 857 Turkish +/ 860 Portuguese +/ 861 Icelandic +/ 862 Hebrew +/ 863 Canadian French +/ 864 Arabic +/ 865 Nordic +/ 866 Russian +/ 869 Greek 2 +*/ + +#include "../ff.h" + + +#if _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 720 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 737 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 771 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP771(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 775 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 850 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 852 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 855 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 857 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 860 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, + 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 861 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 862 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 863 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, + 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, + 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 864 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */ + 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, + 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, + 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, + 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, + 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 +}; + +#elif _CODE_PAGE == 865 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 866 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 869 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */ + 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, + 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, + 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, + 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, + 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed at current configuration. Remove from the project. +#endif + + + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR chr, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */ +) +{ + WCHAR c; + + + if (chr < 0x80) { /* ASCII */ + c = chr; + + } else { + if (dir) { /* OEM code to Unicode */ + c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80]; + + } else { /* Unicode to OEM code */ + for (c = 0; c < 0x80; c++) { + if (chr == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + + +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted (BMP only) */ +) +{ + /* Compressed upper conversion table */ + static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 + }; + static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF2,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 + }; + const WCHAR *p; + WCHAR bc, nc, cmd; + + + p = chr < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || chr < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (chr < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: chr = p[chr - bc]; break; /* Table conversion */ + case 1: chr -= (chr - bc) & 1; break; /* Case pairs */ + case 2: chr -= 16; break; /* Shift -16 */ + case 3: chr -= 32; break; /* Shift -32 */ + case 4: chr -= 48; break; /* Shift -48 */ + case 5: chr -= 26; break; /* Shift -26 */ + case 6: chr += 8; break; /* Shift +8 */ + case 7: chr -= 80; break; /* Shift -80 */ + case 8: chr -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; + } + + return chr; +} + diff --git a/source/arm9/source/fatfs/option/syscall.c b/source/arm9/source/fatfs/option/syscall.c deleted file mode 100644 index 8e25898..0000000 --- a/source/arm9/source/fatfs/option/syscall.c +++ /dev/null @@ -1,151 +0,0 @@ -/*------------------------------------------------------------------------*/ -/* Sample code of OS dependent controls for FatFs */ -/* (C)ChaN, 2014 */ -/*------------------------------------------------------------------------*/ - - -#include "../ff.h" - - -#if _FS_REENTRANT -/*------------------------------------------------------------------------*/ -/* Create a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to create a new -/ synchronization object, such as semaphore and mutex. When a 0 is returned, -/ the f_mount() function fails with FR_INT_ERR. -*/ - -int ff_cre_syncobj ( /* !=0:Function succeeded, ==0:Could not create due to any error */ - BYTE vol, /* Corresponding logical drive being processed */ - _SYNC_t *sobj /* Pointer to return the created sync object */ -) -{ - int ret; - - - *sobj = CreateMutex(NULL, FALSE, NULL); /* Win32 */ - ret = (int)(*sobj != INVALID_HANDLE_VALUE); - -// *sobj = SyncObjects[vol]; /* uITRON (give a static created sync object) */ -// ret = 1; /* The initial value of the semaphore must be 1. */ - -// *sobj = OSMutexCreate(0, &err); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// *sobj = xSemaphoreCreateMutex(); /* FreeRTOS */ -// ret = (int)(*sobj != NULL); - - return ret; -} - - - -/*------------------------------------------------------------------------*/ -/* Delete a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to delete a synchronization -/ object that created with ff_cre_syncobj function. When a 0 is returned, -/ the f_mount() function fails with FR_INT_ERR. -*/ - -int ff_del_syncobj ( /* !=0:Function succeeded, ==0:Could not delete due to any error */ - _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ -) -{ - int ret; - - - ret = CloseHandle(sobj); /* Win32 */ - -// ret = 1; /* uITRON (nothing to do) */ - -// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// vSemaphoreDelete(sobj); /* FreeRTOS */ -// ret = 1; - - return ret; -} - - - -/*------------------------------------------------------------------------*/ -/* Request Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on entering file functions to lock the volume. -/ When a 0 is returned, the file function fails with FR_TIMEOUT. -*/ - -int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ - _SYNC_t sobj /* Sync object to wait */ -) -{ - int ret; - - ret = (int)(WaitForSingleObject(sobj, _FS_TIMEOUT) == WAIT_OBJECT_0); /* Win32 */ - -// ret = (int)(wai_sem(sobj) == E_OK); /* uITRON */ - -// OSMutexPend(sobj, _FS_TIMEOUT, &err)); /* uC/OS-II */ -// ret = (int)(err == OS_NO_ERR); - -// ret = (int)(xSemaphoreTake(sobj, _FS_TIMEOUT) == pdTRUE); /* FreeRTOS */ - - return ret; -} - - - -/*------------------------------------------------------------------------*/ -/* Release Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on leaving file functions to unlock the volume. -*/ - -void ff_rel_grant ( - _SYNC_t sobj /* Sync object to be signaled */ -) -{ - ReleaseMutex(sobj); /* Win32 */ - -// sig_sem(sobj); /* uITRON */ - -// OSMutexPost(sobj); /* uC/OS-II */ - -// xSemaphoreGive(sobj); /* FreeRTOS */ -} - -#endif - - - - -#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ -/*------------------------------------------------------------------------*/ -/* Allocate a memory block */ -/*------------------------------------------------------------------------*/ -/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. -*/ - -void* ff_memalloc ( /* Returns pointer to the allocated memory block */ - UINT msize /* Number of bytes to allocate */ -) -{ - return malloc(msize); /* Allocate a new memory block with POSIX API */ -} - - -/*------------------------------------------------------------------------*/ -/* Free a memory block */ -/*------------------------------------------------------------------------*/ - -void ff_memfree ( - void* mblock /* Pointer to the memory block to free */ -) -{ - free(mblock); /* Discard the memory block with POSIX API */ -} - -#endif diff --git a/source/arm9/source/fatfs/sdmmc/common.h b/source/arm9/source/fatfs/sdmmc/common.h deleted file mode 100644 index b4ab486..0000000 --- a/source/arm9/source/fatfs/sdmmc/common.h +++ /dev/null @@ -1,15 +0,0 @@ -#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/arm9/source/fatfs/sdmmc/delay.h b/source/arm9/source/fatfs/sdmmc/delay.h index d08c0cc..c211085 100644 --- a/source/arm9/source/fatfs/sdmmc/delay.h +++ b/source/arm9/source/fatfs/sdmmc/delay.h @@ -1,9 +1,5 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - #pragma once -#include "common.h" +#include "../../common.h" void waitcycles(u32 us); diff --git a/source/arm9/source/fatfs/sdmmc/delay.s b/source/arm9/source/fatfs/sdmmc/delay.s index 3a2cfdf..0bf19c2 100644 --- a/source/arm9/source/fatfs/sdmmc/delay.s +++ b/source/arm9/source/fatfs/sdmmc/delay.s @@ -1,15 +1,16 @@ +.text .arm -.global waitcycles -.type waitcycles STT_FUNC +.align 4 -@waitcycles ( u32 us ) +.global waitcycles +.type waitcycles, %function waitcycles: - PUSH {R0-R2,LR} - STR R0, [SP,#4] - waitcycles_loop: - LDR R3, [SP,#4] - SUBS R2, R3, #1 - STR R2, [SP,#4] - CMP R3, #0 - BNE waitcycles_loop - POP {R0-R2,PC} + push {r0-r2, lr} + str r0, [sp, #4] + waitcycles_loop: + ldr r3, [sp, #4] + subs r2, r3, #1 + str r2, [sp, #4] + cmp r3, #0 + bne waitcycles_loop + pop {r0-r2, pc} diff --git a/source/arm9/source/fatfs/sdmmc/sdmmc.c b/source/arm9/source/fatfs/sdmmc/sdmmc.c index 6d12848..7b9e2f4 100644 --- a/source/arm9/source/fatfs/sdmmc/sdmmc.c +++ b/source/arm9/source/fatfs/sdmmc/sdmmc.c @@ -1,32 +1,55 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common.h" +/* + * 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-2015, 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 "sdmmc.h" #include "delay.h" -struct mmcdevice handleNAND; -struct mmcdevice handleSD; +static struct mmcdevice handleNAND; +static struct mmcdevice handleSD; -static inline u16 sdmmc_read16(u16 reg) { - return *(vu16*)(SDMMC_BASE + reg); +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 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 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_write32(u16 reg, u32 val) +{ + *(vu32 *)(SDMMC_BASE + reg) = val; } -static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) { +static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) +{ u16 val = sdmmc_read16(reg); val &= ~clear; val |= set; @@ -40,208 +63,257 @@ static inline void setckl(u32 data) sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); } - mmcdevice *getMMCDevice(int drive) { - if(drive==0) return &handleNAND; + if(drive == 0) return &handleNAND; return &handleSD; } -static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx) +static int geterror(struct mmcdevice *ctx) { - return (ctx->error << 29) >> 31; + return (int)((ctx->error << 29) >> 31); } -static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx) +static void inittarget(struct mmcdevice *ctx) { - sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); + 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); - } - + if(ctx->SDOPT == 0) sdmmc_mask16(REG_SDOPT, 0, 0x8000); + else sdmmc_mask16(REG_SDOPT, 0x8000, 0); } static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args) { - bool getSDRESP = (cmd << 15) >> 31; + u32 getSDRESP = (cmd << 15) >> 31; u16 flags = (cmd << 15) >> 31; - const bool readdata = cmd & 0x20000; - const bool writedata = cmd & 0x40000; + const int readdata = cmd & 0x20000; + const int writedata = cmd & 0x40000; - if (readdata || writedata) + if(readdata || writedata) flags |= TMIO_STAT0_DATAEND; ctx->error = 0; - while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working? - sdmmc_write16(REG_SDIRMASK0,0); - sdmmc_write16(REG_SDIRMASK1,0); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); - sdmmc_mask16(REG_SDDATACTL32,0x1800,0); - - sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); - sdmmc_write16(REG_SDCMDARG1,args >> 16); - sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? + sdmmc_write16(REG_SDIRMASK0, 0); + sdmmc_write16(REG_SDIRMASK1, 0); + sdmmc_write16(REG_SDSTATUS0, 0); + sdmmc_write16(REG_SDSTATUS1, 0); + sdmmc_mask16(REG_DATACTL32, 0x1800, 0); + sdmmc_write16(REG_SDCMDARG0, args & 0xFFFF); + sdmmc_write16(REG_SDCMDARG1, args >> 16); + sdmmc_write16(REG_SDCMD, cmd & 0xFFFF); u32 size = ctx->size; - vu8 *dataPtr = ctx->data; + u8 *rDataPtr = ctx->rData; + const u8 *tDataPtr = ctx->tData; - bool useBuf = ( NULL != dataPtr ); + bool rUseBuf = rDataPtr != NULL; + bool tUseBuf = tDataPtr != NULL; u16 status0 = 0; - while(true) { - u16 status1 = sdmmc_read16(REG_SDSTATUS1); - if (status1 & TMIO_STAT1_RXRDY) { - if (readdata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); - if (size > 0x1FF) { - for(int i = 0; i<0x200; i+=2) { - u16 data = sdmmc_read16(REG_SDFIFO); - *dataPtr++ = data & 0xFF; - *dataPtr++ = data >> 8; + while(true) + { + vu16 status1 = sdmmc_read16(REG_SDSTATUS1); + vu16 ctl32 = sdmmc_read16(REG_DATACTL32); + if((ctl32 & 0x100)) + { + if(readdata) + { + if(rUseBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); + if(size > 0x1FF) + { + //Gabriel Marcano: This implementation doesn't assume alignment. + //I've removed the alignment check doen with former rUseBuf32 as a result + for(int i = 0; i < 0x200; i += 4) + { + u32 data = sdmmc_read32(REG_SDFIFO32); + *rDataPtr++ = data; + *rDataPtr++ = data >> 8; + *rDataPtr++ = data >> 16; + *rDataPtr++ = data >> 24; + } + size -= 0x200; } - size -= 0x200; } + + sdmmc_mask16(REG_DATACTL32, 0x800, 0); } } - - if (status1 & TMIO_STAT1_TXRQ) { - if (writedata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ); - if (size > 0x1FF) { - for (int i = 0; i<0x200; i+=2) { - u16 data = *dataPtr++; - data |= *dataPtr++ << 8; - sdmmc_write16(REG_SDFIFO, data); + if(!(ctl32 & 0x200)) + { + if(writedata) + { + if(tUseBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); + if(size > 0x1FF) + { + for(int i = 0; i < 0x200; i += 4) + { + u32 data = *tDataPtr++; + data |= (u32)*tDataPtr++ << 8; + data |= (u32)*tDataPtr++ << 16; + data |= (u32)*tDataPtr++ << 24; + sdmmc_write32(REG_SDFIFO32, data); + } + size -= 0x200; } - size -= 0x200; } + + sdmmc_mask16(REG_DATACTL32, 0x1000, 0); } } - if (status1 & TMIO_MASK_GW) { + if(status1 & TMIO_MASK_GW) + { ctx->error |= 4; break; } - if (!(status1 & TMIO_STAT1_CMD_BUSY)) { + if(!(status1 & TMIO_STAT1_CMD_BUSY)) + { status0 = sdmmc_read16(REG_SDSTATUS0); - if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) + if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) + { ctx->error |= 0x1; - if (status0 & TMIO_STAT0_DATAEND) + } + if(status0 & TMIO_STAT0_DATAEND) + { ctx->error |= 0x2; + } - if ((status0 & flags) == flags) + if((status0 & flags) == flags) break; } } ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); - - if (getSDRESP != 0) { - ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16); - ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16); - ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16); - ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16); + sdmmc_write16(REG_SDSTATUS0, 0); + sdmmc_write16(REG_SDSTATUS1, 0); + + if(getSDRESP != 0) + { + ctx->ret[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16)); + ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16)); + ctx->ret[2] = (u32)(sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16)); + ctx->ret[3] = (u32)(sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16)); } } -u32 __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in) +int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in) { - if (handleSD.isSDHC == 0) - sector_no <<= 9; + if(handleSD.isSDHC == 0) sector_no <<= 9; inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = in; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleSD.tData = in; handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x52C19,sector_no); + sdmmc_send_command(&handleSD, 0x52C19, sector_no); return geterror(&handleSD); } -u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out) +int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) { - if (handleSD.isSDHC == 0) - sector_no <<= 9; + if(handleSD.isSDHC == 0) sector_no <<= 9; inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = out; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleSD.rData = out; handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x33C12,sector_no); + sdmmc_send_command(&handleSD, 0x33C12, sector_no); return geterror(&handleSD); } -u32 __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out) +int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) { - if (handleNAND.isSDHC == 0) - sector_no <<= 9; + if(handleNAND.isSDHC == 0) sector_no <<= 9; inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - - handleNAND.data = out; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleNAND.rData = out; handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x33C12,sector_no); + sdmmc_send_command(&handleNAND, 0x33C12, sector_no); inittarget(&handleSD); return geterror(&handleNAND); } -u32 __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in) //experimental +int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental { - if (handleNAND.isSDHC == 0) - sector_no <<= 9; + if(handleNAND.isSDHC == 0) sector_no <<= 9; inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - - handleNAND.data = in; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleNAND.tData = in; handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x52C19,sector_no); + sdmmc_send_command(&handleNAND, 0x52C19, sector_no); inittarget(&handleSD); return geterror(&handleNAND); } -static u32 calcSDSize(u8* csd, int type) +static u32 calcSDSize(u8 *csd, int type) { u32 result = 0; - if (type == -1) type = csd[14] >> 6; - switch (type) { + if(type == -1) type = csd[14] >> 6; + switch(type) + { case 0: - { - u32 block_len = csd[9] & 0xf; - block_len = 1u << block_len; - u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1); - mult = 1u << (mult + 2); - result = csd[8] & 3; - result = (result << 8) | csd[7]; - result = (result << 2) | (csd[6] >> 6); - result = (result + 1) * mult * block_len / 512; - } + { + u32 block_len = csd[9] & 0xF; + block_len = 1u << block_len; + u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1)); + mult = 1u << (mult + 2); + result = csd[8] & 3; + result = (result << 8) | csd[7]; + result = (result << 2) | (csd[6] >> 6); + result = (result + 1) * mult * block_len / 512; break; + } case 1: - result = csd[7] & 0x3f; + result = csd[7] & 0x3F; result = (result << 8) | csd[6]; result = (result << 8) | csd[5]; result = (result + 1) * 1024; break; - default: - break; //Do nothing otherwise + default: + break; //Do nothing otherwise FIXME perhaps return some error? } return result; } static void InitSD() +{ + *(vu16 *)0x10006100 &= 0xF7FFu; //SDDATACTL32 + *(vu16 *)0x10006100 &= 0xEFFFu; //SDDATACTL32 + *(vu16 *)0x10006100 |= 0x402u; //SDDATACTL32 + *(vu16 *)0x100060D8 = (*(vu16 *)0x100060D8 & 0xFFDD) | 2; + *(vu16 *)0x10006100 &= 0xFFFFu; //SDDATACTL32 + *(vu16 *)0x100060D8 &= 0xFFDFu; //SDDATACTL + *(vu16 *)0x10006104 = 512; //SDBLKLEN32 + *(vu16 *)0x10006108 = 1; //SDBLKCOUNT32 + *(vu16 *)0x100060E0 &= 0xFFFEu; //SDRESET + *(vu16 *)0x100060E0 |= 1u; //SDRESET + *(vu16 *)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 + *(vu16 *)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 + *(vu16 *)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 + *(vu16 *)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 + *(vu16 *)0x10006002 &= 0xFFFCu; //SDPORTSEL + *(vu16 *)0x10006024 = 0x20; + *(vu16 *)0x10006028 = 0x40EE; + *(vu16 *)0x10006002 &= 0xFFFCu; ////SDPORTSEL + *(vu16 *)0x10006026 = 512; //SDBLKLEN + *(vu16 *)0x10006008 = 0; //SDSTOP +} + +static int Nand_Init() { //NAND handleNAND.isSDHC = 0; @@ -251,80 +323,50 @@ static void InitSD() handleNAND.clk = 0x80; handleNAND.devicenumber = 1; - //SD - handleSD.isSDHC = 0; - handleSD.SDOPT = 0; - handleSD.res = 0; - handleSD.initarg = 0; - handleSD.clk = 0x80; - handleSD.devicenumber = 0; - - *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 - *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 - *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 - *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; - *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 - *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL - *(vu16*)0x10006104 = 0; //SDBLKLEN32 - *(vu16*)0x10006108 = 1; //SDBLKCOUNT32 - *(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET - *(vu16*)0x100060E0 |= 1u; //SDRESET - *(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 - *(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 - *(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 - *(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 - *(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL - *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 - *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE - *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL - *(vu16*)0x10006026 = 512; //SDBLKLEN - *(vu16*)0x10006008 = 0; //SDSTOP - - inittarget(&handleSD); -} - -static int Nand_Init() -{ inittarget(&handleNAND); waitcycles(0xF000); - sdmmc_send_command(&handleNAND,0,0); + sdmmc_send_command(&handleNAND, 0, 0); - do { - do { - sdmmc_send_command(&handleNAND,0x10701,0x100000); - } while ( !(handleNAND.error & 1) ); - } while((handleNAND.ret[0] & 0x80000000) == 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, 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, 0x10403, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10609,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.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; + 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, 0x3B70100); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10506,0x3B90100); - 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, 0x1040D, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10410,0x200); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10410, 0x200); + if((handleNAND.error & 0x4)) return -1; handleNAND.clk |= 0x200; @@ -335,72 +377,104 @@ static int Nand_Init() static int SD_Init() { + //SD + handleSD.isSDHC = 0; + handleSD.SDOPT = 0; + handleSD.res = 0; + handleSD.initarg = 0; + handleSD.clk = 0x80; + handleSD.devicenumber = 0; + inittarget(&handleSD); - waitcycles(1u << 18); //Card needs a little bit of time to be detected, it seems - + waitcycles(1u << 22); //Card needs a little bit of time to be detected, it seems FIXME test again to see what a good number is for the delay + //If not inserted - if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1; - - sdmmc_send_command(&handleSD,0,0); - sdmmc_send_command(&handleSD,0x10408,0x1AA); - //u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E; + if(!(*((vu16 *)(SDMMC_BASE + REG_SDSTATUS0)) & TMIO_STAT0_SIGSTATE)) return 5; + + sdmmc_send_command(&handleSD, 0, 0); + sdmmc_send_command(&handleSD, 0x10408, 0x1AA); 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); + 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); + } + while(!(handleSD.error & 1)); + } + while((handleSD.ret[0] & 0x80000000) == 0); if(!((handleSD.ret[0] >> 30) & 1) || !temp) temp2 = 0; handleSD.isSDHC = temp2; - sdmmc_send_command(&handleSD,0x10602,0); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10602, 0); + if((handleSD.error & 0x4)) return -1; - sdmmc_send_command(&handleSD,0x10403,0); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10403, 0); + if((handleSD.error & 0x4)) return -2; handleSD.initarg = handleSD.ret[0] >> 0x10; - sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10609, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -3; - handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-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, 0x10507, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -4; - sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -5; handleSD.SDOPT = 1; - sdmmc_send_command(&handleSD,0x10446,0x2); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10446, 0x2); + if((handleSD.error & 0x4)) return -6; - sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x1040D, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -7; - sdmmc_send_command(&handleSD,0x10410,0x200); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10410, 0x200); + if((handleSD.error & 0x4)) return -8; handleSD.clk |= 0x200; return 0; } -int sdmmc_sdcard_init() +void sdmmc_get_cid(bool isNand, u32 *info) { - InitSD(); - int result = Nand_Init(); - return result | SD_Init(); + struct mmcdevice *device = isNand ? &handleNAND : &handleSD; + + inittarget(device); + + // use cmd7 to put sd card in standby mode + // CMD7 + sdmmc_send_command(device, 0x10507, 0); + + // get sd card info + // use cmd10 to read CID + sdmmc_send_command(device, 0x1060A, device->initarg << 0x10); + + for(int i = 0; i < 4; ++i) + info[i] = device->ret[i]; + + // put sd card back to transfer mode + // CMD7 + sdmmc_send_command(device, 0x10507, device->initarg << 0x10); } +u32 sdmmc_sdcard_init() +{ + u32 ret = 0; + InitSD(); + if(Nand_Init() != 0) ret &= 1; + if(SD_Init() != 0) ret &= 2; + return ret; +} diff --git a/source/arm9/source/fatfs/sdmmc/sdmmc.h b/source/arm9/source/fatfs/sdmmc/sdmmc.h index 66cee62..d2c198b 100644 --- a/source/arm9/source/fatfs/sdmmc/sdmmc.h +++ b/source/arm9/source/fatfs/sdmmc/sdmmc.h @@ -1,52 +1,48 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - #pragma once -#include "common.h" +#include "../../common.h" -#define SDMMC_BASE 0x10006000u +#define SDMMC_BASE 0x10006000 -#define REG_SDCMD 0x00 -#define REG_SDPORTSEL 0x02 -#define REG_SDCMDARG 0x04 -#define REG_SDCMDARG0 0x04 -#define REG_SDCMDARG1 0x06 -#define REG_SDSTOP 0x08 -#define REG_SDBLKCOUNT 0x0a +#define REG_SDCMD 0x00 +#define REG_SDPORTSEL 0x02 +#define REG_SDCMDARG 0x04 +#define REG_SDCMDARG0 0x04 +#define REG_SDCMDARG1 0x06 +#define REG_SDSTOP 0x08 +#define REG_SDBLKCOUNT 0x0A -#define REG_SDRESP0 0x0c -#define REG_SDRESP1 0x0e -#define REG_SDRESP2 0x10 -#define REG_SDRESP3 0x12 -#define REG_SDRESP4 0x14 -#define REG_SDRESP5 0x16 -#define REG_SDRESP6 0x18 -#define REG_SDRESP7 0x1a +#define REG_SDRESP0 0x0C +#define REG_SDRESP1 0x0E +#define REG_SDRESP2 0x10 +#define REG_SDRESP3 0x12 +#define REG_SDRESP4 0x14 +#define REG_SDRESP5 0x16 +#define REG_SDRESP6 0x18 +#define REG_SDRESP7 0x1A -#define REG_SDSTATUS0 0x1c -#define REG_SDSTATUS1 0x1e +#define REG_SDSTATUS0 0x1C +#define REG_SDSTATUS1 0x1E -#define REG_SDIRMASK0 0x20 -#define REG_SDIRMASK1 0x22 -#define REG_SDCLKCTL 0x24 +#define REG_SDIRMASK0 0x20 +#define REG_SDIRMASK1 0x22 +#define REG_SDCLKCTL 0x24 -#define REG_SDBLKLEN 0x26 -#define REG_SDOPT 0x28 -#define REG_SDFIFO 0x30 +#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_DATACTL 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_DATACTL32 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 REG_CLK_AND_WAIT_CTL 0x138 +#define REG_RESET_SDIO 0x1E0 #define TMIO_STAT0_CMDRESPEND 0x0001 #define TMIO_STAT0_DATAEND 0x0004 @@ -70,31 +66,7 @@ #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_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) @@ -103,7 +75,8 @@ #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) typedef struct mmcdevice { - vu8* data; + u8 *rData; + const u8 *tData; u32 size; u32 error; u16 stat0; @@ -118,11 +91,10 @@ typedef struct mmcdevice { u32 res; } mmcdevice; -int sdmmc_sdcard_init(); -u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out); -u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in); +u32 sdmmc_sdcard_init(); +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in); +int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in); +void sdmmc_get_cid(bool isNand, u32 *info); mmcdevice *getMMCDevice(int drive); - -u32 sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out); -u32 sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in); - diff --git a/source/arm9/source/firm.c b/source/arm9/source/firm.c new file mode 100644 index 0000000..19ad8e3 --- /dev/null +++ b/source/arm9/source/firm.c @@ -0,0 +1,82 @@ +#include "firm.h" + +#include "crypto.h" +#include "../../../build/chainloader.h" + +static __attribute__((noinline)) bool overlaps(u32 as, u32 ae, u32 bs, u32 be) +{ + if(as <= bs && bs <= ae) + return true; + if(bs <= as && as <= be) + return true; + return false; +} + +static __attribute__((noinline)) bool inRange(u32 as, u32 ae, u32 bs, u32 be) +{ + if(as >= bs && ae <= be) + return true; + return false; +} + +bool checkFirmPayload(Firm* firm, u32 payloadSize) +{ + if(memcmp(firm->magic, "FIRM", 4) != 0 || firm->arm9Entry == NULL) //Allow for the ARM11 entrypoint to be zero in which case nothing is done on the ARM11 side + return false; + + bool arm9EpFound = false, + arm11EpFound = false; + + u32 size = 0x200; + for(u32 i = 0; i < 4; i++) + size += firm->section[i].size; + + if(size != payloadSize) return false; + + for(u32 i = 0; i < 4; i++) + { + FirmSection *section = &firm->section[i]; + + //Allow empty sections + if(section->size == 0) + continue; + + if((section->offset < 0x200) || + (section->address + section->size < section->address) || //Overflow check + ((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check + (overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm, (u32)firm + size)) || + ((!inRange((u32)section->address, (u32)section->address + section->size, 0x08000000, 0x08000000 + 0x00100000)) && + (!inRange((u32)section->address, (u32)section->address + section->size, 0x18000000, 0x18000000 + 0x00600000)) && + (!inRange((u32)section->address, (u32)section->address + section->size, 0x1FF00000, 0x1FFFFC00)) && + (!inRange((u32)section->address, (u32)section->address + section->size, 0x20000000, 0x20000000 + 0x8000000)))) + return false; + + __attribute__((aligned(4))) u8 hash[0x20]; + + sha(hash, (u8 *)firm + section->offset, section->size, SHA_256_MODE); + + if(memcmp(hash, section->hash, 0x20) != 0) + return false; + + if(firm->arm9Entry >= section->address && firm->arm9Entry < (section->address + section->size)) + arm9EpFound = true; + + if(firm->arm11Entry >= section->address && firm->arm11Entry < (section->address + section->size)) + arm11EpFound = true; + } + + return arm9EpFound && (firm->arm11Entry == NULL || arm11EpFound); +} + +void launchFirm(int argc, char **argv) +{ + u32 *chainloaderAddress = (u32 *)0x01FF9000; + + // Should not be needed: no ARM11 process in background + //prepareArm11ForFirmlaunch(); + + memcpy(chainloaderAddress, chainloader, chainloader_size); + + // No need to flush caches here, the chainloader is in ITCM + ((void (*)(int, char **, u32))chainloaderAddress)(argc, argv, 0x0000BEEF); +} diff --git a/source/arm9/source/firm.h b/source/arm9/source/firm.h new file mode 100644 index 0000000..232a120 --- /dev/null +++ b/source/arm9/source/firm.h @@ -0,0 +1,25 @@ +#pragma once + +#include "common.h" + +typedef struct __attribute__((packed)) +{ + u32 offset; + u8 *address; + u32 size; + u32 procType; + u8 hash[0x20]; +} FirmSection; + +typedef struct __attribute__((packed)) +{ + char magic[4]; + u32 reserved1; + u8 *arm11Entry; + u8 *arm9Entry; + u8 reserved2[0x30]; + FirmSection section[4]; +} Firm; + +bool checkFirmPayload(Firm* firm, u32 payloadSize); +void launchFirm(int argc, char **argv); diff --git a/source/arm9/source/screen.c b/source/arm9/source/screen.c new file mode 100644 index 0000000..77b1ca1 --- /dev/null +++ b/source/arm9/source/screen.c @@ -0,0 +1,101 @@ +/* +* screeninit.c +* +* Screen init code by dark_samus, bil1s, Normmatt, delebile and others. +* Screen deinit code by tiniVi. +*/ + +#include "screen.h" + +#include +#include "i2c.h" +#include "../../../build/screeninit.h" + +struct fb fbs[2]; + +vu32 *arm11Entry = (u32 *)0x1FFFFFF8; + +/*void deinitScreens(void) +{ + void __attribute__((naked)) ARM11(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + //Clear ARM11 entry offset + *arm11Entry = 0; + + //Shutdown LCDs + *(vu32 *)0x10202A44 = 0; + *(vu32 *)0x10202244 = 0; + *(vu32 *)0x10202014 = 0; + + //Wait for the entry to be set + while(!*arm11Entry); + + //Jump to it + ((void (*)())*arm11Entry)(); + } + + if(PDN_GPU_CNT != 1) + { + *arm11Entry = (u32)ARM11; + while(*arm11Entry); + } +}*/ + +void initScreens(u8 brightnessLevel) +{ + if(PDN_GPU_CNT == 1) + { + u32 *const screenInitAddress = (u32 *)0x24FFFC00; + memcpy(screenInitAddress, screeninit, screeninit_size); + + //Write brightness level for the stub to pick up + screenInitAddress[2] = brightnessLevel%BRIGHTNESS_COUNT; + + *arm11Entry = (u32)screenInitAddress; + while(*arm11Entry); + + //Turn on backlight + i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); + + // Setup arguments for FIRM launch + fbs[0].top_left = (u8 *)0x18300000; + fbs[1].top_left = (u8 *)0x18400000; + fbs[0].top_right = (u8 *)0x18300000; + fbs[1].top_right = (u8 *)0x18400000; + fbs[0].bottom = (u8 *)0x18346500; + fbs[1].bottom = (u8 *)0x18446500; + } +} + +/*const u32 brightness[BRIGHTNESS_COUNT] = {0x5F, 0x4C, 0x39, 0x26}; + +void updateBrightness(u8 brightnessLevel) +{ + static int brightnessValue; + brightnessValue = brightness[brightnessLevel%BRIGHTNESS_COUNT]; + + void __attribute__((naked)) ARM11(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + //Clear ARM11 entry offset + *arm11Entry = 0; + + //Change brightness + *(vu32 *)0x10202240 = brightnessValue; + *(vu32 *)0x10202A40 = brightnessValue; + + //Wait for the entry to be set + while(!*arm11Entry); + + //Jump to it + ((void (*)())*arm11Entry)(); + } + + *arm11Entry = (u32)ARM11; + while(*arm11Entry); +}*/ diff --git a/source/arm9/source/screen.h b/source/arm9/source/screen.h new file mode 100644 index 0000000..e1765a8 --- /dev/null +++ b/source/arm9/source/screen.h @@ -0,0 +1,25 @@ +/* +* screeninit.h +* +* Screen init code by dark_samus, bil1s, Normmatt, delebile and others. +* Screen deinit code by tiniVi. +*/ + +#pragma once + +#include "common.h" + +#define BRIGHTNESS_COUNT 0x4 +#define PDN_GPU_CNT (*(vu8 *)0x10141200) + +struct fb { + u8 *top_left; + u8 *top_right; + u8 *bottom; +} __attribute__((packed)); + +extern struct fb fbs[2]; + +//void deinitScreens(void); +void initScreens(u8 brightnessLevel); +//void updateBrightness(u8 brightnessLevel); diff --git a/source/config.c b/source/config.c index 419a230..56753ec 100644 --- a/source/config.c +++ b/source/config.c @@ -3,17 +3,20 @@ // #include #include -#include #include "ini.h" #include "config.h" #include "font.h" + #ifdef ARM9 #include "arm9/source/fatfs/ff.h" #include "utility.h" #include "memory.h" #endif -#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 +#define COMPUTED_CONFIG_PATH (START_DRIVE_RO?CONFIG_PATH_RO:CONFIG_PATH) + +#define SECTION_MATCH(s) strcmp(section, s) == 0 +#define NAME_MATCH(n) strcmp(name, n) == 0 typedef struct { const char *ptr; @@ -31,9 +34,9 @@ void setColorAlpha(u8 *cfgColor, const char *color) { if ( (color[6] >= '0' && color[6] <= '9') || (color[6] >= 'A' && color[6] <= 'F') || (color[6] >= 'a' && color[6] <= 'f') ) { long l = strtoul(color, NULL, 16); - cfgColor[0] = (u8) (l >> 8 & 0xFF); + cfgColor[0] = (u8) (l >> 24 & 0xFF); cfgColor[1] = (u8) (l >> 16 & 0xFF); - cfgColor[2] = (u8) (l >> 24 & 0xFF); + cfgColor[2] = (u8) (l >> 8 & 0xFF); cfgColor[3] = (u8) (l & 0xFF); } else @@ -43,6 +46,248 @@ void setColorAlpha(u8 *cfgColor, const char *color) { } } +bool setBoolean(const char *item) +{ + if ( item[0] == 'Y' || item[0] == 'y' || item[0] == 'T' || item[0] == 't' ) + return true; // Value for "YES", "yes", "TRUE" and "true" + else if ( item[0] == '-' || (item[0] >= '0' && item[0] <= '9') ) + return ( 0 != atoi(item) ); + return false; +} + +int setAnimTimes(int* animTime, int* animStart, char *item) +{ + int sep1Ind = 0; + while (item[sep1Ind] != ':') + { + if (item[sep1Ind] == '\0') + return -1; + sep1Ind++; + } + + item[sep1Ind] = '\0'; + *animTime = atoi(item); + + item = &item[sep1Ind+1]; + int sep2Ind = 0; + while (item[sep2Ind] != ':') + sep2Ind++; + + if ( item[sep2Ind] == '\0' ) + sep2Ind = -sep1Ind-3; + else + item[sep2Ind] = '\0'; + *animStart = atoi(item); + + return sep1Ind+sep2Ind+2; +} + +void setAnimWithColor(int* animTime, int* animStart, u8 *cfgColor, char *item) +{ + int colorInd = setAnimTimes(animTime, animStart, item); + if (colorInd < 2) + return; + setColor(cfgColor, &item[colorInd]); +} + +void setAnimWithColorAlpha(int* animTime, int* animStart, u8 *cfgColor, char *item) +{ + int colorInd = setAnimTimes(animTime, animStart, item); + if (colorInd < 2) + return; + setColorAlpha(cfgColor, &item[colorInd]); +} + +int readIntValuesList(int* values, int maxValCount, char *item) +{ + int valCount = 0; + while ( valCount < maxValCount ) + { + int sepInd = 0; + while (item[sepInd] != ':' && item[sepInd] != ';' && item[sepInd] != '\0' && item[sepInd] != '\r' && item[sepInd] != '\n') + sepInd++; + bool lastVal = ( item[sepInd] != ':' ); + item[sepInd] = '\0'; + + if ( item[0] == '-' || (item[0] >= '0' && item[0] <= '9') ) + values[valCount] = atoi(item); + else if ( item[0] == 'T' || item[0] == 't' || item[0] == 'Y' || item[0] == 'y' ) // "true" or "yes" + values[valCount] = 1; + else if ( item[0] == 'I' || item[0] == 'i' ) // "infininte" + values[valCount] = 1; + else + values[valCount] = 0; + + item = &item[sepInd+1]; + valCount++; + if ( lastVal ) + break; + } + return valCount; +} + +void setThumbnailParams(thumbnail_s* thumbnail, char *item) +{ + int params[5]; + int pCount = readIntValuesList(params, 5, item); + switch(pCount) + { + case 5: + thumbnail->isRGBA = ( params[4] != 0 ); + case 4: + thumbnail->sizeY = params[3]; + case 3: + thumbnail->sizeX = params[2]; + case 2: + thumbnail->posY = params[1]; + case 1: + thumbnail->posX = params[0]; + } +} + +void readStrAsHexData(const char *dataAsStr, char *data, int* dataSize) { + *dataSize = strlen(dataAsStr)/2; + for (int i = 0; i < *dataSize; i++) + { + if ( dataAsStr[0] >= '0' && dataAsStr[0] <= '9' ) + data[i] = (dataAsStr[0]-'0')<<4; + else if ( dataAsStr[0] >= 'A' && dataAsStr[0] <= 'F' ) + data[i] = (10+dataAsStr[0]-'A')<<4; + else if ( dataAsStr[0] >= 'a' && dataAsStr[0] <= 'f' ) + data[i] = (10+dataAsStr[0]-'a')<<4; + else + data[i] = 0; + if ( dataAsStr[1] >= '0' && dataAsStr[1] <= '9' ) + data[i] += (dataAsStr[1]-'0'); + else if ( dataAsStr[1] >= 'A' && dataAsStr[1] <= 'F' ) + data[i] += (10+dataAsStr[1]-'A'); + else if ( dataAsStr[1] >= 'a' && dataAsStr[1] <= 'f' ) + data[i] += (10+dataAsStr[1]-'a'); + dataAsStr+=2; + } +} + +void writeHexDataAsStr(const char *data, int dataSize, char* dataAsStr) { + + char const hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + for (int i = 0; i < dataSize; i++) + { + dataAsStr[0] = hex_chars[data[i]/16]; + dataAsStr[1] = hex_chars[data[i]%16]; + dataAsStr += 2; + } +} + +void readMovieProperty(movie_config_s* mvConf, const char *name, const char* value) { + + if (NAME_MATCH("path")) { + strncpy(mvConf->path, value, 128); + } else if (NAME_MATCH("compression")) { + if ( value[0] >= '0' && value[0] <= '9' ) + mvConf->compressed = atoi(value); + else if ( 0 == strcmp(value, "QuickLZ") ) + mvConf->compressed = 1; + } else if (NAME_MATCH("loopCount")) { + if ( value[0] == 'I' || value[0] == 'i' ) // Value for "INFINITE" or "infinite" + mvConf->loopCount = -1; + else + mvConf->loopCount = atoi(value); + } else if (NAME_MATCH("loopStreamType")) { + if ( value[0] >= '0' && value[0] <= '9' ) + mvConf->loopStreamType = atoi(value); + else if ( 0 == strcmp(value, "file") ) + mvConf->loopStreamType = FILE_STREAM; + else if ( 0 == strcmp(value, "memory") ) + mvConf->loopStreamType = MEMORY_STREAM; + else if ( 0 == strcmp(value, "compressed_memory") ) + mvConf->loopStreamType = MEMORY_COMPRESSED_STREAM; + } else if (NAME_MATCH("loopReverse")) { + mvConf->loopReverse = setBoolean(value); + } else if (NAME_MATCH("loopStartFrame")) { + mvConf->loopStartFrame = atoi(value); + } else if (NAME_MATCH("delayOnLoopStart")) { + mvConf->loopTimeOnStartFrame = atoi(value); + } else if (NAME_MATCH("loopEndFrame")) { + mvConf->loopEndFrame = atoi(value); + } else if (NAME_MATCH("delayOnLoopEnd")) { + mvConf->loopTimeOnEndFrame = atoi(value); + } +} + +void writeMovieProperty(movie_config_s* mvConf, char *cfg, int* sizePtr) { + + *sizePtr += snprintf(cfg+*sizePtr, 256, "path=%s;\n", mvConf->path); + if ( 0 != mvConf->compressed ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "compression=QuickLZ;\n"); + if ( 0 != mvConf->loopCount ) + { + if ( mvConf->loopCount < 0 ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopCount=infinite;\n"); + else + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopCount=%i;\n", mvConf->loopCount); + if ( 0 != mvConf->loopStreamType ) + { + if ( MEMORY_COMPRESSED_STREAM == mvConf->loopStreamType ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopStreamType=compressed_memory;\n"); + else + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopStreamType=memory;\n"); + } + if ( 0 != mvConf->loopReverse ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopReverse=yes;\n"); + if ( mvConf->loopStartFrame > 0 ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopStartFrame=%i;\n", mvConf->loopStartFrame); + if ( mvConf->loopTimeOnStartFrame > 0 ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "delayOnLoopStart=%i;\n", mvConf->loopTimeOnStartFrame); + if ( mvConf->loopEndFrame >= 0 ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "loopEndFrame=%i;\n", mvConf->loopEndFrame); + if ( mvConf->loopTimeOnEndFrame > 0 ) + *sizePtr += snprintf(cfg+*sizePtr, 256, "delayOnLoopEnd=%i;\n", mvConf->loopTimeOnEndFrame); + } +} + +void thumbnailInit(thumbnail_s* thumbnail, int sizeX, int sizeY) +{ + thumbnail->path[0] = '\0'; + thumbnail->posX = 0; + thumbnail->posY = 0; + thumbnail->sizeX = sizeX; + thumbnail->sizeY = sizeY; + thumbnail->isRGBA = false; + thumbnail->imgBuff = NULL; + thumbnail->loaded = 0; +} + +void thumbnailAlloc(thumbnail_s* thumbnail, int maxSizeX, int maxSizeY) +{ + if ( thumbnail->sizeX > 0 && thumbnail->sizeY > 0 && thumbnail->sizeX <= maxSizeX && thumbnail->sizeY <= maxSizeY ) + { + int allocSize = thumbnail->sizeX * thumbnail->sizeY * (thumbnail->isRGBA?4:3); + #ifdef ARM9 + thumbnail->imgBuff = (u8*)memAlloc(allocSize); + #else + thumbnail->imgBuff = (u8*)malloc(allocSize); + #endif + } + else + thumbnail->loaded = -1; +} + +void entryInit(boot_entry_s* entry) +{ + entry->title[0] = '\0'; + entry->path[0] = '\0'; + entry->key = -1; + entry->offset = 0; + entry->patchesCount = 0; + + entry->splashTop[0] = '\0'; + entry->splashBot[0] = '\0'; + + thumbnailInit(&entry->thumbTop, 400, 240); + thumbnailInit(&entry->thumbTop3D, 400, 240); + thumbnailInit(&entry->thumbBot, 320, 240); +} + static char *ini_buffer_reader(char *str, int num, void *stream) { buffer_ctx *ctx = (buffer_ctx *) stream; int idx = 0; @@ -91,66 +336,268 @@ static int handler(void *user, const char *section, const char *name, } // general - if (MATCH("general", "timeout")) { - config->timeout = atoi(item); - } else if (MATCH("general", "recovery")) { - config->recovery = atoi(item); - } else if (MATCH("general", "default")) { - config->index = atoi(item); - } else if (MATCH("general", "autobootfix")) { - config->autobootfix = atoi(item); - } - - // theme - else if (MATCH("theme", "bgTop1")) { - setColor(config->bgTop1, item); - } else if (MATCH("theme", "bgTop2")) { - setColor(config->bgTop2, item); - } else if (MATCH("theme", "bgBottom")) { - setColor(config->bgBot, item); - } else if (MATCH("theme", "highlight")) { - setColorAlpha(config->highlight, item); - } else if (MATCH("theme", "borders")) { - setColor(config->borders, item); - } else if (MATCH("theme", "font1")) { - setColor(config->fntDef, item); - } else if (MATCH("theme", "font2")) { - setColor(config->fntSel, item); - } else if (MATCH("theme", "bgImgTop")) { - strncpy(config->bgImgTop, item, 128); - } else if (MATCH("theme", "bgImgBot")) { - strncpy(config->bgImgBot, item, 128); - } - - // entries - else if (MATCH("entry", "title")) { - strncpy(config->entries[config->count].title, item, 64); - } else if (MATCH("entry", "path")) { - strncpy(config->entries[config->count].path, item, 128); - } else if (MATCH("entry", "offset")) { - config->entries[config->count].offset = strtoul(item, NULL, 16); - } else if (MATCH("entry", "key")) { - if(strlen(config->entries[config->count].title) > 0) { - config->entries[config->count].key = atoi(item); - config->count++; - } - } - else { - return 0; - } - return 1; + if (SECTION_MATCH("general")) + { + if (NAME_MATCH("timeout")) { + config->timeout = atoi(item); + } else if (NAME_MATCH("recovery")) { + config->recovery = atoi(item); + } else if (NAME_MATCH("default")) { + config->index = atoi(item); + } else if (NAME_MATCH("splashTop")) { + strncpy(config->splashTopDef, item, 128); + } else if (NAME_MATCH("splashBot")) { + strncpy(config->splashBotDef, item, 128); + } + #ifdef ARM9 + else if (NAME_MATCH("screeninit")) { + config->directBootScreenInit = atoi(item); + } else if (NAME_MATCH("brightness")) { + config->brightness = atoi(item); + } + #else + else if (NAME_MATCH("autobootfix")) { + config->autobootfix = atoi(item); + } + #endif + } + + // password + if (SECTION_MATCH("password")) + { + if (NAME_MATCH("keys")) { + config->passSize = readIntValuesList(config->password, PASSWORD_MAX_SIZE, item); + } else if (NAME_MATCH("attempts")) { + config->passMaxAttempt = atoi(item); + } else if (NAME_MATCH("failPath")) { + strncpy(config->failPath, item, 128); + } else if (NAME_MATCH("failOffset")) { + config->failOffset = strtoul(item, NULL, 16); + } else if (NAME_MATCH("failImgTop")) { + strncpy(config->failSplashTop, item, 128); + } else if (NAME_MATCH("failImgBot")) { + strncpy(config->failSplashBot, item, 128); + } + } + + // theme + else if (SECTION_MATCH("theme")) + { + if (NAME_MATCH("bgTop1")) { + setColor(config->bgTop1, item); + } else if (NAME_MATCH("bgTop2")) { + setColor(config->bgTop2, item); + } else if (NAME_MATCH("bgBottom")) { + setColor(config->bgBot, item); + } else if (NAME_MATCH("highlight")) { + setColorAlpha(config->highlight, item); + } else if (NAME_MATCH("borders")) { + setColorAlpha(config->borders, item); + } else if (NAME_MATCH("font1")) { + setColorAlpha(config->fntDef, item); + } else if (NAME_MATCH("font2")) { + setColorAlpha(config->fntSel, item); + } else if (NAME_MATCH("font3")) { + setColorAlpha(config->fntBot, item); + config->fntBotActive = true; + } else if (NAME_MATCH("bgImgTop")) { + strncpy(config->bgImgTop, item, 128); + } else if (NAME_MATCH("bgImgTopAlpha")) { + config->imgTopIsRGBA = setBoolean(item); + } else if (NAME_MATCH("bgImgBot")) { + strncpy(config->bgImgBot, item, 128); + } else if (NAME_MATCH("bgImgBotAlpha")) { + config->imgBotIsRGBA = setBoolean(item); + } + #ifndef ARM9 + else if (NAME_MATCH("bgImgTop3D")) { + strncpy(config->bgImgTop3D, item, 128); + } else if (NAME_MATCH("bgImgTop3DAlpha")) { + config->imgTop3DIsRGBA = setBoolean(item); + } + #endif + } + + // animation + else if (SECTION_MATCH("animation")) + { + if (NAME_MATCH("bgTop1")) { + setAnimWithColor(&config->bgTop1AnimTime, &config->bgTop1AnimTimeStart, config->bgTop1AnimColor, item); + } else if (NAME_MATCH("bgTop2")) { + setAnimWithColor(&config->bgTop2AnimTime, &config->bgTop2AnimTimeStart, config->bgTop2AnimColor, item); + } else if (NAME_MATCH("bgBottom")) { + setAnimWithColor(&config->bgBotAnimTime, &config->bgBotAnimTimeStart, config->bgBotAnimColor, item); + } else if (NAME_MATCH("highlight")) { + setAnimWithColorAlpha(&config->highlightAnimTime, &config->highlightAnimTimeStart, config->highlightAnimColor, item); + } else if (NAME_MATCH("borders")) { + setAnimWithColorAlpha(&config->bordersAnimTime, &config->bordersAnimTimeStart, config->bordersAnimColor, item); + } else if (NAME_MATCH("font1")) { + setAnimWithColorAlpha(&config->fntDefAnimTime, &config->fntDefAnimTimeStart, config->fntDefAnimColor, item); + } else if (NAME_MATCH("font2")) { + setAnimWithColorAlpha(&config->fntSelAnimTime, &config->fntSelAnimTimeStart, config->fntSelAnimColor, item); + } else if (NAME_MATCH("font3")) { + setAnimWithColorAlpha(&config->fntBotAnimTime, &config->fntBotAnimTimeStart, config->fntBotAnimColor, item); + config->fntBotActive = true; + } else if (NAME_MATCH("menuFadeIn")) { + setAnimTimes(&config->menuFadeInTime, &config->menuFadeInTimeStart, item); + } else if (NAME_MATCH("bgImgTopFadeIn")) { + setAnimTimes(&config->bgImgTopFadeInTime, &config->bgImgTopFadeInTimeStart, item); + } else if (NAME_MATCH("bgImgBotFadeIn")) { + setAnimTimes(&config->bgImgBotFadeInTime, &config->bgImgBotFadeInTimeStart, item); + } + } + + // movies + else if (SECTION_MATCH("topMovie")) { + readMovieProperty(&config->movieTop, name, item); + } else if (SECTION_MATCH("bottomMovie")) { + readMovieProperty(&config->movieBot, name, item); + } +#ifndef ARM9 + else if (SECTION_MATCH("top3DMovie")) { + readMovieProperty(&config->movieTop3D, name, item); + } +#endif + + // entries + else if (SECTION_MATCH("entry")) + { + int entryInd = config->count; + boot_entry_s* entry = &config->entries[entryInd]; + + if (NAME_MATCH("title")) { + strncpy(entry->title, item, 64); + } else if (NAME_MATCH("path")) { + strncpy(entry->path, item, 128); + } else if (NAME_MATCH("offset")) { + entry->offset = strtoul(item, NULL, 16); + } else if (NAME_MATCH("splashTop")) { + strncpy(entry->splashTop, item, 128); + } else if (NAME_MATCH("splashBot")) { + strncpy(entry->splashBot, item, 128); + } else if (NAME_MATCH("thumbTop")) { + strncpy(entry->thumbTop.path, item, 128); + } else if (NAME_MATCH("thumbTopParams")) { + setThumbnailParams(&entry->thumbTop, item); + } else if (NAME_MATCH("thumbTop3D")) { + strncpy(entry->thumbTop3D.path, item, 128); + } else if (NAME_MATCH("thumbTop3DParams")) { + setThumbnailParams(&entry->thumbTop3D, item); + } else if (NAME_MATCH("thumbBot")) { + strncpy(entry->thumbBot.path, item, 128); + } else if (NAME_MATCH("thumbBotParams")) { + setThumbnailParams(&entry->thumbBot, item); + } + // End current entry + else if (NAME_MATCH("key")) { + if(strlen(entry->title) > 0) { + entry->key = atoi(item); + config->count++; + if (config->count >= config->maxCount) + { + config->maxCount += ENTRIES_COUNT_INC; + #ifdef ARM9 + // Increase size of allocated space (nothing must be allocated since "config->entries" first allocation) + memAlloc(ENTRIES_COUNT_INC*sizeof(boot_entry_s)); + #else + config->entries = realloc(config->entries, config->maxCount*sizeof(boot_entry_s)); + #endif + } + entryInit(&config->entries[config->count]); + } + } + #ifdef ARM9 + else + { + int patchInd = entry->patchesCount; + if ( patchInd < PATCHES_MAX_PER_ENTRY ) + { + // Binary patches for current entry + if (NAME_MATCH("patchMemSearch")) { + binary_patch* curPatch = &entry->patches[patchInd]; + readStrAsHexData(item, curPatch->memToSearch, &curPatch->memToSearchSize); + } else if (NAME_MATCH("patchMemOverwrite")) { + binary_patch* curPatch = &entry->patches[patchInd]; + readStrAsHexData(item, curPatch->memOverwrite, &curPatch->memOverwriteSize); + } else if (NAME_MATCH("patchMemOverwriteStr")) { + binary_patch* curPatch = &entry->patches[patchInd]; + curPatch->memOverwriteSize = strlen(item)+1; + memcpy(curPatch->memOverwrite, item, curPatch->memOverwriteSize); + } else if (NAME_MATCH("patchMemOverwriteWStr")) { + binary_patch* curPatch = &entry->patches[patchInd]; + int strSize = strlen(item)+1; + curPatch->memOverwriteSize = strSize*2; + for (int i = 0; i < strSize; i++) + { + curPatch->memOverwrite[2*i] = item[i]; + curPatch->memOverwrite[2*i+1] = '\0'; + } + } else if (NAME_MATCH("patchOccurence")) { + entry->patches[patchInd].occurence = atoi(item); + entry->patchesCount++; + } + } + } + #endif + } + return 0; +} + +void configMovieInit(movie_config_s* mvConf) +{ + mvConf->path[0] = '\0'; + mvConf->compressed = 0; + mvConf->loopCount = 0; + mvConf->loopStreamType = 0; + mvConf->loopReverse = 0; + mvConf->loopStartFrame = 0; + mvConf->loopTimeOnStartFrame = 0; + mvConf->loopEndFrame = -1; + mvConf->loopTimeOnEndFrame = 0; } void configThemeInit() { + + // theme config->imgError = true; + config->imgError3D = true; config->imgErrorBot = true; + config->bgImgTop[0] = '\0'; + config->bgImgTop3D[0] = '\0'; + config->bgImgBot[0] = '\0'; + config->imgTopIsRGBA = false; + config->imgTop3DIsRGBA = false; + config->imgBotIsRGBA = false; memcpy(config->bgTop1, (u8[3]) {0x4a, 0x00, 0x31}, sizeof(u8[3])); memcpy(config->bgTop2, (u8[3]) {0x6f, 0x01, 0x49}, sizeof(u8[3])); memcpy(config->bgBot, (u8[3]) {0x6f, 0x01, 0x49}, sizeof(u8[3])); - memcpy(config->highlight, (u8[3]) {0xdc, 0xdc, 0xdc}, sizeof(u8[3])); - memcpy(config->borders, (u8[3]) {0xff, 0xff, 0xff}, sizeof(u8[3])); - memcpy(config->fntDef, (u8[3]) {0xff, 0xff, 0xff}, sizeof(u8[3])); - memcpy(config->fntSel, (u8[3]) {0x00, 0x00, 0x00}, sizeof(u8[3])); + memcpy(config->highlight, (u8[4]) {0xdc, 0xdc, 0xdc, 0xff}, sizeof(u8[4])); + memcpy(config->borders, (u8[4]) {0xff, 0xff, 0xff, 0xff}, sizeof(u8[4])); + memcpy(config->fntDef, (u8[4]) {0xff, 0xff, 0xff, 0xff}, sizeof(u8[4])); + memcpy(config->fntSel, (u8[4]) {0x00, 0x00, 0x00, 0xff}, sizeof(u8[4])); + memcpy(config->fntBot, (u8[4]) {0xff, 0xff, 0xff, 0xff}, sizeof(u8[4])); + config->fntBotActive = false; + + // animation + config->bgTop1AnimTime = 0; + config->bgTop2AnimTime = 0; + config->bgBotAnimTime = 0; + config->highlightAnimTime = 0; + config->bordersAnimTime = 0; + config->fntDefAnimTime = 0; + config->fntSelAnimTime = 0; + config->fntBotAnimTime = 0; + config->menuFadeInTime = 0; + config->menuFadeInTimeStart = 0; + config->bgImgTopFadeInTime = 0; + config->bgImgTopFadeInTimeStart = 0; + config->bgImgBotFadeInTime = 0; + config->bgImgBotFadeInTimeStart = 0; + + // movie + configMovieInit(&config->movieTop); + configMovieInit(&config->movieTop3D); + configMovieInit(&config->movieBot); } int configInit() { @@ -166,46 +613,100 @@ int configInit() { config->timeout = 3; config->autobootfix = 8; + config->brightness = 0; config->index = 0; config->recovery = 2; + config->directBootScreenInit = 1; + + config->splashTopDef[0] = '\0'; + config->splashBotDef[0] = '\0'; + + config->password[0] = -1; + config->passSize = 0; + config->passMaxAttempt = 0; + config->failPath[0] = '\0'; + config->failOffset = 0; + config->failSplashTop[0] = '\0'; + config->failSplashBot[0] = '\0'; + +#ifdef ARM9 + config->entries = memAlloc(DEFAULT_ENTRIES_COUNT*sizeof(boot_entry_s)); +#else + config->entries = malloc(DEFAULT_ENTRIES_COUNT*sizeof(boot_entry_s)); +#endif config->count = 0; + config->maxCount = DEFAULT_ENTRIES_COUNT; + entryInit(&config->entries[0]); + configThemeInit(); // read config file to buffer - size_t size = fileSize(CONFIG_PATH); - if (!size) { - return -1; - } - - char buffer[size]; - memset(buffer, 0, size); - if (fileRead(CONFIG_PATH, buffer, size) != 0) { - debug("Could not read config file, creating one..."); - configSave(); // write new config file - return -1; - } - ctx.ptr = buffer; - ctx.bytes_left = strlen(ctx.ptr); + size_t size = fileSize(COMPUTED_CONFIG_PATH); + if ( size != -1 && size > 0 ) + { + char buffer[size]; + memset(buffer, 0, size); + if (fileRead(COMPUTED_CONFIG_PATH, buffer, size) != 0) + { + debug("Could not read config file, creating one..."); + configSave(); + } + else + { + ctx.ptr = buffer; + ctx.bytes_left = strlen(ctx.ptr); - if (ini_parse_stream((ini_reader) ini_buffer_reader, &ctx, handler, config) < 0) { - debug("Could not parse config file, creating one..."); - configSave(); // write new config file - return -1; + if (ini_parse_stream((ini_reader) ini_buffer_reader, &ctx, handler, config) < 0) { + debug("Could not parse config file, creating one..."); + configSave(); // write new config file + } + } } + + int i = 0; + while ( i < config->passSize && config->password[i] >= 0 && config->password[i] <= MAX_KEY ) + i++; + if ( i < PASSWORD_MAX_SIZE ) + config->password[i] = -1; + config->passSize = i; - memcpy(fontDefault.color, config->fntDef, sizeof(u8[3])); - loadBg(GFX_TOP); - loadBg(GFX_BOTTOM); + loadBg(GFX_TOP, GFX_LEFT); +#ifndef ARM9 + loadBg(GFX_TOP, GFX_RIGHT); + if (!config->imgError3D && !gfxIs3D()) + gfxSet3D(true); +#endif + loadBg(GFX_BOTTOM, GFX_LEFT); + for (int i = 0 ; i < config->count ; i++) + { + boot_entry_s* entry = &config->entries[i]; + thumbnailAlloc(&entry->thumbTop, 400, 240); + #ifndef ARM9 + thumbnailAlloc(&entry->thumbTop3D, 400, 240); + #endif + thumbnailAlloc(&entry->thumbBot, 320, 240); + } + return 0; } int configAddEntry(char *title, char *path, long offset) { + + if (config->count >= config->maxCount) + return 1; - strncpy(config->entries[config->count].title, title, 64); - strncpy(config->entries[config->count].path, path, 128); - config->entries[config->count].offset = offset; - config->entries[config->count].key = -1; + boot_entry_s* entry = &config->entries[config->count]; + entryInit(entry); + strncpy(entry->title, title, 64); + strncpy(entry->path, path, 128); + entry->offset = offset; + entry->patchesCount = 0; + entry->splashTop[0] = '\0'; + entry->splashBot[0] = '\0'; + thumbnailInit(&entry->thumbTop, 400, 240); + thumbnailInit(&entry->thumbTop3D, 400, 240); + thumbnailInit(&entry->thumbBot, 320, 240); config->count++; configSave(); @@ -214,6 +715,19 @@ int configAddEntry(char *title, char *path, long offset) { int configRemoveEntry(int index) { + if ( index >= config->count ) + return 1; + +#ifndef ARM9 + boot_entry_s* entry = &config->entries[index]; + if ( NULL != entry->thumbTop.imgBuff ) + free(entry->thumbTop.imgBuff); + if ( NULL != entry->thumbTop3D.imgBuff ) + free(entry->thumbTop.imgBuff); + if ( NULL != entry->thumbBot.imgBuff ) + free(entry->thumbTop.imgBuff); +#endif + int i = 0; for(i=0; icount; i++) { if(i > index) { @@ -232,6 +746,9 @@ int configRemoveEntry(int index) { void configSave() { + if (START_DRIVE_RO) + return; + int size = 0, i = 0; int buffSize = 256*(256*sizeof(char)); #ifdef ARM9 @@ -242,51 +759,249 @@ void configSave() { memset(cfg, 0, buffSize); // 256 lines * 256 char // general section - size += snprintf(cfg, 256, "[general];\n"); -#ifndef ARM9 + size += snprintf(cfg, 256, "[general]\n"); +#ifdef ARM9 + if (config->directBootScreenInit != 1) + size += snprintf(cfg+size, 256, "screeninit=%i;\n", config->directBootScreenInit); + if (config->brightness > 0) + size += snprintf(cfg+size, 256, "brightness=%i;\n", config->brightness); +#else size += snprintf(cfg+size, 256, "autobootfix=%i;\n", config->autobootfix); #endif size += snprintf(cfg+size, 256, "timeout=%i;\n", config->timeout); size += snprintf(cfg+size, 256, "recovery=%i;\n", config->recovery); - size += snprintf(cfg+size, 256, "default=%i;\n\n", config->index); + size += snprintf(cfg+size, 256, "default=%i;\n", config->index); + if (config->splashTopDef[0] != '\0') + size += snprintf(cfg+size, 256, "splashTop=%s;\n", config->splashTopDef); + if (config->splashBotDef[0] != '\0') + size += snprintf(cfg+size, 256, "splashBot=%s;\n", config->splashBotDef); + + // password section + if ( config->passSize > 0 ) + { + size += snprintf(cfg+size, 256, "\n[password]\n"); + size += snprintf(cfg+size, 256, "keys="); + for ( int i = 0 ; i < config->passSize-1 ; i++ ) + size += snprintf(cfg+size, 256, "%i:", config->password[i]); + size += snprintf(cfg+size, 256, "%i;\n", config->password[config->passSize-1]); + if ( config->passMaxAttempt > 0 ) + size += snprintf(cfg+size, 256, "attempts=%d;\n", config->passMaxAttempt); + if ( config->failPath[0] != '\0' ) + { + size += snprintf(cfg+size, 256, "failPath=%s;\n", config->failPath); + size += snprintf(cfg+size, 256, "failOffset=%x;\n", (int)config->failOffset); + } + if (config->failSplashTop[0] != '\0') + size += snprintf(cfg+size, 256, "failImgTop=%s;\n", config->failSplashTop); + if (config->failSplashBot[0] != '\0') + size += snprintf(cfg+size, 256, "failImgBot=%s;\n", config->failSplashBot); + } // theme section - size += snprintf(cfg+size, 256, "[theme];\n"); + size += snprintf(cfg+size, 256, "\n[theme]\n"); size += snprintf(cfg+size, 256, "bgTop1=%02X%02X%02X;\n", config->bgTop1[0], config->bgTop1[1], config->bgTop1[2]); size += snprintf(cfg+size, 256, "bgTop2=%02X%02X%02X;\n", config->bgTop2[0], config->bgTop2[1], config->bgTop2[2]); size += snprintf(cfg+size, 256, "bgBottom=%02X%02X%02X;\n", config->bgBot[0], config->bgBot[1], config->bgBot[2]); - if ( config->highlight[3] < 0xFF ) - size += snprintf(cfg+size, 256, "highlight=%02X%02X%02X%02X;\n", config->highlight[2], config->highlight[1], config->highlight[0], config->highlight[3]); - else - size += snprintf(cfg+size, 256, "highlight=%02X%02X%02X;\n", config->highlight[0], config->highlight[1], config->highlight[2]); - size += snprintf(cfg+size, 256, "borders=%02X%02X%02X;\n", config->borders[0], config->borders[1], config->borders[2]); - size += snprintf(cfg+size, 256, "font1=%02X%02X%02X;\n", config->fntDef[0], config->fntDef[1], config->fntDef[2]); - size += snprintf(cfg+size, 256, "font2=%02X%02X%02X;\n", config->fntSel[0], config->fntSel[1], config->fntSel[2]); + if ( config->highlight[3] < 0xFF ) + size += snprintf(cfg+size, 256, "highlight=%02X%02X%02X%02X;\n", config->highlight[0], config->highlight[1], config->highlight[2], config->highlight[3]); + else + size += snprintf(cfg+size, 256, "highlight=%02X%02X%02X;\n", config->highlight[0], config->highlight[1], config->highlight[2]); + if ( config->borders[3] < 0xFF ) + size += snprintf(cfg+size, 256, "borders=%02X%02X%02X%02X;\n", config->borders[0], config->borders[1], config->borders[2], config->borders[3]); + else + size += snprintf(cfg+size, 256, "borders=%02X%02X%02X;\n", config->borders[0], config->borders[1], config->borders[2]); + if ( config->fntDef[3] < 0xFF ) + size += snprintf(cfg+size, 256, "font1=%02X%02X%02X%02X;\n", config->fntDef[0], config->fntDef[1], config->fntDef[2], config->fntDef[3]); + else + size += snprintf(cfg+size, 256, "font1=%02X%02X%02X;\n", config->fntDef[0], config->fntDef[1], config->fntDef[2]); + if ( config->fntSel[3] < 0xFF ) + size += snprintf(cfg+size, 256, "font2=%02X%02X%02X%02X;\n", config->fntSel[0], config->fntSel[1], config->fntSel[2], config->fntSel[3]); + else + size += snprintf(cfg+size, 256, "font2=%02X%02X%02X;\n", config->fntSel[0], config->fntSel[1], config->fntSel[2]); + if ( config->fntBotActive ) + { + if ( config->fntBot[3] < 0xFF ) + size += snprintf(cfg+size, 256, "font3=%02X%02X%02X%02X;\n", config->fntBot[0], config->fntBot[1], config->fntBot[2], config->fntBot[3]); + else + size += snprintf(cfg+size, 256, "font3=%02X%02X%02X;\n", config->fntBot[0], config->fntBot[1], config->fntBot[2]); + } size += snprintf(cfg+size, 256, "bgImgTop=%s;\n", config->bgImgTop); - size += snprintf(cfg+size, 256, "bgImgBot=%s;\n\n", config->bgImgBot); + if ( config->imgTopIsRGBA ) + size += snprintf(cfg+size, 256, "bgImgTopAlpha=yes;\n"); +#ifndef ARM9 + if ( config->bgImgTop3D[0] != '\0' ) + size += snprintf(cfg+size, 256, "bgImgTop3D=%s;\n", config->bgImgTop3D); + if ( config->imgTop3DIsRGBA ) + size += snprintf(cfg+size, 256, "bgImgTop3DAlpha=yes;\n"); +#endif + size += snprintf(cfg+size, 256, "bgImgBot=%s;\n", config->bgImgBot); + if ( config->imgBotIsRGBA ) + size += snprintf(cfg+size, 256, "bgImgBotAlpha=yes;\n"); - for(i=0; icount; i++) { - size += snprintf(cfg+size, 256, "[entry];\n"); - size += snprintf(cfg+size, 256, "title=%s;\n", config->entries[i].title); - size += snprintf(cfg+size, 256, "path=%s;\n", config->entries[i].path); - size += snprintf(cfg+size, 256, "offset=%x;\n", (int)config->entries[i].offset); - size += snprintf(cfg+size, 256, "key=%i;\n\n", config->entries[i].key); + // animation section + size += snprintf(cfg+size, 256, "\n[animation]\n"); + if ( config->bgTop1AnimTime > 0 ) + { + size += snprintf(cfg+size, 256, "bgTop1=%i:%i:%02X%02X%02X;\n", config->bgTop1AnimTime, config->bgTop1AnimTimeStart, + config->bgTop1AnimColor[0], config->bgTop1AnimColor[1], config->bgTop1AnimColor[2]); } -#ifdef ARM9 - FIL file; - unsigned int br = 0; - f_unlink(CONFIG_PATH); - if(f_open(&file, CONFIG_PATH, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { - debug("Could not open cfg: %s", CONFIG_PATH); - return; + if ( config->bgTop2AnimTime > 0 ) + { + size += snprintf(cfg+size, 256, "bgTop2=%i:%i:%02X%02X%02X;\n", config->bgTop2AnimTime, config->bgTop2AnimTimeStart, + config->bgTop2AnimColor[0], config->bgTop2AnimColor[1], config->bgTop2AnimColor[2]); } - f_write(&file, cfg, size, &br); - f_close(&file); -#else - unlink(CONFIG_PATH); - FILE *file = fopen(CONFIG_PATH, "w"); - fwrite(cfg, 1 , size, file); - fclose(file); + if ( config->bgBotAnimTime > 0 ) + { + size += snprintf(cfg+size, 256, "bgBottom=%i:%i:%02X%02X%02X;\n", config->bgBotAnimTime, config->bgBotAnimTimeStart, + config->bgBotAnimColor[0], config->bgBotAnimColor[1], config->bgBotAnimColor[2]); + } + if ( config->highlightAnimTime > 0 ) + { + if ( config->highlightAnimColor[3] < 0xFF ) + { + size += snprintf(cfg+size, 256, "highlight=%i:%i:%02X%02X%02X%02X;\n", config->highlightAnimTime, config->highlightAnimTimeStart, + config->highlightAnimColor[0], config->highlightAnimColor[1], config->highlightAnimColor[2], config->highlightAnimColor[3]); + } + else + { + size += snprintf(cfg+size, 256, "highlight=%i:%i:%02X%02X%02X;\n", config->highlightAnimTime, config->highlightAnimTimeStart, + config->highlightAnimColor[0], config->highlightAnimColor[1], config->highlightAnimColor[2]); + } + } + if ( config->bordersAnimTime > 0 ) + { + if ( config->highlightAnimColor[3] < 0xFF ) + { + size += snprintf(cfg+size, 256, "borders=%i:%i:%02X%02X%02X%02X;\n", config->bordersAnimTime, config->bordersAnimTimeStart, + config->bordersAnimColor[0], config->bordersAnimColor[1], config->bordersAnimColor[2], config->bordersAnimColor[3]); + } + else + { + size += snprintf(cfg+size, 256, "borders=%i:%i:%02X%02X%02X;\n", config->bordersAnimTime, config->bordersAnimTimeStart, + config->bordersAnimColor[0], config->bordersAnimColor[1], config->bordersAnimColor[2]); + } + } + if ( config->fntDefAnimTime > 0 ) + { + if ( config->fntDefAnimColor[3] < 0xFF ) + { + size += snprintf(cfg+size, 256, "font1=%i:%i:%02X%02X%02X%02X;\n", config->fntDefAnimTime, config->fntDefAnimTimeStart, + config->fntDefAnimColor[0], config->fntDefAnimColor[1], config->fntDefAnimColor[2], config->fntDefAnimColor[3]); + } + else + { + size += snprintf(cfg+size, 256, "font1=%i:%i:%02X%02X%02X;\n", config->fntDefAnimTime, config->fntDefAnimTimeStart, + config->fntDefAnimColor[0], config->fntDefAnimColor[1], config->fntDefAnimColor[2]); + } + } + if ( config->fntSelAnimTime > 0 ) + { + if ( config->fntSelAnimColor[3] < 0xFF ) + { + size += snprintf(cfg+size, 256, "font2=%i:%i:%02X%02X%02X%02X;\n", config->fntSelAnimTime, config->fntSelAnimTimeStart, + config->fntSelAnimColor[0], config->fntSelAnimColor[1], config->fntSelAnimColor[2], config->fntSelAnimColor[3]); + } + else + { + size += snprintf(cfg+size, 256, "font2=%i:%i:%02X%02X%02X;\n", config->fntSelAnimTime, config->fntSelAnimTimeStart, + config->fntSelAnimColor[0], config->fntSelAnimColor[1], config->fntSelAnimColor[2]); + } + } + if ( config->fntBotActive && config->fntBotAnimTime > 0 ) + { + if ( config->fntBotAnimColor[3] < 0xFF ) + { + size += snprintf(cfg+size, 256, "font3=%i:%i:%02X%02X%02X%02X;\n", config->fntBotAnimTime, config->fntBotAnimTimeStart, + config->fntBotAnimColor[0], config->fntBotAnimColor[1], config->fntBotAnimColor[2], config->fntBotAnimColor[3]); + } + else + { + size += snprintf(cfg+size, 256, "font3=%i:%i:%02X%02X%02X;\n", config->fntBotAnimTime, config->fntBotAnimTimeStart, + config->fntBotAnimColor[0], config->fntBotAnimColor[1], config->fntBotAnimColor[2]); + } + } + if ( config->menuFadeInTime > 0 ) + size += snprintf(cfg+size, 256, "menuFadeIn=%i:%i;\n", config->menuFadeInTime, config->menuFadeInTimeStart); + if ( config->bgImgTopFadeInTime > 0 ) + size += snprintf(cfg+size, 256, "bgImgTopFadeIn=%i:%i;\n", config->bgImgTopFadeInTime, config->bgImgTopFadeInTimeStart); + if ( config->bgImgBotFadeInTime > 0 ) + size += snprintf(cfg+size, 256, "bgImgBotFadeIn=%i:%i;\n", config->bgImgBotFadeInTime, config->bgImgBotFadeInTimeStart); + + // movie section + if ( config->movieTop.path[0] != '\0' ) + { + size += snprintf(cfg+size, 256, "\n[topMovie]\n"); + writeMovieProperty(&config->movieTop, cfg, &size); + } +#ifndef ARM9 + if ( config->movieTop3D.path[0] != '\0' ) + { + size += snprintf(cfg+size, 256, "\n[top3DMovie]\n"); + writeMovieProperty(&config->movieTop3D, cfg, &size); + } +#endif + if ( config->movieBot.path[0] != '\0' ) + { + size += snprintf(cfg+size, 256, "\n[bottomMovie]\n"); + writeMovieProperty(&config->movieBot, cfg, &size); + } + + // entries section + for(i=0; icount; i++) + { + boot_entry_s* entry = &config->entries[i]; + + size += snprintf(cfg+size, 256, "\n[entry]\n"); + size += snprintf(cfg+size, 256, "title=%s;\n", entry->title); + size += snprintf(cfg+size, 256, "path=%s;\n", entry->path); + size += snprintf(cfg+size, 256, "offset=%x;\n", (int)entry->offset); + + int patchesCount = entry->patchesCount; + #ifdef ARM9 + for (int j = 0; j < patchesCount; j++) + { + binary_patch* curPatch = &entry->patches[j]; + size += snprintf(cfg+size, 256, "patchMemSearch="); + writeHexDataAsStr(curPatch->memToSearch, curPatch->memToSearchSize, cfg+size); + size += curPatch->memToSearchSize*2; + size += snprintf(cfg+size, 256, ";\npatchMemOverwrite="); + writeHexDataAsStr(curPatch->memOverwrite, curPatch->memOverwriteSize, cfg+size); + size += curPatch->memOverwriteSize*2; + size += snprintf(cfg+size, 256, ";\npatchOccurence=%i;\n", curPatch->occurence); + } + #endif + + if (entry->splashTop[0] != '\0') + size += snprintf(cfg+size, 256, "splashTop=%s;\n", entry->splashTop); + if (entry->splashBot[0] != '\0') + size += snprintf(cfg+size, 256, "splashBot=%s;\n", entry->splashBot); + + if ( entry->thumbTop.path[0] != '\0' ) + { + thumbnail_s* thumb = &entry->thumbTop; + size += snprintf(cfg+size, 256, "thumbTop=%s;\n", thumb->path); + size += snprintf(cfg+size, 256, "thumbTopParams=%d:%d:%d:%d:%s;\n", thumb->posX, thumb->posY, thumb->sizeX, thumb->sizeY, thumb->isRGBA?"yes":"no"); + } + #ifndef ARM9 + if ( entry->thumbTop3D.path[0] != '\0' ) + { + thumbnail_s* thumb = &entry->thumbTop3D; + size += snprintf(cfg+size, 256, "thumbTop3D=%s;\n", thumb->path); + size += snprintf(cfg+size, 256, "thumbTop3DParams=%d:%d:%d:%d:%s;\n", thumb->posX, thumb->posY, thumb->sizeX, thumb->sizeY, thumb->isRGBA?"yes":"no"); + } + #endif + if ( entry->thumbBot.path[0] != '\0' ) + { + thumbnail_s* thumb = &entry->thumbBot; + size += snprintf(cfg+size, 256, "thumbBot=%s;\n", thumb->path); + size += snprintf(cfg+size, 256, "thumbBotParams=%d:%d:%d:%d:%s;\n", thumb->posX, thumb->posY, thumb->sizeX, thumb->sizeY, thumb->isRGBA?"yes":"no"); + } + + size += snprintf(cfg+size, 256, "key=%i;\n", entry->key); + } + + fileWrite(COMPUTED_CONFIG_PATH, cfg, size); +#ifndef ARM9 free(cfg); #endif } @@ -294,25 +1009,49 @@ void configSave() { void configExit() { #ifndef ARM9 if (config) { + for (int i = 0 ; i < config->count ; i++) + { + boot_entry_s* entry = &config->entries[i]; + if ( NULL != entry->thumbTop.imgBuff ) + free(entry->thumbTop.imgBuff); + if ( NULL != entry->thumbTop3D.imgBuff ) + free(entry->thumbTop.imgBuff); + if ( NULL != entry->thumbBot.imgBuff ) + free(entry->thumbTop.imgBuff); + } + free(config->entries); + if (config->bgImgTopBuff) { free(config->bgImgTopBuff); } + if (config->bgImgTop3DBuff) { + free(config->bgImgTop3DBuff); + } if (config->bgImgBotBuff) { free(config->bgImgBotBuff); } + free(config); } #endif } -void loadBg(gfxScreen_t screen) { +void loadBg(gfxScreen_t screen, gfx3dSide_t side) { - const char *path = screen == GFX_TOP ? config->bgImgTop : config->bgImgBot; +#ifdef ARM9 + const char *path = (screen == GFX_TOP ? config->bgImgTop : config->bgImgBot); +#else + const char *path = (screen == GFX_TOP ? (side == GFX_RIGHT ? config->bgImgTop3D : config->bgImgTop) : config->bgImgBot); +#endif size_t size = fileSize(path); - if (!size) { + if ( size == -1 || size == 0 ) { return; } + size_t maxsize = GFX_TOP ? (400*240*4) : (320*240*4); + if (size > maxsize) + size = maxsize; + #ifdef ARM9 u8 *bg = screen == GFX_TOP ? PTR_TOP_BG : PTR_BOT_BG; #else @@ -323,9 +1062,20 @@ void loadBg(gfxScreen_t screen) { } if (screen == GFX_TOP) { - config->bgImgTopSize = size; - config->bgImgTopBuff = bg; - config->imgError = false; + #ifndef ARM9 + if (side == GFX_RIGHT) { + config->bgImgTop3DSize = size; + config->bgImgTop3DBuff = bg; + config->imgError3D = false; + } else + { + #endif + config->bgImgTopSize = size; + config->bgImgTopBuff = bg; + config->imgError = false; + #ifndef ARM9 + } + #endif } else { config->bgImgBotSize = size; config->bgImgBotBuff = bg; diff --git a/source/config.h b/source/config.h index aa67e89..49db7c0 100644 --- a/source/config.h +++ b/source/config.h @@ -8,46 +8,158 @@ extern "C" { #ifdef ARM9 #include "arm9/source/common.h" - -#define CONFIG_PATH "/a9lh.cfg" +#define CONFIG_PATH "/a9lh.cfg\0Free space for binary patch to chainload another instance of CtrBootManager9... Space ends up now!" +#define CONFIG_PATH_RO "/CtrBootManager.cfg" #else + #include <3ds.h> #include "utility.h" #define CONFIG_PATH "/boot.cfg" +#define CONFIG_PATH_RO CONFIG_PATH #endif + +#include "draw.h" +#include "loader.h" // Binary patches define + #define BIT(n) (1U<<(n)) -#define CONFIG_MAX_ENTRIES 11 + +#define PASSWORD_MAX_SIZE 16 +#define MAX_KEY 11 + +#define DEFAULT_ENTRIES_COUNT 11 +#define ENTRIES_COUNT_INC 12 +#define PATCHES_MAX_PER_ENTRY 4 + +#define FILE_STREAM 0 +#define MEMORY_STREAM 1 +#define MEMORY_COMPRESSED_STREAM 2 + +typedef struct { + char path[128]; + int posX; + int posY; + int sizeX; + int sizeY; + bool isRGBA; + u8 *imgBuff; + int loaded; +} thumbnail_s; typedef struct { char title[64]; char path[128]; int key; long offset; + binary_patch patches[PATCHES_MAX_PER_ENTRY]; + int patchesCount; + + char splashTop[128]; + char splashBot[128]; + + thumbnail_s thumbTop; + thumbnail_s thumbTop3D; + thumbnail_s thumbBot; } boot_entry_s; +typedef struct { + char path[128]; + int compressed; + int loopCount; + int loopReverse; + int loopStreamType; + int loopStartFrame; + int loopTimeOnStartFrame; + int loopEndFrame; + int loopTimeOnEndFrame; +} movie_config_s; + typedef struct { int timeout; int autobootfix; + int brightness; int index; int recovery; + int directBootScreenInit; + + int password[PASSWORD_MAX_SIZE]; + int passSize; + int passMaxAttempt; + char failPath[128]; + long failOffset; + char failSplashTop[128]; + char failSplashBot[128]; + int count; - boot_entry_s entries[CONFIG_MAX_ENTRIES]; + int maxCount; + boot_entry_s* entries; + + char splashTopDef[128]; + char splashBotDef[128]; + u8 bgTop1[3]; u8 bgTop2[3]; u8 bgBot[3]; u8 highlight[4]; - u8 borders[3]; - u8 fntDef[3]; - u8 fntSel[3]; + u8 borders[4]; + u8 fntDef[4]; + u8 fntSel[4]; + u8 fntBot[4]; + bool fntBotActive; + + u8 bgTop1AnimColor[3]; + u8 bgTop2AnimColor[3]; + u8 bgBotAnimColor[3]; + u8 highlightAnimColor[4]; + u8 bordersAnimColor[4]; + u8 fntDefAnimColor[4]; + u8 fntSelAnimColor[4]; + u8 fntBotAnimColor[4]; + + int bgTop1AnimTime; + int bgTop2AnimTime; + int bgBotAnimTime; + int highlightAnimTime; + int bordersAnimTime; + int fntDefAnimTime; + int fntSelAnimTime; + int fntBotAnimTime; + + int bgTop1AnimTimeStart; + int bgTop2AnimTimeStart; + int bgBotAnimTimeStart; + int highlightAnimTimeStart; + int bordersAnimTimeStart; + int fntDefAnimTimeStart; + int fntSelAnimTimeStart; + int fntBotAnimTimeStart; + + int menuFadeInTime; + int menuFadeInTimeStart; + int bgImgTopFadeInTime; + int bgImgTopFadeInTimeStart; + int bgImgBotFadeInTime; + int bgImgBotFadeInTimeStart; + char bgImgTop[128]; + char bgImgTop3D[128]; char bgImgBot[128]; + bool imgTopIsRGBA; + bool imgTop3DIsRGBA; + bool imgBotIsRGBA; bool imgError; + bool imgError3D; bool imgErrorBot; u8 *bgImgTopBuff; + u8 *bgImgTop3DBuff; u8 *bgImgBotBuff; off_t bgImgTopSize; + off_t bgImgTop3DSize; off_t bgImgBotSize; + + movie_config_s movieTop; + movie_config_s movieTop3D; + movie_config_s movieBot; } boot_config_s; boot_config_s *config; @@ -62,7 +174,7 @@ void configSave(); void configExit(); -void loadBg(gfxScreen_t screen); +void loadBg(gfxScreen_t screen, gfx3dSide_t side); #ifdef __cplusplus } diff --git a/source/draw.c b/source/draw.c new file mode 100644 index 0000000..89b6406 --- /dev/null +++ b/source/draw.c @@ -0,0 +1,291 @@ +#include +#include +#include + +#ifdef ARM9 +#include "arm9/source/common.h" +#include "memory.h" +#include "arm9/source/screen.h" +#else +#include <3ds.h> +#endif + +#include +#include "text.h" +#include "anim.h" + +#ifdef ARM9 + +u8 *gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16 *width, u16 *height) { + //WARNING: width and height must stay inverted to simulate 3DSX version behavior! + if (screen == GFX_TOP) { + if (width) *width = 240; + if (height) *height = 400; + return PTR_TOP_SCREEN_BUF; + } else { + if (width) *width = 240; + if (height) *height = 320; + return PTR_BOT_SCREEN_BUF; + } +} + +#endif + +void drawPixel(int x, int y, char r, char g, char b, u8 *screen) { + int height = 240; + + u32 v = (height - 1 - y + x * height) * 3; + screen[v] = b; + screen[v + 1] = g; + screen[v + 2] = r; +} + +void drawPixelAlpha(int x, int y, char r, char g, char b, char a, u8 *screen) { + int height = 240; + + u32 v = (height - 1 - y + x * height) * 3; + + int alpha = a; + int one_minus_alpha = 255-alpha; + + screen[v + 0] = (u8)((alpha*b+one_minus_alpha*screen[v + 0])/255); + screen[v + 1] = (u8)((alpha*g+one_minus_alpha*screen[v + 1])/255); + screen[v + 2] = (u8)((alpha*a+one_minus_alpha*screen[v + 2])/255); +} + +void drawLine(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b, char a) { + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + int x, y; + if ( a == 0xFF ) + { + if (x1 == x2) { + if (y1 < y2) for (y = y1; y < y2; y++) drawPixel(x1, y, r, g, b, fbAdr); + else for (y = y2; y < y1; y++) drawPixel(x1, y, r, g, b, fbAdr); + } else { + if (x1 < x2) for (x = x1; x < x2; x++) drawPixel(x, y1, r, g, b, fbAdr); + else for (x = x2; x < x1; x++) drawPixel(x, y1, r, g, b, fbAdr); + } + } + else if ( a > 0 ) + { + if (x1 == x2) { + if (y1 < y2) for (y = y1; y < y2; y++) drawPixelAlpha(x1, y, r, g, b, a, fbAdr); + else for (y = y2; y < y1; y++) drawPixelAlpha(x1, y, r, g, b, a, fbAdr); + } else { + if (x1 < x2) for (x = x1; x < x2; x++) drawPixelAlpha(x, y1, r, g, b, a, fbAdr); + else for (x = x2; x < x1; x++) drawPixelAlpha(x, y1, r, g, b, a, fbAdr); + } + } +} + +void drawRect(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b, char a) { + drawLine(screen, side, x1+1, y1, x2, y1, r, g, b, a); + drawLine(screen, side, x2, y1, x2, y2, r, g, b, a); + drawLine(screen, side, x1, y2, x2+1, y2, r, g, b, a); + drawLine(screen, side, x1, y1, x1, y2, r, g, b, a); +} + +void drawRectColor(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, u8 *rgbaColor) { + drawRect(screen, side, x1, y1, x2, y2, rgbaColor[0], rgbaColor[1], rgbaColor[2], rgbaColor[3]); +} + +void drawTextf(gfxScreen_t screen, gfx3dSide_t side, font_s *f, s16 x, s16 y, const char *fmt, ...) { + char s[512]; + memset(s, 0, 512); + va_list args; + va_start(args, fmt); + int len = vsprintf(s, fmt, args); + va_end(args); + if (len) + drawText(screen, side, f, s, x, y); +} + +void drawText(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, s16 x, s16 y) { + if (!str)return; + if (!f)f = &fontDefault; + + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + drawString(fbAdr, f, str, x, 240 - y, fbHeight, fbWidth); +} + +void drawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, u16 length, s16 x, s16 y) { + if (!str)return; + if (!f)f = &fontDefault; + + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + drawStringN(fbAdr, f, str, length, x, 240 - y, fbHeight, fbWidth); +} + +void drawImage(gfxScreen_t screen, gfx3dSide_t side, u8* img, bool rgba, u16 w, u16 h, s16 x, s16 y, char a) +{ + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + int imax = w; + if (imax+x > fbHeight) + imax = fbHeight-x; + + int jmax = h; + if (jmax+y > fbWidth) + jmax = fbWidth-y; + + int i, j; + if ( rgba ) + { + for (i = 0; i < imax; i++) + { + u8* imgAdr = img + 4*i*h; + u8* rowFbAdr = fbAdr + 3*((x+i)*fbWidth + y); + for (j = 0; j < jmax; j++) + { + int alpha = ((int)a*imgAdr[0]) / 255; + int one_minus_alpha = 255-alpha; + rowFbAdr[0] = (u8)((alpha*imgAdr[1]+one_minus_alpha*rowFbAdr[0])/255); + rowFbAdr[1] = (u8)((alpha*imgAdr[2]+one_minus_alpha*rowFbAdr[1])/255); + rowFbAdr[2] = (u8)((alpha*imgAdr[3]+one_minus_alpha*rowFbAdr[2])/255); + imgAdr += 4; + rowFbAdr += 3; + } + } + } + else + { + int alpha = a; + int one_minus_alpha = 255-a; + for (i = 0; i < imax; i++) + { + u8* imgAdr = img + 3*i*h; + u8* rowFbAdr = fbAdr + 3*((x+i)*fbWidth + y); + for (j = 0; j < jmax; j++) + { + rowFbAdr[0] = (u8)((alpha*imgAdr[0]+one_minus_alpha*rowFbAdr[0])/255); + rowFbAdr[1] = (u8)((alpha*imgAdr[1]+one_minus_alpha*rowFbAdr[1])/255); + rowFbAdr[2] = (u8)((alpha*imgAdr[2]+one_minus_alpha*rowFbAdr[2])/255); + imgAdr += 3; + rowFbAdr += 3; + } + } + } +} + +void fillColor(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3]) { + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + //TODO : optimize; use GX command ? + int i; + for (i = 0; i < fbWidth * fbHeight; i++) { + *(fbAdr++) = rgbColor[2]; + *(fbAdr++) = rgbColor[1]; + *(fbAdr++) = rgbColor[0]; + } +} + +void fillColorGradient(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3]) { + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + u8 colorLine[720]; // Avoid dynamic "fbWidth*3", illogical crash on 3DSX version with top right buffer + + //TODO : optimize; use GX command ? + // Avoid float operations, illogical crash on 3DSX version with top right buffer + int i; + int width = fbWidth; + for (i = 0; i < fbWidth; i++) { + int minus = width-i-1; + colorLine[i * 3 + 0] = (minus*rgbColorStart[2] + i*rgbColorEnd[2]) / fbWidth; + colorLine[i * 3 + 1] = (minus*rgbColorStart[1] + i*rgbColorEnd[1]) / fbWidth; + colorLine[i * 3 + 2] = (minus*rgbColorStart[0] + i*rgbColorEnd[0]) / fbWidth; + } + + for (i = 0; i < fbHeight; i++) { + memcpy(fbAdr, colorLine, fbWidth * 3); + fbAdr += fbWidth * 3; + } +} + +void _drawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbaColor[4], s16 x, s16 y, u16 width, u16 height) { + u16 fbWidth, fbHeight; + u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); + + if (x + width < 0 || x >= fbWidth)return; + if (y + height < 0 || y >= fbHeight)return; + + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width >= fbWidth)width = fbWidth - x; + if (y + height >= fbHeight)height = fbHeight - y; + + if ( rgbaColor[3] == 0xFF ) + { + u8 colorLine[width * 3]; + + int j; + for (j = 0; j < width; j++) { + colorLine[j * 3 + 0] = rgbaColor[2]; + colorLine[j * 3 + 1] = rgbaColor[1]; + colorLine[j * 3 + 2] = rgbaColor[0]; + } + + fbAdr += fbWidth * 3 * y; + for (j = 0; j < height; j++) { + memcpy(&fbAdr[x * 3], colorLine, width * 3); + fbAdr += fbWidth * 3; + } + } + else if ( rgbaColor[3] > 0 ) + { + int alpha = rgbaColor[3]; + int one_minus_alpha = 255-alpha; + + int i, j; + fbAdr += fbWidth * 3 * y; + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + fbAdr[3*(i+x)+0] = (u8)((alpha*rgbaColor[2]+one_minus_alpha*fbAdr[3*(i+x)+0])/255); + fbAdr[3*(i+x)+1] = (u8)((alpha*rgbaColor[1]+one_minus_alpha*fbAdr[3*(i+x)+1])/255); + fbAdr[3*(i+x)+2] = (u8)((alpha*rgbaColor[0]+one_minus_alpha*fbAdr[3*(i+x)+2])/255); + } + fbAdr += fbWidth * 3; + } + } +} + +void drawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[4], s16 x, s16 y, u16 width, u16 height) { + _drawRectangle(screen, side, rgbColor, 240 - y, x, height, width); +} + +void clearTop(u8 top1[3], u8 top2[3]) { + fillColorGradient(GFX_TOP, GFX_LEFT, top1, top2); + if( IS3DACTIVE ) + fillColorGradient(GFX_TOP, GFX_RIGHT, top1, top2); +} + +void clearBot(u8 bot[8]) { + fillColor(GFX_BOTTOM, GFX_LEFT, bot); +} + +void swapFrameBuffers() { +#ifdef ARM9 + memcpy(fbs[0].top_left, PTR_TOP_SCREEN_BUF, TOP_SCREEN_SIZE); + memcpy(fbs[0].bottom, PTR_BOT_SCREEN_BUF, BOT_SCREEN_SIZE); +#else + gfxFlushBuffers(); + gfxSwapBuffers(); + gspWaitForVBlank(); +#endif + incrementAnimTime(); +} diff --git a/source/draw.h b/source/draw.h new file mode 100644 index 0000000..39178c5 --- /dev/null +++ b/source/draw.h @@ -0,0 +1,58 @@ +#pragma once + +#ifdef ARM9 + +#include "arm9/source/common.h" + +typedef enum +{ + GFX_TOP = 0, ///< Top screen + GFX_BOTTOM = 1 ///< Bottom screen +}gfxScreen_t; + +typedef enum +{ + GFX_LEFT = 0, ///< Left eye framebuffer + GFX_RIGHT = 1,///< Right eye framebuffer +}gfx3dSide_t; + +u8 *gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16 *width, u16 *height); + +#define IS3DACTIVE false + +#else + +#include <3ds.h> +#define IS3DACTIVE gfxIs3D() + +#endif + +#define TOP_SCREEN_SIZE (400*240*3) +#define BOT_SCREEN_SIZE (320*240*3) + +#include "font.h" + +//rendering stuff +void drawLine(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b, char a); + +void drawRectColor(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, u8 *rgbaColor); + +void drawText(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, s16 x, s16 y); + +void drawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, u16 length, s16 x, s16 y); + +void drawTextf(gfxScreen_t screen, gfx3dSide_t side, font_s *f, s16 x, s16 y, const char *fmt, ...); + +void drawImage(gfxScreen_t screen, gfx3dSide_t side, u8* img, bool rgba, u16 w, u16 h, s16 x, s16 y, char a); + +void fillColor(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3]); + +void fillColorGradient(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3]); + +void drawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbaColor[4], s16 x, s16 y, u16 width, u16 height); + +void clearTop(u8 top1[3], u8 top2[3]); + +void clearBot(u8 bot[8]); + +void swapFrameBuffers(); diff --git a/source/font.c b/source/font.c index 7fe9292..509bce3 100644 --- a/source/font.c +++ b/source/font.c @@ -13,5 +13,5 @@ font_s fontDefault = defaultData, defaultDesc, 16, - (u8[]) {0xFF, 0xFF, 0xFF} - }; \ No newline at end of file + (u8[]) {0xFF, 0xFF, 0xFF, 0xFF} + }; diff --git a/source/font.h b/source/font.h index c155605..166bf4e 100644 --- a/source/font.h +++ b/source/font.h @@ -10,7 +10,7 @@ typedef struct { u8 *data; charDesc_s *desc; u8 height; - u8 color[3]; + u8 color[4]; } font_s; extern u8 defaultData[]; diff --git a/source/gfx.c b/source/gfx.c deleted file mode 100644 index 7b55c5a..0000000 --- a/source/gfx.c +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include - -#ifdef ARM9 - -#include "arm9/source/common.h" -#include "memory.h" -#else -#include <3ds.h> -#endif - -#include -#include "gfx.h" -#include "text.h" - -#ifdef ARM9 - -u8 *gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16 *width, u16 *height) { - if (screen == GFX_TOP) { - if (width) *width = 240; - if (height) *height = 400; - return PTR_TOP_SCREEN_BUF; - } else { - if (width) *width = 240; - if (height) *height = 320; - return PTR_BOT_SCREEN_BUF; - } -} - -#endif - -void drawPixel(int x, int y, char r, char g, char b, u8 *screen) { - int height = 240; - - u32 v = (height - 1 - y + x * height) * 3; - screen[v] = b; - screen[v + 1] = g; - screen[v + 2] = r; -} - -void drawLine(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b) { - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - - int x, y; - if (x1 == x2) { - if (y1 < y2) for (y = y1; y < y2; y++) drawPixel(x1, y, r, g, b, fbAdr); - else for (y = y2; y < y1; y++) drawPixel(x1, y, r, g, b, fbAdr); - } else { - if (x1 < x2) for (x = x1; x < x2; x++) drawPixel(x, y1, r, g, b, fbAdr); - else for (x = x2; x < x1; x++) drawPixel(x, y1, r, g, b, fbAdr); - } -} - -void drawRect(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b) { - drawLine(screen, side, x1, y1, x2, y1, r, g, b); - drawLine(screen, side, x2, y1, x2, y2, r, g, b); - drawLine(screen, side, x1, y2, x2, y2, r, g, b); - drawLine(screen, side, x1, y1, x1, y2, r, g, b); -} - -void drawRectColor(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, u8 *color) { - drawRect(screen, side, x1, y1, x2, y2, color[0], color[1], color[2]); -} - -void gfxDrawTextf(gfxScreen_t screen, gfx3dSide_t side, font_s *f, s16 x, s16 y, const char *fmt, ...) { - char s[512]; - memset(s, 0, 512); - va_list args; - va_start(args, fmt); - int len = vsprintf(s, fmt, args); - va_end(args); - if (len) - gfxDrawText(screen, side, f, s, x, y); -} - -void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, s16 x, s16 y) { - if (!str)return; - if (!f)f = &fontDefault; - - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - - drawString(fbAdr, f, str, x, 240 - y, fbHeight, fbWidth); -} - -void gfxDrawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, u16 length, s16 x, s16 y) { - if (!str)return; - if (!f)f = &fontDefault; - - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - - drawStringN(fbAdr, f, str, length, x, 240 - y, fbHeight, fbWidth); -} - -void gfxFillColor(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3]) { - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - - //TODO : optimize; use GX command ? - int i; - for (i = 0; i < fbWidth * fbHeight; i++) { - *(fbAdr++) = rgbColor[2]; - *(fbAdr++) = rgbColor[1]; - *(fbAdr++) = rgbColor[0]; - } -} - -void gfxFillColorGradient(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3]) { - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - u8 colorLine[fbWidth * 3]; - - //TODO : optimize; use GX command ? - int i; - float n; - float total = (float) (fbWidth - 1); - // make slightly bigger to prevent gradients from blending around. SHould be removed and have the gradient color be better later. - total *= 1.5f; - for (i = 0; i < fbWidth; i++) { - n = (float) i / total; - colorLine[i * 3 + 0] = (float) rgbColorStart[2] * (1.0f - n) + (float) rgbColorEnd[2] * n; - colorLine[i * 3 + 1] = (float) rgbColorStart[1] * (1.0f - n) + (float) rgbColorEnd[1] * n; - colorLine[i * 3 + 2] = (float) rgbColorStart[0] * (1.0f - n) + (float) rgbColorEnd[0] * n; - } - - for (i = 0; i < fbHeight; i++) { - memcpy(fbAdr, colorLine, fbWidth * 3); - fbAdr += fbWidth * 3; - } -} - -void _gfxDrawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[4], s16 x, s16 y, u16 width, u16 height) { - u16 fbWidth, fbHeight; - u8 *fbAdr = gfxGetFramebuffer(screen, side, &fbWidth, &fbHeight); - - if (x + width < 0 || x >= fbWidth)return; - if (y + height < 0 || y >= fbHeight)return; - - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - if (x + width >= fbWidth)width = fbWidth - x; - if (y + height >= fbHeight)height = fbHeight - y; - - if ( rgbColor[3] == 0xFF ) - { - u8 colorLine[width * 3]; - - int j; - for (j = 0; j < width; j++) { - colorLine[j * 3 + 0] = rgbColor[2]; - colorLine[j * 3 + 1] = rgbColor[1]; - colorLine[j * 3 + 2] = rgbColor[0]; - } - - fbAdr += fbWidth * 3 * y; - for (j = 0; j < height; j++) { - memcpy(&fbAdr[x * 3], colorLine, width * 3); - fbAdr += fbWidth * 3; - } - } - else - { - float alpha = (float)rgbColor[3] / 255.f; - float one_minus_alpha = 1.f - alpha; - int i, j; - fbAdr += fbWidth * 3 * y; - for (j = 0; j < height; j++) - { - for (i = 0; i < width; i++) - { - fbAdr[3*(i+x)+0] = (u8)(alpha*(float)rgbColor[0]+one_minus_alpha*(float)fbAdr[3*(i+x)+0]); - fbAdr[3*(i+x)+1] = (u8)(alpha*(float)rgbColor[1]+one_minus_alpha*(float)fbAdr[3*(i+x)+1]); - fbAdr[3*(i+x)+2] = (u8)(alpha*(float)rgbColor[2]+one_minus_alpha*(float)fbAdr[3*(i+x)+2]); - } - fbAdr += fbWidth * 3; - } - } -} - -void gfxDrawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[4], s16 x, s16 y, u16 width, u16 height) { - _gfxDrawRectangle(screen, side, rgbColor, 240 - y, x, height, width); -} - -void gfxClearTop(u8 top1[3], u8 top2[3]) { - gfxFillColorGradient(GFX_TOP, GFX_LEFT, top1, top2); -} - -void gfxClearBot(u8 bot[8]) { - gfxFillColor(GFX_BOTTOM, GFX_LEFT, bot); -} - -void gfxClear() { -#ifdef ARM9 - memset(PTR_TOP_SCREEN, 0, TOP_SCREEN_SIZE); - memset(PTR_BOT_SCREEN, 0, BOT_SCREEN_SIZE); - memset(PTR_TOP_SCREEN_BUF, 0, TOP_SCREEN_SIZE); - memset(PTR_BOT_SCREEN_BUF, 0, BOT_SCREEN_SIZE); - memset(PTR_TOP_BG, 0, TOP_SCREEN_SIZE); - memset(PTR_BOT_BG, 0, BOT_SCREEN_SIZE); -#else - gfxFillColor(GFX_TOP, GFX_LEFT, (u8[3]) {0x00, 0x00, 0x00}); - gfxFillColor(GFX_BOTTOM, GFX_LEFT, (u8[3]) {0x00, 0x00, 0x00}); -#endif -} - -void gfxSwap() { -#ifdef ARM9 - memcpy(PTR_TOP_SCREEN, PTR_TOP_SCREEN_BUF, TOP_SCREEN_SIZE); - memcpy(PTR_BOT_SCREEN, PTR_BOT_SCREEN_BUF, BOT_SCREEN_SIZE); -#else - gfxFlushBuffers(); - gfxSwapBuffers(); - gspWaitForVBlank(); -#endif -} diff --git a/source/gfx.h b/source/gfx.h deleted file mode 100644 index 6bc6d65..0000000 --- a/source/gfx.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#ifdef ARM9 - -#include "arm9/source/common.h" - -u8 *gfxGetFramebuffer(gfxScreen_t screen, gfx3dSide_t side, u16 *width, u16 *height); - -#define TOP_SCREEN_SIZE (400*240*3) -#define BOT_SCREEN_SIZE (320*240*3) -#else -#include <3ds.h> -#endif - -#include "font.h" - -//rendering stuff -void drawLine(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, char r, char g, char b); - -void drawRectColor(gfxScreen_t screen, gfx3dSide_t side, int x1, int y1, int x2, int y2, u8 *color); - -void gfxDrawText(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, s16 x, s16 y); - -void gfxDrawTextN(gfxScreen_t screen, gfx3dSide_t side, font_s *f, char *str, u16 length, s16 x, s16 y); - -void gfxDrawTextf(gfxScreen_t screen, gfx3dSide_t side, font_s *f, s16 x, s16 y, const char *fmt, ...); - -void gfxFillColor(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3]); - -void gfxFillColorGradient(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColorStart[3], u8 rgbColorEnd[3]); - -void gfxDrawRectangle(gfxScreen_t screen, gfx3dSide_t side, u8 rgbColor[3], s16 x, s16 y, u16 width, u16 height); - -void gfxClearTop(u8 top1[3], u8 top2[3]); - -void gfxClearBot(u8 bot[8]); - -void gfxClear(); - -void gfxSwap(); diff --git a/source/hb_menu/boot.c b/source/hb_menu/boot.c index 81bb0e4..70f04bf 100644 --- a/source/hb_menu/boot.c +++ b/source/hb_menu/boot.c @@ -69,8 +69,7 @@ bool isNinjhax2(void) { int bootApp(char *executablePath, executableMetadata_s *em, char *arg) { // open file that we're going to boot up fsInit(); - FSUSER_OpenFileDirectly(&hbFileHandle, sdmcArchive, fsMakePath(PATH_ASCII, executablePath), - FS_OPEN_READ, 0); + FSUSER_OpenFile(&hbFileHandle, sdmcArchive, fsMakePath(PATH_ASCII, executablePath), FS_OPEN_READ, 0); fsExit(); // set argv/argc @@ -161,4 +160,4 @@ int bootApp(char *executablePath, executableMetadata_s *em, char *arg) { } return 0; -} \ No newline at end of file +} diff --git a/source/hb_menu/netloader.c b/source/hb_menu/netloader.c index d3f77ec..6cb2552 100644 --- a/source/hb_menu/netloader.c +++ b/source/hb_menu/netloader.c @@ -14,7 +14,7 @@ #include #include #include -#include "gfx.h" +#include "draw.h" #define ZLIB_CHUNK (16 * 1024) @@ -55,9 +55,9 @@ static int netloader_draw_progress(void) { //gfxSwapBuffers(); //gspWaitForVBlank(); drawBg(); - gfxDrawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, "%s: %s", netloadedPath, progress); - gfxSwap(); + swapFrameBuffers(); return 0; } diff --git a/source/loader.c b/source/loader.c index 9735552..2c31a46 100644 --- a/source/loader.c +++ b/source/loader.c @@ -1,26 +1,108 @@ #ifndef ARM9 -#include #include <3ds.h> +#include #include "brahma.h" #else -#include "gfx.h" +#include "arm9/source/common.h" #include "arm9/source/fatfs/ff.h" #include "stage2_bin.h" +#include "memory.h" +#include "arm9/source/firm.h" +#include "arm9/source/screen.h" #endif +#include +#include "draw.h" +#include "loader.h" #include "utility.h" -#include "memory.h" + +int screenBehavior(gfxScreen_t screen, int screenSize, char* splash) +{ + if ( NULL == splash || splash[0] == '\0' ) + return 0; + + u8* fb = gfxGetFramebuffer(screen, GFX_LEFT, NULL, NULL); + if ( splash[0] == '/' ) + fileRead(splash, fb, screenSize); + else if ( strcasecmp(splash, "clear") == 0 ) + memset(fb, 0, screenSize); + + return 1; +} + +void screensBehavior(char* splashTop, char* splashBot) +{ +#ifndef ARM9 + gfxSet3D(false); +#endif + + int needSwap = screenBehavior(GFX_TOP, TOP_SCREEN_SIZE, splashTop); + needSwap += screenBehavior(GFX_BOTTOM, BOT_SCREEN_SIZE, splashBot); + + if (needSwap) + swapFrameBuffers(); +} #ifdef ARM9 -int load_bin(char *path, long offset) { +unsigned char *memsearch(unsigned char *startPos, const void *pattern, int size, int patternSize) // From Luma3DS' pathchanger.c +{ + const unsigned char *patternc = (const unsigned char *)pattern; + + //Preprocessing + int table[256]; + + int i; + for(i = 0; i < 256; ++i) + table[i] = patternSize + 1; + for(i = 0; i < patternSize; ++i) + table[patternc[i]] = patternSize - i; + + //Searching + int j = 0; + + while(j <= size - patternSize) + { + if(memcmp(patternc, startPos + j, patternSize) == 0) + return startPos + j; + j += table[startPos[j + patternSize]]; + } - size_t size = fileSize(path); - if (!size) { + return NULL; +} + +void applyPatches(unsigned char* binaryData, size_t binarySize, long offset, binary_patch* patches, int patchesCount) +{ + for (int i = 0 ; i < patchesCount ; i++) + { + unsigned char* binFound = binaryData; + int curOccurence = 0; + while ( NULL != (binFound = memsearch(binFound, patches[i].memToSearch, binarySize-offset+binaryData-binFound, patches[i].memToSearchSize)) ) + { + curOccurence++; + if ( patches[i].occurence == 0 || curOccurence == patches[i].occurence ) + { + memcpy(binFound, patches[i].memOverwrite, patches[i].memOverwriteSize); + + if ( patches[i].occurence > 0 ) + break; + binFound += patches[i].memOverwriteSize; + } + else + binFound += patches[i].memToSearchSize; + } + } +} + +int load_bin(char *path, long offset, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot) { + + // Load binary + size_t payloadSize = fileSize(path); + if (!payloadSize) { return -1; } @@ -28,7 +110,8 @@ int load_bin(char *path, long offset) { return -1; } - gfxClear(); + applyPatches((unsigned char*)PTR_PAYLOAD_MAIN_DATA, payloadSize, offset, patches, patchesCount); + screensBehavior(splashTop, splashBot); memcpy((void *) PTR_PAYLOAD_STAGE2, stage2_bin, stage2_bin_size); ((void (*)()) PTR_PAYLOAD_STAGE2)(); @@ -36,19 +119,54 @@ int load_bin(char *path, long offset) { return 0; } +static Firm *const firm = (Firm *const)PTR_PAYLOAD_FIRM_DATA; +extern struct fb fbs[2]; + +int load_firm(char *path, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot) +{ + u32 *loaderAddress = (u32 *)PTR_PAYLOAD_FIRM; + u32 maxPayloadSize = (u32)((u8 *)loaderAddress - (u8 *)firm); + + size_t payloadSize = fileSize(path); + if (!payloadSize || payloadSize <= 0x200 || payloadSize > maxPayloadSize) { + return -1; + } + + if (fileRead(path, firm, payloadSize) != 0) { + return -1; + } + + if(!checkFirmPayload(firm, payloadSize)) { + return -1; + } + + applyPatches((unsigned char*)firm, payloadSize, sizeof(Firm), patches, patchesCount); + screensBehavior(splashTop, splashBot); + + // Luma3DS loading support + char absPath[256]; + char *argv[2] = {computeFullPath(path, absPath), (char *)fbs}; + + launchFirm((firm->reserved2[0] & 1) ? 2 : 1, argv); + return 0; +} + #else char boot_app[512]; bool boot_app_enabled; -int load_3dsx(char *path) { +int load_3dsx(char *path, char* splashTop, char* splashBot) { + + screensBehavior(splashTop, splashBot); memset(boot_app, 0, 512); strncpy(boot_app, path, 512); boot_app_enabled = true; return 0; } -int load_bin(char *path, long offset) { +int load_bin(char *path, long offset, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot) { + screensBehavior(splashTop, splashBot); gfxExit(); if (brahma_init()) { @@ -69,7 +187,7 @@ int load_bin(char *path, long offset) { #endif -int load(char *path, long offset) { +int load(char *path, long offset, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot) { // check for reboot/poweroff if (strcasecmp(path, "reboot") == 0) { reboot(); @@ -79,14 +197,17 @@ int load(char *path, long offset) { } else if (strcasecmp(path, "homemenu") == 0) { return load_homemenu(); #endif - } else { + } else if (NULL != path && path[0] != '\0') { + const char *ext = get_filename_ext(path); - if (strcasecmp(ext, "bin") == 0 - || strcasecmp(ext, "dat") == 0) { - return load_bin(path, offset); -#ifndef ARM9 + if (strcasecmp(ext, "bin") == 0 || strcasecmp(ext, "dat") == 0) { + return load_bin(path, offset, patches, patchesCount, splashTop, splashBot); +#ifdef ARM9 + } else if (strcasecmp(ext, "firm") == 0) { + return load_firm(path, patches, patchesCount, splashTop, splashBot); +#else } else if (strcasecmp(ext, "3dsx") == 0) { - return load_3dsx(path); + return load_3dsx(path, splashTop, splashBot); #endif } else { debug("Invalid file: %s\n", path); diff --git a/source/loader.h b/source/loader.h index 4b26e03..e66357b 100644 --- a/source/loader.h +++ b/source/loader.h @@ -1,12 +1,22 @@ #ifndef _loader_h_ #define _loader_h_ -int load(char *path, long offset); +typedef struct { + char memToSearch[256]; + char memOverwrite[256]; + int memToSearchSize; + int memOverwriteSize; + int occurence; +} binary_patch; -int load_bin(char *path, long offset); +int load(char *path, long offset, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot); -#ifndef ARM9 -int load_3dsx(char *path); +void screensBehavior(char* splashTop, char* splashBot); + +#ifdef ARM9 +int load_firm(char *path, binary_patch* patches, int patchesCount, char* splashTop, char* splashBot); +#else +int load_3dsx(char *path, char* splashTop, char* splashBot); #endif #endif // _loader_h_ diff --git a/source/main.c b/source/main.c index 47ae98b..3d3d015 100644 --- a/source/main.c +++ b/source/main.c @@ -2,20 +2,23 @@ #include #include #include "config.h" +#include "anim.h" #include "utility.h" #include "menu.h" #ifdef ARM9 +#include "memory.h" #include "arm9/source/common.h" #include "arm9/source/hid.h" #include "arm9/source/fatfs/ff.h" +#include "arm9/source/screen.h" #else + #include <3ds.h> #include "hb_menu/netloader.h" - extern char boot_app[512]; extern bool boot_app_enabled; @@ -41,6 +44,7 @@ void __appExit() { gfxExit(); netloader_exit(); configExit(); + animExit(); amExit(); ptmuExit(); acExit(); @@ -53,13 +57,71 @@ void __appExit() { } #endif +#ifdef ARM9 +int startCtrBootManager(int argc, char **argv, u32 magicWord) { +#else int main() { +#endif #ifdef ARM9 *(u32 *) 0x10000020 = 0; *(u32 *) 0x10000020 = 0x340; - FATFS fs; - f_mount(&fs, "0:", 0); + + // Setup framebuffers: + // May be corrupted if screen were not initialized or if framebuffers were pushed on arguments + fbs[0].top_left = (u8 *)*PTR_TOP_SCREEN; + fbs[1].top_left = (u8 *)*PTR_TOP_SCREEN; + fbs[0].top_right = (u8 *)*PTR_TOP_RIGHT_SCREEN; + fbs[1].top_right = (u8 *)*PTR_TOP_RIGHT_SCREEN; + fbs[0].bottom = (u8 *)*PTR_BOT_SCREEN; + fbs[1].bottom = (u8 *)*PTR_BOT_SCREEN; + + // Code inspired from Luma3DS + u16 launchedPath[4]; + if ((magicWord & 0xFFFF) == 0xBEEF && argc >= 1) + { + u32 i; + for(i = 0; i < 4 && argv[0][i] != 0; i++) + launchedPath[i] = argv[0][i]; + + // Framebuffers management + if(argc >= 2) + { + u8 **fb = (u8 **)(void *)argv[1]; + fbs[0].top_left = fb[0]; + fbs[0].top_right = fb[1]; + fbs[0].bottom = fb[2]; + + if (argc >= 4) + { + fbs[1].top_left = fb[3]; + fbs[1].top_right = fb[4]; + fbs[1].bottom = fb[5]; + } + else + { + fbs[1].top_left = fbs[0].top_left; + fbs[1].top_right = fbs[0].top_right; + fbs[1].bottom = fbs[0].bottom; + } + + *PTR_TOP_SCREEN = fbs[0].top_left; + *PTR_TOP_RIGHT_SCREEN = fbs[0].top_right; + *PTR_BOT_SCREEN = fbs[0].bottom; + } + } + else if (magicWord == 0xBABE && argc == 2) + { + u32 i; + u16 *p = (u16 *)argv[0]; + for(i = 0; i < 4 && p[i] != 0; i++) + launchedPath[i] = p[i]; + } + else + launchedPath[0] = 0; + + bool loadedFromSD = (memcmp(launchedPath, "n\0a\0n\0d\0", 8) != 0); + initFileSystems(loadedFromSD); #else if (netloader_init() != 0) { // fix SOC_Initialize @@ -69,18 +131,24 @@ int main() { osSetSpeedupEnable(true); // offset potential issues caused by homebrew that just ran (from hb_menu) - aptOpenSession(); + //aptOpenSession(); APT_SetAppCpuTimeLimit(0); - aptCloseSession(); + //aptCloseSession(); if (!boot_app_enabled) { // fix SOC_Initialize #endif + animInit(); if (configInit() != 0 || config->count <= 0) { // recovery + #ifdef ARM9 + initScreens(config->brightness); + #endif + animSetup(); while (aptMainLoop()) { if (menu_more() == 0) break; } } else { + animSetup(); while (aptMainLoop()) { if (menu_boot() == 0) break; diff --git a/source/memory.h b/source/memory.h index 5f0be6d..154f993 100644 --- a/source/memory.h +++ b/source/memory.h @@ -5,20 +5,29 @@ #ifndef CTRBOOTMANAGER_MEMORY_H #define CTRBOOTMANAGER_MEMORY_H -#define PTR_TOP_SCREEN (u8*)(*(u32*)0x23FFFE00) -#define PTR_BOT_SCREEN (u8*)(*(u32*)0x23FFFE08) -#define PTR_TOP_SCREEN_BUF (u8*)(0x2507E700) -#define PTR_BOT_SCREEN_BUF (u8*)(0x250C4A00) -#define PTR_TOP_BG (u8*)(0x25000000) -#define PTR_BOT_BG (u8*)(0x25046300) -#define PTR_CFG (u8*)(0x25200000) -#define PTR_CFG_TMP (u8*)(0x25230000) -#define PTR_PICKER (u8*)(0x25210000) -#define PTR_PICKER_FILE (u8*)(0x25220000) +#define PTR_TOP_SCREEN (void**)(0x23FFFE00) +#define PTR_TOP_RIGHT_SCREEN (void**)(0x23FFFE04) +#define PTR_BOT_SCREEN (void**)(0x23FFFE08) +#define PTR_TOP_SCREEN_BUF (u8*)(0x25000000) +#define PTR_BOT_SCREEN_BUF (u8*)(0x25046500) +#define PTR_TOP_BG (u8*)(0x2507E900) +#define PTR_BOT_BG (u8*)(0x250DC500) +#define PTR_FILE_SYSTEMS (u8*)(0x25130000) +#define PTR_CFG (u8*)(0x25140000) +#define PTR_PICKER (u8*)(0x25150000) +#define PTR_PICKER_FILE (u8*)(0x25160000) +#define PTR_CFG_TMP (u8*)(0x25170000) +#define PTR_ANIM (u8*)(0x25180000) +#define PTR_MOVIE_COMP (u8*)(0x25190000) +#define PTR_DYNAMIC_MEM_START (u8*)(0x252A0000) + #define PTR_PAYLOAD_MAIN 0x23F00000 #define PTR_PAYLOAD_MAIN_DATA 0x26000000 #define PTR_PAYLOAD_SIZE_MAX 0x000FFD00 #define PTR_PAYLOAD_STAGE2 0x08006000 +#define PTR_PAYLOAD_FIRM 0x27FFE000 +#define PTR_PAYLOAD_FIRM_DATA 0x20001000 + #endif //CTRBOOTMANAGER_MEMORY_H diff --git a/source/menu.c b/source/menu.c index ce1e769..c565ee9 100644 --- a/source/menu.c +++ b/source/menu.c @@ -6,24 +6,53 @@ #include #include #include -#include "gfx.h" +#include "draw.h" +#include "anim.h" #include "config.h" #include "menu.h" void drawBg() { - if (!config->imgError) { - memcpy(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), config->bgImgTopBuff, - (size_t) config->bgImgTopSize); - } else { - gfxClearTop(config->bgTop1, config->bgTop2); + + u16 fbWidth, fbHeight; + u8* fbTop = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &fbWidth, &fbHeight); + if (readMovie(fbTop, &anim->topMovie) < 0) + { + if (!config->imgError && 0xFF == anim->imgTopFade && !config->imgTopIsRGBA) { + memcpy(fbTop, config->bgImgTopBuff, (size_t) config->bgImgTopSize); + } else { + clearTop(anim->bgTop1, anim->bgTop2); + if (!config->imgError && anim->imgTopFade) + drawImage(GFX_TOP, GFX_LEFT, config->bgImgTopBuff, config->imgTopIsRGBA, fbHeight, fbWidth, 0, 0, anim->imgTopFade); + } } - if (!config->imgErrorBot) { - memcpy(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), config->bgImgBotBuff, - (size_t) config->bgImgBotSize); - } else { - gfxClearBot(config->bgBot); + u8* fbBot = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &fbWidth, &fbHeight); + if (readMovie(fbBot, &anim->botMovie) < 0) + { + if (!config->imgErrorBot && 0xFF == anim->imgBotFade && !config->imgBotIsRGBA) { + memcpy(fbBot, config->bgImgBotBuff, (size_t) config->bgImgBotSize); + } else { + clearBot(anim->bgBot); + if (!config->imgErrorBot && anim->imgBotFade) + drawImage(GFX_BOTTOM, GFX_LEFT, config->bgImgBotBuff, config->imgBotIsRGBA, fbHeight, fbWidth, 0, 0, anim->imgBotFade); + } + } + drawRectColor(GFX_TOP, GFX_LEFT, MENU_MIN_X, MENU_MIN_Y - 20, MENU_MAX_X, MENU_MAX_Y, anim->borders); + + u8* fbTop3D = IS3DACTIVE ? gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, &fbWidth, &fbHeight) : NULL; + if (fbTop3D) + { + if (readMovie(fbTop3D, &anim->top3DMovie) < 0) + { + if (!config->imgError3D && 0xFF == anim->imgTopFade && !config->imgTop3DIsRGBA) { + memcpy(fbTop3D, config->bgImgTop3DBuff, (size_t) config->bgImgTop3DSize); + } else { + if (!config->imgError3D && anim->imgTopFade) + drawImage(GFX_TOP, GFX_RIGHT, config->bgImgTop3DBuff, config->imgTop3DIsRGBA, fbHeight, fbWidth, 0, 0, anim->imgTopFade); + } + } + + drawRectColor(GFX_TOP, GFX_RIGHT, MENU_MIN_X, MENU_MIN_Y - 20, MENU_MAX_X, MENU_MAX_Y, anim->borders); } - drawRectColor(GFX_TOP, GFX_LEFT, MENU_MIN_X, MENU_MIN_Y - 20, MENU_MAX_X, MENU_MAX_Y, config->borders); } void drawTitle(const char *format, ...) { @@ -34,7 +63,10 @@ void drawTitle(const char *format, ...) { vsnprintf(msg, 512, format, argp); va_end(argp); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, msg, 140, 25); + memcpy(fontDefault.color, anim->fntDef, sizeof(u8[4])); + drawText(GFX_TOP, GFX_LEFT, &fontDefault, msg, 140, 25); + if( IS3DACTIVE ) + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, msg, 140, 25); } void drawItem(bool selected, int y, const char *format, ...) { @@ -46,13 +78,17 @@ void drawItem(bool selected, int y, const char *format, ...) { va_end(argp); if (selected) { - gfxDrawRectangle(GFX_TOP, GFX_LEFT, config->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); + drawRectangle(GFX_TOP, GFX_LEFT, anim->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); + if( IS3DACTIVE ) + drawRectangle(GFX_TOP, GFX_RIGHT, anim->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); } - memcpy(fontDefault.color, selected ? config->fntSel : config->fntDef, sizeof(u8[3])); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, msg, (s16) (MENU_MIN_X + 6), - (s16) y + (s16) MENU_MIN_Y); + memcpy(fontDefault.color, selected ? anim->fntSel : anim->fntDef, sizeof(u8[4])); + + drawText(GFX_TOP, GFX_LEFT, &fontDefault, msg, (s16) (MENU_MIN_X + 6), (s16) y + (s16) MENU_MIN_Y); + if( IS3DACTIVE ) + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, msg, (s16) (MENU_MIN_X + 6), (s16) y + (s16) MENU_MIN_Y); - memcpy(fontDefault.color, config->fntDef, sizeof(u8[3])); + memcpy(fontDefault.color, anim->fntDef, sizeof(u8[4])); } void drawItemN(bool selected, int maxChar, int y, const char *format, ...) { @@ -64,13 +100,16 @@ void drawItemN(bool selected, int maxChar, int y, const char *format, ...) { va_end(argp); if (selected) { - gfxDrawRectangle(GFX_TOP, GFX_LEFT, config->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); + drawRectangle(GFX_TOP, GFX_LEFT, anim->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); + if( IS3DACTIVE ) + drawRectangle(GFX_TOP, GFX_RIGHT, anim->highlight, (s16) (MENU_MIN_X + 4), (s16) (y + MENU_MIN_Y), 361, 15); } - memcpy(fontDefault.color, selected ? config->fntSel : config->fntDef, sizeof(u8[3])); - gfxDrawTextN(GFX_TOP, GFX_LEFT, &fontDefault, msg, maxChar, (s16) (MENU_MIN_X + 6), - (s16) y + (s16) MENU_MIN_Y); + memcpy(fontDefault.color, selected ? anim->fntSel : anim->fntDef, sizeof(u8[4])); + drawTextN(GFX_TOP, GFX_LEFT, &fontDefault, msg, maxChar, (s16) (MENU_MIN_X + 6), (s16) y + (s16) MENU_MIN_Y); + if( IS3DACTIVE ) + drawTextN(GFX_TOP, GFX_RIGHT, &fontDefault, msg, maxChar, (s16) (MENU_MIN_X + 6), (s16) y + (s16) MENU_MIN_Y); - memcpy(fontDefault.color, config->fntDef, sizeof(u8[3])); + memcpy(fontDefault.color, anim->fntDef, sizeof(u8[4])); } void drawInfo(const char *format, ...) { @@ -81,6 +120,18 @@ void drawInfo(const char *format, ...) { vsnprintf(msg, 512, format, argp); va_end(argp); - gfxDrawText(GFX_BOTTOM, GFX_LEFT, &fontDefault, "Informations", (s16) (MENU_MIN_X + 6), 40); - gfxDrawText(GFX_BOTTOM, GFX_LEFT, &fontDefault, msg, (s16) (MENU_MIN_X + 12), 80); + memcpy(fontDefault.color, config->fntBotActive ? anim->fntBot : anim->fntDef, sizeof(u8[4])); + drawText(GFX_BOTTOM, GFX_LEFT, &fontDefault, "Informations", (s16) (MENU_MIN_X + 6), 40); + drawText(GFX_BOTTOM, GFX_LEFT, &fontDefault, msg, (s16) (MENU_MIN_X + 12), 80); + + memcpy(fontDefault.color, anim->fntDef, sizeof(u8[4])); +} + +char* splashScreen(char* splash, char* defSplash) +{ + if ( NULL != splash && splash[0] != '\0') + return splash; + if ( NULL != defSplash && defSplash[0] != '\0') + return defSplash; + return NULL; } diff --git a/source/menu.h b/source/menu.h index e0e73a3..0aaa3ab 100644 --- a/source/menu.h +++ b/source/menu.h @@ -16,12 +16,16 @@ void drawItemN(bool selected, int maxChar, int y, const char *format, ...); void drawInfo(const char *format, ...); +char* splashScreen(char* splash, char* defSplash); + int menu_more(); int menu_boot(); +int menu_password(); + int menu_config(); int menu_netloader(); -#endif \ No newline at end of file +#endif diff --git a/source/menu_boot.c b/source/menu_boot.c index aecf81e..d625daf 100644 --- a/source/menu_boot.c +++ b/source/menu_boot.c @@ -2,6 +2,7 @@ #include "arm9/source/common.h" #include "arm9/source/hid.h" +#include "arm9/source/screen.h" #else #include <3ds.h> @@ -9,10 +10,11 @@ #endif #include "loader.h" -#include "gfx.h" +#include "draw.h" #include "utility.h" #include "menu.h" #include "config.h" +#include "anim.h" static bool timer = true; @@ -35,18 +37,17 @@ int key_override(int index) { int boot(int index) { -#ifdef ARM9 - return load(config->entries[index].path, - config->entries[index].offset); -#else +#ifndef ARM9 int delay = config->autobootfix; while (aptMainLoop() && delay > 0) { - gfxSwap(); + swapFrameBuffers(); delay--; } - return load(config->entries[index].path, - config->entries[index].offset); #endif + boot_entry_s* entry = &config->entries[index]; + return load(entry->path, entry->offset, entry->patches, entry->patchesCount, + splashScreen(entry->splashTop, config->splashTopDef), + splashScreen(entry->splashBot, config->splashBotDef)); } int menu_boot() { @@ -64,15 +65,39 @@ int menu_boot() { if (key & BIT(config->recovery) || config->timeout < 0) { // disable autoboot timer = false; } else if (config->timeout == 0 || key_override(boot_index) != boot_index) { // autoboot + #ifdef ARM9 + if (config->directBootScreenInit) + initScreens(config->brightness); + else + { + // Screen was not initialize, we must absolutly avoid any splash screen! + boot_entry_s* entry = &config->entries[key_override(boot_index)]; + entry->splashTop[0] = '\0'; + entry->splashBot[0] = '\0'; + config->splashTopDef[0] = '\0'; + config->splashBotDef[0] = '\0'; + } + #endif return boot(key_override(boot_index)); } #ifndef ARM9 time(&start); +#else + initScreens(config->brightness); #endif + static const u32 checkedKeys = 0x0FFF; + + // Trash first key + hidScanInput(); + hidKeysDown(); + while (aptMainLoop()) { + hidScanInput(); + u32 kDown = hidKeysDown(); + if (timer) { #ifdef ARM9 now++; @@ -85,39 +110,47 @@ int menu_boot() { int index = key_override(boot_index); return boot(index); } + + if (kDown & checkedKeys) + timer = false; } - hidScanInput(); - u32 kDown = hidKeysDown(); + if (!timer) + { + if ( menu_password() ) + kDown = 0; - if (kDown & KEY_DOWN) { - timer = false; - boot_index++; - if (boot_index > config->count) - boot_index = 0; - } - else if (kDown & KEY_UP) { - timer = false; - boot_index--; - if (boot_index < 0) - boot_index = config->count; - } - else if (kDown & KEY_A) { - timer = false; - if (boot_index == config->count) { - if(menu_more() == 0) { + if (kDown & KEY_DOWN) { + boot_index++; + if (boot_index > config->count) + boot_index = 0; + } + else if (kDown & KEY_UP) { + boot_index--; + if (boot_index < 0) + boot_index = config->count; + } + if (kDown & KEY_RIGHT) { + boot_index = (boot_index == config->count) ? 0 : config->count; + } + else if (kDown & KEY_LEFT) { + boot_index = (boot_index == 0) ? config->count : 0; + } + else if (kDown & KEY_A) { + if (boot_index == config->count) { + if(menu_more() == 0) { + break; + } + } else if (boot(boot_index) == 0) { break; } - } else if (boot(boot_index) == 0) { - break; } - } - else if (kDown & KEY_X) { - timer = false; - if (boot_index != config->count) { - if (confirm(3, "Delete boot entry: \"%s\" ?\n", config->entries[boot_index].title)) { - configRemoveEntry(boot_index); - boot_index = config->index; + else if (kDown & KEY_X) { + if (boot_index != config->count) { + if (confirm(3, "Delete boot entry: \"%s\" ?\n", config->entries[boot_index].title)) { + configRemoveEntry(boot_index); + boot_index = config->index; + } } } } @@ -128,11 +161,50 @@ int menu_boot() { return 0; } +void drawThumbnail(gfxScreen_t screen, gfx3dSide_t side, thumbnail_s* thumbnail) +{ + if ( NULL == thumbnail->imgBuff ) + return; + if ( 0 == thumbnail->loaded ) + { + // Lazy image load + size_t size = fileSize(thumbnail->path); + if ( size == -1 || size == 0 ) { + thumbnail->loaded = -1; + return; + } + + size_t maxsize = thumbnail->sizeX * thumbnail->sizeY * (thumbnail->isRGBA?4:3); + if (size > maxsize) + size = maxsize; + + if (fileRead(thumbnail->path, thumbnail->imgBuff, size) != 0) { + thumbnail->loaded = -1; + return; + } + + thumbnail->loaded = 1; + } + if ( thumbnail->loaded <= 0 ) + return; + + drawImage(screen, side, thumbnail->imgBuff, thumbnail->isRGBA, thumbnail->sizeX, thumbnail->sizeY, thumbnail->posX, thumbnail->posY, anim->thumbFade); +} + static void draw(int boot_index, time_t elapsed) { int i = 0; drawBg(); + + if ( boot_index < config->count ) + { + boot_entry_s* selEntry = &config->entries[boot_index]; + drawThumbnail(GFX_TOP, GFX_LEFT, &selEntry->thumbTop); + if ( IS3DACTIVE ) + drawThumbnail(GFX_TOP, GFX_RIGHT, &selEntry->thumbTop3D); + drawThumbnail(GFX_BOTTOM, GFX_LEFT, &selEntry->thumbBot); + } if (!timer) { drawTitle("*** Select a boot entry ***"); @@ -140,19 +212,45 @@ static void draw(int boot_index, time_t elapsed) { drawTitle("*** Booting %s in %i ***", config->entries[boot_index].title, config->timeout - elapsed); } - for (i = 0; i < config->count; i++) { - drawItem(i == boot_index, 16 * i, config->entries[i].title); - if (i == boot_index) { - drawInfo("Name: %s\nPath: %s\nOffset: 0x%lx\n\n\nPress (A) to launch\nPress (X) to remove entry\n", - config->entries[i].title, - config->entries[i].path, - config->entries[i].offset); + int entriesStart = ENTRIES_COUNT_INC * (boot_index / ENTRIES_COUNT_INC); + int entriesEnd = entriesStart + ENTRIES_COUNT_INC; + if ( entriesEnd > config->count ) + entriesEnd = config->count; + int displayedEntries = entriesEnd - entriesStart; + + for (i = 0; i < displayedEntries; i++) { + boot_entry_s* curEntry = &config->entries[entriesStart+i]; + bool selected = (entriesStart+i == boot_index); + drawItem(selected, 16 * i, curEntry->title); + if (selected) { + #ifdef ARM9 + if ( curEntry->patchesCount > 0 ) + { + drawInfo("Name: %s\nPath: %s\nOffset: 0x%lx\nPatches count: %d\n\nPress (A) to launch\nPress (X) to remove entry\n", + curEntry->title, + curEntry->path, + curEntry->offset, + curEntry->patchesCount); + } + else + #endif + { + drawInfo("Name: %s\nPath: %s\nOffset: 0x%lx\n\n\nPress (A) to launch\nPress (X) to remove entry\n", + curEntry->title, + curEntry->path, + curEntry->offset); + } } } - drawItem(boot_index == config->count, 16 * i, "More..."); - if (boot_index == config->count) { - drawInfo("Show more options ..."); + + if ( i < ENTRIES_COUNT_INC ) + { + bool selected = (boot_index == config->count); + drawItem(selected, 16 * i, "More..."); + if (selected) { + drawInfo("Show more options ..."); + } } - gfxSwap(); + swapFrameBuffers(); } diff --git a/source/menu_config.c b/source/menu_config.c index 3acb347..b217f96 100644 --- a/source/menu_config.c +++ b/source/menu_config.c @@ -1,16 +1,24 @@ +#include +#include +#include + #ifdef ARM9 #include "arm9/source/common.h" #include "arm9/source/hid.h" +#include "arm9/source/screen.h" #else #include <3ds.h> #endif -#include #include "config.h" -#include "gfx.h" +#include "draw.h" #include "menu.h" #include "utility.h" + +static int pass_size = 0; +static int pass_fail = -1; + void keyLeft(int index) { switch (index) { @@ -28,11 +36,28 @@ void keyLeft(int index) { if (config->recovery > 0) config->recovery--; else - config->recovery = 11; + config->recovery = MAX_KEY; break; case 3: + #ifdef ARM9 + if (config->brightness < BRIGHTNESS_COUNT-1) + config->brightness++; + #else if (config->autobootfix > 0) config->autobootfix--; + #endif + break; + case 4: + if ( config->passMaxAttempt > 0 ) + config->passMaxAttempt--; + break; + case 5: + if (pass_fail > -1) + pass_fail--; + break; + case 6: + if (pass_size > 0) + pass_size--; break; } } @@ -50,23 +75,118 @@ void keyRight(int index) { config->index = 0; break; case 2: - if (config->recovery < 11) + if (config->recovery < MAX_KEY) config->recovery++; else config->recovery = 0; break; case 3: + #ifdef ARM9 + if (config->brightness > 0) + config->brightness--; + #else config->autobootfix++; + #endif + break; + case 4: + config->passMaxAttempt++; + break; + case 5: + #ifdef ARM9 + if (pass_fail < config->count+1) + #else + if (pass_fail < config->count+2) + #endif + pass_fail++; + break; + case 6: + if (pass_size < PASSWORD_MAX_SIZE) + pass_size++; break; } } +void keyIn(int index) { + + if ( index != 7 ) + return; // Currently, only password is managed + + static const u32 checkedKeys = 0x0FFF; + config->passSize = pass_size; + + hidScanInput(); + u32 kDownPrev = (hidKeysDown() & checkedKeys); + int curKey = 0; + + while (aptMainLoop() && curKey < pass_size) + { + hidScanInput(); + u32 kDown = (hidKeysDown() & checkedKeys); + + if ( 0 == kDownPrev && 0 != kDown ) + { + kDownPrev = kDown; + int value = 0; + while ( (kDown >> (value+1)) != 0 ) + value++; + + config->password[curKey] = value; + curKey++; + } + kDownPrev = kDown; + + drawBg(); + drawTitle("*** Password setup ***"); + + char msg[256]; + int size = 0; + size += snprintf(msg, 256, "Enter the new password (%i keys):\n", pass_size); + for ( int i = 0 ; i < curKey ; i++ ) + { + size += snprintf(msg+size, 256, get_button(config->password[i])); + if ( i < curKey-1 ) + size += snprintf(msg+size, 256, (i%4 == 3) ? "\n" : ", "); + } + msg[size] = '\0'; + + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 64, MENU_MIN_Y + 64, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 64, MENU_MIN_Y + 64, msg); + + swapFrameBuffers(); + } +} + int menu_config() { -#ifdef ARM9 - int menu_count = 3, menu_index = 0; -#else - int menu_count = 4, menu_index = 0; + + pass_size = config->passSize; + pass_fail = -1; + + if ( strcasecmp(config->failPath, "reboot") == 0 ) + pass_fail = config->count; + else if (strcasecmp(config->failPath, "shutdown") == 0) + pass_fail = config->count+1; +#ifndef ARM9 + else if (strcasecmp(config->failPath, "homemenu") == 0) + pass_fail = config->count+2; #endif + else + { + for ( int i = 0 ; i < config->count ; i++ ) + { + boot_entry_s* entry = &config->entries[i]; + if ( 0 == entry->patchesCount && strcasecmp(config->failPath, entry->path) == 0 + && config->failOffset == entry->offset ) + { + pass_fail = i; + break; + } + } + } + + bool pass_fail_found = ( pass_fail >= 0 ); + int menu_count = 8, menu_index = 0; + // key repeat timer time_t t_start = 0, t_end = 0, t_elapsed = 0; @@ -81,56 +201,171 @@ int menu_config() { time(&t_start); // reset held timer } #endif - if (kDown & KEY_DOWN) { - menu_index++; - if (menu_index >= menu_count) - menu_index = 0; - } - else if (kDown & KEY_UP) { - menu_index--; - if (menu_index < 0) - menu_index = menu_count - 1; + if (START_DRIVE_RO) + { + if (kDown & KEY_B) + return 0; + + drawBg(); + drawTitle("*** Informations ***"); + + char* msg = "CtrBootManager has been started from SysNAND.\n\ +For safety reasons, the configuration file cannot be saved.\n\n\ +Please manually create a configuration file named\n\ +\"CtrBootManager.cfg\" and write it to SysNAND root.\n\n\ +I decline all responsability for any harm on your 3DS system!\n\ +Do it at your own risk!!!"; + + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 32, MENU_MIN_Y + 32, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 32, MENU_MIN_Y + 32, msg); } + else + { + if (kDown & KEY_DOWN) { + menu_index++; + if (menu_index >= menu_count) + menu_index = 0; + } + else if (kDown & KEY_UP) { + menu_index--; + if (menu_index < 0) + menu_index = menu_count - 1; + } - if (kDown & KEY_LEFT) { - keyLeft(menu_index); - time(&t_start); - } else if (kHeld & KEY_LEFT) { - time(&t_end); - t_elapsed = t_end - t_start; - if (t_elapsed > 0) { + if (kDown & KEY_LEFT) { keyLeft(menu_index); - svcSleep(100); + time(&t_start); + } else if (kHeld & KEY_LEFT) { + time(&t_end); + t_elapsed = t_end - t_start; + if (t_elapsed > 0) { + keyLeft(menu_index); + svcSleep(100); + } } - } - if (kDown & KEY_RIGHT) { - keyRight(menu_index); - time(&t_start); - } else if (kHeld & KEY_RIGHT) { - time(&t_end); - t_elapsed = t_end - t_start; - if (t_elapsed > 0) { + if (kDown & KEY_RIGHT) { keyRight(menu_index); - svcSleep(100); + time(&t_start); + } else if (kHeld & KEY_RIGHT) { + time(&t_end); + t_elapsed = t_end - t_start; + if (t_elapsed > 0) { + keyRight(menu_index); + svcSleep(100); + } + } + + if (kDown & KEY_A) { + keyIn(menu_index); } - } - if (kDown & KEY_B) { - configSave(); - return 0; - } + if (kDown & KEY_B) { - drawBg(); - drawTitle("*** Boot configuration ***"); + // Apply password failed behavior + int size = 0; + if ( pass_fail == config->count ) + size = snprintf(config->failPath, 256, "reboot"); + else if ( pass_fail == config->count+1 ) + size = snprintf(config->failPath, 256, "shutdown"); + #ifndef ARM9 + else if ( pass_fail == config->count+2 ) + size = snprintf(config->failPath, 256, "homemenu"); + #endif + else if ( pass_fail >= 0 ) + { + boot_entry_s* entry = &config->entries[pass_fail]; + size = snprintf(config->failPath, 256, entry->path); + config->failOffset = entry->offset; + } + if ( pass_fail >= 0 || pass_fail_found ) + config->failPath[size] = '\0'; + + configSave(); + return 0; + } - drawItem(menu_index == 0, 0, "Timeout: %i", config->timeout); - drawItem(menu_index == 1, 16, "Default: %s", config->entries[config->index].title); - drawItem(menu_index == 2, 32, "Recovery key: %s", get_button(config->recovery)); -#ifndef ARM9 - drawItem(menu_index == 3, 48, "Bootfix: %i", config->autobootfix); + drawBg(); + drawTitle("*** Boot configuration ***"); + + if ( config->timeout >= 0 ) + drawItem(menu_index == 0, 0, "Timeout: %i", config->timeout); + else + drawItem(menu_index == 0, 0, "Timeout: Disabled"); + + drawItem(menu_index == 1, 16, "Default: %s", config->entries[config->index].title); + drawItem(menu_index == 2, 32, "Recovery key: %s", get_button(config->recovery)); + + if ( config->passMaxAttempt > 0 ) + drawItem(menu_index == 4, 80, "Password allowed attempts: %i", config->passMaxAttempt); + else + drawItem(menu_index == 4, 80, "Password allowed attempts: Infinite"); + + char msg[256]; + int size = 0; + if ( pass_fail < 0 ) + { + if (config->failPath[0] == '\0' || pass_fail_found) + { + if (config->failSplashTop[0] != '\0' || config->failSplashBot[0] != '\0') + size = snprintf(msg, 256, "Freeze on failure images"); + else if (config->splashTopDef[0] != '\0' || config->splashBotDef[0] != '\0') + size = snprintf(msg, 256, "Freeze on default splash screen"); + else + size = snprintf(msg, 256, "Freeze on last rendered image"); + } + else + size = snprintf(msg, 256, "Load \"%s\"", config->failPath); + } + else if ( pass_fail == config->count ) + size = snprintf(msg, 256, "Reboot"); + else if ( pass_fail == config->count+1 ) + size = snprintf(msg, 256, "Power off"); + #ifndef ARM9 + else if ( pass_fail == config->count+2 ) + size = snprintf(msg, 256, "Return to home menu"); + #endif + else + size = snprintf(msg, 256, "Load %s", config->entries[pass_fail].title); + msg[size] = '\0'; + + drawItem(menu_index == 5, 96, "Password fail behavior: %s", msg); + drawItem(menu_index == 6, 112, "Password size: %i", pass_size); + drawItem(menu_index == 7, 128, "Password:"); + + size = 0; + if ( config->passSize > 0 ) + { + for ( int i = 0 ; i < config->passSize ; i++ ) + { + size += snprintf(msg+size, 256, get_button(config->password[i])); + if ( i < config->passSize-1 ) + size += snprintf(msg+size, 256, (i%4 == 3) ? "\n" : ", "); + } + } + else + size = snprintf(msg, 256, "None"); + msg[size] = '\0'; + + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 64, MENU_MIN_Y + 128, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 64, MENU_MIN_Y + 128, msg); + + #ifdef ARM9 + drawItem(menu_index == 3, 48, "Screen initialization brightness: %i", BRIGHTNESS_COUNT-config->brightness); + #else + drawItem(menu_index == 3, 48, "Bootfix: %i", config->autobootfix); + #endif + } + +#ifdef ARM9 + drawInfo("CtrBootManager9 v2.5.2"); +#else + drawInfo("CtrBootManager v2.5.2"); #endif - gfxSwap(); + + swapFrameBuffers(); } return -1; } diff --git a/source/menu_more.c b/source/menu_more.c index 46db358..12bfbcd 100644 --- a/source/menu_more.c +++ b/source/menu_more.c @@ -4,11 +4,17 @@ #include "arm9/source/hid.h" #else + #include <3ds.h> +#include #include #include "menu_netloaderarm9.h" + #endif -#include "gfx.h" + +#include +#include "config.h" +#include "draw.h" #include "utility.h" #include "loader.h" #include "menu.h" @@ -17,10 +23,12 @@ #ifdef ARM9 #define MENU_COUNT 4 static char menu_item[4][64] = {"File browser", "Settings", "Reboot", "PowerOff"}; +#define SETTING_ID 1 #else #define MENU_COUNT 6 static char menu_item[6][64] = {"File browser", "Netload 3dsx", "Netload arm9", "Settings", "Reboot", "PowerOff"}; +#define SETTING_ID 3 #endif static int menu_index = 0; @@ -32,15 +40,21 @@ int menu_choose() { memset(&picked, 0, sizeof(file_s)); pick_file(&picked, "/"); - if (strlen(picked.path) > 0) { - return load(picked.path, 0); + int pathLength = 0; + if ((pathLength = strlen(picked.path)) > 0) { + int offset = ( pathLength > 3 && strcasecmp(&picked.path[pathLength-3], "dat") == 0 ) ? 0x12000 : 0; + return load(picked.path, offset, NULL, 0, config->splashTopDef, config->splashBotDef); } return -1; } int menu_more() { + + if (START_DRIVE_RO) + sprintf(menu_item[SETTING_ID], "Informations"); + menu_password(); menu_index = 0; while (aptMainLoop()) { @@ -58,6 +72,12 @@ int menu_more() { if (menu_index < 0) menu_index = MENU_COUNT - 1; } + else if (kDown & KEY_RIGHT) { + menu_index = (menu_index == MENU_COUNT - 1) ? 0 : (MENU_COUNT - 1); + } + else if (kDown & KEY_LEFT) { + menu_index = (menu_index == 0) ? (MENU_COUNT - 1) : 0; + } else if (kDown & KEY_A) { if (menu_index == 0 && menu_choose() == 0) { return 0; @@ -110,7 +130,10 @@ static void draw() { drawInfo("Browse for a file to boot"); break; case 1: - drawInfo("Edit boot settings"); + if (START_DRIVE_RO) + drawInfo("See informations"); + else + drawInfo("Edit boot settings"); break; case 2: drawInfo("Reboot the 3ds..."); @@ -129,7 +152,10 @@ static void draw() { drawInfo("Netload a file (arm9) from the computer with nc"); break; case 3: - drawInfo("Edit boot settings"); + if (START_DRIVE_RO) + drawInfo("See informations"); + else + drawInfo("Edit boot settings"); break; case 4: drawInfo("Reboot the 3ds..."); @@ -142,5 +168,5 @@ static void draw() { break; } - gfxSwap(); + swapFrameBuffers(); } diff --git a/source/menu_netloader.c b/source/menu_netloader.c index 4af7943..43a1b2b 100644 --- a/source/menu_netloader.c +++ b/source/menu_netloader.c @@ -1,6 +1,7 @@ #include <3ds.h> #include -#include "gfx.h" +#include "config.h" +#include "draw.h" #include "netloader.h" #include "utility.h" #include "loader.h" @@ -22,8 +23,10 @@ int menu_netloader() { ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF, NETLOADER_PORT); drawBg(); - gfxDrawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); - gfxSwap(); + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); + swapFrameBuffers(); while (aptMainLoop()) { @@ -38,7 +41,7 @@ int menu_netloader() { int rc = netloader_loop(); if (rc > 0) { netloader_boot = true; - return load_3dsx(netloadedPath); + return load_3dsx(netloadedPath, config->splashTopDef, config->splashBotDef); } else if (rc < 0) { netloader_draw_error(); break; diff --git a/source/menu_netloaderarm9.c b/source/menu_netloaderarm9.c index 616fb7b..590b4e6 100644 --- a/source/menu_netloaderarm9.c +++ b/source/menu_netloaderarm9.c @@ -12,7 +12,7 @@ #include #include "utility.h" #include "menu.h" -#include "gfx.h" +#include "draw.h" int menu_netloaderarm9(void) { @@ -51,8 +51,10 @@ int menu_netloaderarm9(void) { "IP: %s, Port: %d\n\nPress B to cancel\n", inet_ntoa(sa.sin_addr), BRAHMA_NETWORK_PORT); drawBg(); - gfxDrawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); - gfxSwap(); + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, msg); + swapFrameBuffers(); sflags = fcntl(sockfd, F_GETFL); if (sflags == -1) { @@ -84,8 +86,8 @@ int menu_netloaderarm9(void) { if (recvd != -1) { total += recvd; drawBg(); - gfxDrawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, "%i", total); - gfxSwap(); + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 16, "%i", total); + swapFrameBuffers(); } if (total >= ARM9_PAYLOAD_MAX_SIZE) { debug("Err: invalid payload size\n"); diff --git a/source/menu_password.c b/source/menu_password.c new file mode 100644 index 0000000..453d8a6 --- /dev/null +++ b/source/menu_password.c @@ -0,0 +1,117 @@ +#include + +#ifdef ARM9 + +#include "arm9/source/common.h" +#include "arm9/source/hid.h" + +#else + +#include <3ds.h> +#include "menu_netloaderarm9.h" + +#endif + +#include "anim.h" +#include "config.h" +#include "draw.h" +#include "utility.h" +#include "loader.h" +#include "menu.h" + +static bool passSuccess = false; +static int attempt = 0; +static int keyIndex = 0; + +static void draw(); + +static u32 kDownPrev = 0; +static u32 kDown = 0; + +int menu_password() { + + if ( passSuccess ) + return 0; + + if ( config->passSize <= 0 ) + { + passSuccess = true; + return 0; + } + + static const u32 checkedKeys = 0x0FFF; + + hidScanInput(); + kDownPrev = (hidKeysDown() & checkedKeys); + int validCount = 0; + + while (aptMainLoop()) { + + hidScanInput(); + kDown = (hidKeysDown() & checkedKeys); + + if ( 0 == kDownPrev && 0 != kDown ) + { + if ( kDown & BIT(config->password[keyIndex]) ) + validCount++; + keyIndex++; + + if ( keyIndex >= config->passSize ) + { + if ( validCount >= config->passSize ) + { + passSuccess = true; + break; + } + + keyIndex = 0; + validCount = 0; + attempt++; + + if ( config->passMaxAttempt > 0 && attempt >= config->passMaxAttempt ) + { + // Fail! + char* splashTop = splashScreen(config->failSplashTop, config->splashTopDef); + char* splashBot = splashScreen(config->failSplashBot, config->splashBotDef); + load(config->failPath, config->failOffset, NULL, 0, splashTop, splashBot); + while (aptMainLoop()) // Loop of death + screensBehavior(splashTop, splashBot); + return -1; + } + } + } + kDownPrev = kDown; + + draw(); + } + return 1; +} + +static void draw() { + + drawBg(); + drawTitle("*** Password required ***", kDownPrev, kDown); + + char passStars[PASSWORD_MAX_SIZE+1]; + int i = 0; + for ( ; i < keyIndex ; i++ ) + passStars[i] = 'X'; + for ( ; i < config->passSize ; i++ ) + passStars[i] = '_'; + passStars[i] = '\0'; + + char* invalidMsg = ( 0 == keyIndex && attempt > 0 ) ? "\nInvalid password, try again!\n" : ""; + char msg[256]; + if ( config->passMaxAttempt > 0 ) + sprintf(msg, "Password: %s\n\nAttempt %d on %d\n%s", passStars, attempt+1, config->passMaxAttempt, invalidMsg); + else + sprintf(msg, "Password: %s\n%s", passStars, invalidMsg); + + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 96, MENU_MIN_Y + 48, msg); + if ( IS3DACTIVE ) + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 96, MENU_MIN_Y + 48, msg); + + drawInfo("Please enter the password for menu access...\n"); + + swapFrameBuffers(); +} diff --git a/source/menu_picker.c b/source/menu_picker.c index de7dabc..abed7c8 100644 --- a/source/menu_picker.c +++ b/source/menu_picker.c @@ -4,16 +4,19 @@ #include "arm9/source/hid.h" #include "arm9/source/fatfs/ff.h" #include "memory.h" + #else + #include <3ds.h> #include -#include #include #include +#include #endif -#include "gfx.h" +#include +#include "draw.h" #include "picker.h" #include "utility.h" #include "config.h" @@ -52,7 +55,11 @@ void parse_file(struct dirent *file) { const char *ext = get_filename_ext(file->d_name); if (strcasecmp(ext, "bin") != 0 && strcasecmp(ext, "dat") != 0 + #ifdef ARM9 + && strcasecmp(ext, "firm") != 0) + #else && strcasecmp(ext, "3dsx") != 0) + #endif return; } @@ -146,19 +153,22 @@ static void draw() { drawItemN(i == picker->file_index, 47, 16 * y, picker->files[i].name); if (i == picker->file_index && !picker->files[i].isDir) { - drawInfo("Press (A) to launch\nPress (X) to add to boot menu"); - + if (START_DRIVE_RO) + drawInfo("Press (A) to launch"); + else + drawInfo("Press (A) to launch\nPress (X) to add to boot menu"); } y++; } - gfxSwap(); + swapFrameBuffers(); } void pick_file(file_s *picked, const char *path) { #ifdef ARM9 picker = (picker_s *) PTR_PICKER; + int curDrive = getDefaultDrive(); #else picker = malloc(sizeof(picker_s)); #endif @@ -184,42 +194,42 @@ void pick_file(file_s *picked, const char *path) { time(&t_start); // reset held timer } #endif - if (kDown & KEY_DOWN) { - picker->file_index++; + if (kDown & KEY_DOWN || kDown & KEY_RIGHT) { + picker->file_index += (kDown & KEY_DOWN) ? 1 : MAX_LINE; if (picker->file_index >= picker->file_count) - picker->file_index = 0; + picker->file_index = (kDown & KEY_DOWN || picker->file_index == picker->file_count - 1 + MAX_LINE) ? 0 : (picker->file_count - 1); #ifndef ARM9 time(&t_start); #endif - } else if (kHeld & KEY_DOWN) { + } else if (kHeld & KEY_DOWN || kHeld & KEY_RIGHT) { #ifndef ARM9 time(&t_end); #endif t_elapsed = t_end - t_start; if (t_elapsed > 0) { - picker->file_index++; + picker->file_index += (kDown & KEY_DOWN) ? 1 : MAX_LINE; if (picker->file_index >= picker->file_count) - picker->file_index = 0; + picker->file_index = (kDown & KEY_DOWN || picker->file_index == picker->file_count - 1 + MAX_LINE) ? 0 : (picker->file_count - 1); svcSleep(100); } } - if (kDown & KEY_UP) { - picker->file_index--; + if (kDown & KEY_UP || kDown & KEY_LEFT) { + picker->file_index -= (kDown & KEY_UP) ? 1 : MAX_LINE; if (picker->file_index < 0) - picker->file_index = picker->file_count - 1; + picker->file_index = (kDown & KEY_UP || picker->file_index == -MAX_LINE) ? (picker->file_count - 1) : 0; #ifndef ARM9 time(&t_start); #endif - } else if (kHeld & KEY_UP) { + } else if (kHeld & KEY_UP || kDown & KEY_LEFT) { #ifndef ARM9 time(&t_end); #endif t_elapsed = t_end - t_start; if (t_elapsed > 0) { - picker->file_index--; + picker->file_index -= (kDown & KEY_UP) ? 1 : MAX_LINE; if (picker->file_index < 0) - picker->file_index = picker->file_count - 1; + picker->file_index = (kDown & KEY_UP || picker->file_index == -MAX_LINE) ? (picker->file_count - 1) : 0; svcSleep(100); } } @@ -230,7 +240,13 @@ void pick_file(file_s *picked, const char *path) { if (!picker->files[index].isDir) { if (confirm(0, "Launch \"%s\" ?", picker->files[index].name)) { strncpy(picked->name, picker->files[index].name, 128); + #ifdef ARM9 + if (curDrive != getDefaultDrive()) + computeFullPath(picker->files[index].path, picked->path); + else + #endif strncpy(picked->path, picker->files[index].path, 256); + picked->isDir = picker->files[index].isDir; picked->size = picker->files[index].size; break; @@ -240,22 +256,37 @@ void pick_file(file_s *picked, const char *path) { get_dir(picker->files[index].path); } } - } else if (kDown & KEY_X) { + } else if (kDown & KEY_X && !START_DRIVE_RO) { int index = picker->file_index; if (!picker->files[index].isDir) { const char *ext = get_filename_ext(picker->files[index].name); + int noOffsetReq = 1; + if (strcasecmp(ext, "bin") == 0 || (noOffsetReq = strcasecmp(ext, "dat")) == 0 #ifdef ARM9 - if (strcasecmp(ext, "bin") == 0 || strcasecmp(ext, "dat") == 0) { + || strcasecmp(ext, "firm") == 0) { #else - if (strcasecmp(ext, "3dsx") == 0) { + || strcasecmp(ext, "3dsx") == 0) { #endif if (confirm(3, "Add entry to boot menu: \"%s\" ?", picker->files[index].name)) { - if (config->count > CONFIG_MAX_ENTRIES - 1) { - debug("Maximum entries reached (%i)\n", CONFIG_MAX_ENTRIES); - } else if (configAddEntry(picker->files[index].name, picker->files[index].path, 0) == 0) { - debug("Added entry: %s\n", picker->files[index].name); - } else { - debug("Error adding entry: %s\n", picker->files[index].name); + if (config->count > config->maxCount - 1) { + debug("Maximum entries reached (%i)\n", config->maxCount); + } + else + { + #ifdef ARM9 + char entryPath[256]; + if (curDrive != getDefaultDrive()) + computeFullPath(picker->files[index].path, entryPath); + else + strncpy(entryPath, picker->files[index].path, 256); + #else + char* entryPath = picker->files[index].path; + #endif + if (configAddEntry(picker->files[index].name, entryPath, noOffsetReq?0:0x12000) == 0) { + debug("Added entry: %s\n", picker->files[index].name); + } else { + debug("Error adding entry: %s\n", picker->files[index].name); + } } } } @@ -280,8 +311,27 @@ void pick_file(file_s *picked, const char *path) { // enter new dir get_dir(picker->now_path); } + #ifdef ARM9 + else if (kDown & KEY_SELECT) { + int prevDrive = curDrive; + do { + curDrive = (curDrive+1)%MANAGED_DRIVES_COUNT; + } while (!switchCurrentDrive(curDrive)); + + if (curDrive != prevDrive) + { + memset(picker, 0, sizeof(picker_s)); + picker->file_count = 0; + picker->file_index = 0; + + get_dir(path); + } + } + #endif } -#ifndef ARM9 +#ifdef ARM9 + setCurrentDriveAsDefault(); +#else free(picker); #endif } diff --git a/source/movie.c b/source/movie.c new file mode 100644 index 0000000..fe8c034 --- /dev/null +++ b/source/movie.c @@ -0,0 +1,116 @@ +#include "movie.h" + +#include "config.h" +#include "utility.h" + +int readMovieFrame(void* iFileHandlePtr, int* ioFileOffset, comp_manager_s* iCompMgr, char* oFrameBuffer, int iFrameBufferSize) +{ + if ( NULL == iCompMgr ) + { + int readBytes = fileHandleRead(iFileHandlePtr, oFrameBuffer, iFrameBufferSize, *ioFileOffset); + if ( readBytes < 0 ) + return -1; + *ioFileOffset += readBytes; + return 0; + } + else + { + int readBytes = fileHandleRead(iFileHandlePtr, iCompMgr->frame_comp, 9, *ioFileOffset); // Read top animations QuickLZ header + if (readBytes != 9) // If it couldn't read the header + return -1; // Disable the animation + *ioFileOffset += readBytes; + + size_t comp_size = qlz_size_compressed(iCompMgr->frame_comp); // Get compressed size + + readBytes = fileHandleRead(iFileHandlePtr, iCompMgr->frame_comp + 9, comp_size - 9, *ioFileOffset); // Read compressed buffer + + if (readBytes != (comp_size - 9) || readCompressedMovieFrameFromBuffer(iCompMgr, oFrameBuffer, iFrameBufferSize)) // Check if EOF + return -1; // Disable the animation + + *ioFileOffset += readBytes; + return 0; + } +} + +int readCompressedMovieFrameFromBuffer(comp_manager_s* iCompMgr, char* oFrameBuffer, int iFrameBufferSize) +{ + if ( qlz_size_decompressed(iCompMgr->frame_comp) != iFrameBufferSize ) + return -1; + + qlz_decompress(iCompMgr->frame_comp, oFrameBuffer, &iCompMgr->state); // Decompress the frame + + if ( iCompMgr->reverse ) + { + for (size_t i = 0; i < iFrameBufferSize; i++) // Perform delta decoding + oFrameBuffer[i] = iCompMgr->frame_prev_read[i] - oFrameBuffer[i]; + } + else + { + for (size_t i = 0; i < iFrameBufferSize; i++) // Perform delta decoding + oFrameBuffer[i] += iCompMgr->frame_prev_read[i]; + } + + memcpy(iCompMgr->frame_prev_write, oFrameBuffer, iFrameBufferSize); // Keep a copy of the fb + return 0; +} + +int getMovieFramesCount(void* iFileHandlePtr, int iFrameBufferSize, int iCompressed) +{ + size_t fileSize = fileHandleSize(iFileHandlePtr); + if ( !iCompressed ) + return fileSize / iFrameBufferSize; + else + { + char compHeader[9]; + int framesCount = 0; + int fileOffset = 0; + + while ( 9 == fileHandleRead(iFileHandlePtr, compHeader, 9, fileOffset) ) + { + size_t comp_size = qlz_size_compressed(compHeader); // Get compressed size + if ( fileOffset + comp_size > fileSize ) + break; + fileOffset += comp_size; + framesCount++; + } + + return framesCount; + } +} + +void getMovieDataOffsetForFrames(void* iFileHandlePtr, int iFrameBufferSize, int iCompressed, int iStartFrame, int iEndFrame, + int* oStartFrameOffset, int* oDataSizeToEndFrame) +{ + size_t fileSize = fileHandleSize(iFileHandlePtr); + if ( !iCompressed ) + { + int framesCount = fileSize / iFrameBufferSize; + if ( NULL != oStartFrameOffset ) + *oStartFrameOffset = iFrameBufferSize * ( ( iStartFrame < framesCount ) ? iStartFrame : (framesCount-1) ); + if ( NULL != oDataSizeToEndFrame ) + *oDataSizeToEndFrame = iFrameBufferSize * ( ( iEndFrame < framesCount ) ? (iEndFrame+1) : framesCount ); + } + else + { + char compHeader[9]; + int curFrame = 0; + int fileOffset = 0; + int firstFrameOffset = -1; + + while ( curFrame <= iEndFrame && 9 == fileHandleRead(iFileHandlePtr, compHeader, 9, fileOffset) ) + { + size_t comp_size = qlz_size_compressed(compHeader); // Get compressed size + if ( fileOffset + comp_size > fileSize ) + break; + if ( curFrame == iStartFrame ) + firstFrameOffset = fileOffset; + fileOffset += comp_size; + curFrame++; + } + + if ( NULL != oStartFrameOffset ) + *oStartFrameOffset = firstFrameOffset; + if ( NULL != oDataSizeToEndFrame ) + *oDataSizeToEndFrame = fileOffset; + } +} diff --git a/source/movie.h b/source/movie.h new file mode 100644 index 0000000..5d48414 --- /dev/null +++ b/source/movie.h @@ -0,0 +1,29 @@ +#ifndef _movie_h_ +#define _movie_h_ + +#include "quicklz.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + qlz_state_decompress state; + const char* frame_prev_read; + char* frame_prev_write; + char* frame_comp; + int reverse; +} comp_manager_s; + +int readMovieFrame(void* iFileHandlePtr, int* ioFileOffset, comp_manager_s* iCompMgr, char* oFrameBuffer, int iFrameBufferSize); +int readCompressedMovieFrameFromBuffer(comp_manager_s* iCompMgr, char* oFrameBuffer, int iFrameBufferSize); + +int getMovieFramesCount(void* iFileHandlePtr, int iFrameBufferSize, int iCompressed); +void getMovieDataOffsetForFrames(void* iFileHandlePtr, int iFrameBufferSize, int iCompressed, int iStartFrame, int iEndFrame, + int* oStartFrameOffset, int* oDataSizeToEndFrame); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/quicklz.c b/source/quicklz.c new file mode 100644 index 0000000..3742129 --- /dev/null +++ b/source/quicklz.c @@ -0,0 +1,848 @@ +// Fast data compression library +// Copyright (C) 2006-2011 Lasse Mikkel Reinhold +// lar@quicklz.com +// +// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything +// released into public must be open source) or under a commercial license if such +// has been acquired (see http://www.quicklz.com/order.html). The commercial license +// does not cover derived or ported versions created by third parties under GPL. + +// 1.5.0 final + +#include "quicklz.h" + +#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0 + #error quicklz.c and quicklz.h have different versions +#endif + +#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64)) + #define X86X64 +#endif + +#define MINOFFSET 2 +#define UNCONDITIONAL_MATCHLEN 6 +#define UNCOMPRESSED_END 4 +#define CWORD_LEN 4 + +#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 + #define OFFSET_BASE source + #define CAST (ui32)(size_t) +#else + #define OFFSET_BASE 0 + #define CAST +#endif + +int qlz_get_setting(int setting) +{ + switch (setting) + { + case 0: return QLZ_COMPRESSION_LEVEL; + case 1: return sizeof(qlz_state_compress); + case 2: return sizeof(qlz_state_decompress); + case 3: return QLZ_STREAMING_BUFFER; +#ifdef QLZ_MEMORY_SAFE + case 6: return 1; +#else + case 6: return 0; +#endif + case 7: return QLZ_VERSION_MAJOR; + case 8: return QLZ_VERSION_MINOR; + case 9: return QLZ_VERSION_REVISION; + } + return -1; +} + +#if QLZ_COMPRESSION_LEVEL == 1 +static int same(const unsigned char *src, size_t n) +{ + while(n > 0 && *(src + n) == *src) + n--; + return n == 0 ? 1 : 0; +} +#endif + +static void reset_table_compress(qlz_state_compress *state) +{ + int i; + for(i = 0; i < QLZ_HASH_VALUES; i++) + { +#if QLZ_COMPRESSION_LEVEL == 1 + state->hash[i].offset = 0; +#else + state->hash_counter[i] = 0; +#endif + } +} + +static void reset_table_decompress(qlz_state_decompress *state) +{ + int i; + (void)state; + (void)i; +#if QLZ_COMPRESSION_LEVEL == 2 + for(i = 0; i < QLZ_HASH_VALUES; i++) + { + state->hash_counter[i] = 0; + } +#endif +} + +static __inline ui32 hash_func(ui32 i) +{ +#if QLZ_COMPRESSION_LEVEL == 2 + return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1); +#else + return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1); +#endif +} + +static __inline ui32 fast_read(void const *src, ui32 bytes) +{ +#ifndef X86X64 + unsigned char *p = (unsigned char*)src; + switch (bytes) + { + case 4: + return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24); + case 3: + return(*p | *(p + 1) << 8 | *(p + 2) << 16); + case 2: + return(*p | *(p + 1) << 8); + case 1: + return(*p); + } + return 0; +#else + if (bytes >= 1 && bytes <= 4) + return *((ui32*)src); + else + return 0; +#endif +} + +static __inline ui32 hashat(const unsigned char *src) +{ + ui32 fetch, hash; + fetch = fast_read(src, 3); + hash = hash_func(fetch); + return hash; +} + +static __inline void fast_write(ui32 f, void *dst, size_t bytes) +{ +#ifndef X86X64 + unsigned char *p = (unsigned char*)dst; + + switch (bytes) + { + case 4: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + *(p + 2) = (unsigned char)(f >> 16); + *(p + 3) = (unsigned char)(f >> 24); + return; + case 3: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + *(p + 2) = (unsigned char)(f >> 16); + return; + case 2: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + return; + case 1: + *p = (unsigned char)f; + return; + } +#else + switch (bytes) + { + case 4: + *((ui32*)dst) = f; + return; + case 3: + *((ui32*)dst) = f; + return; + case 2: + *((ui16 *)dst) = (ui16)f; + return; + case 1: + *((unsigned char*)dst) = (unsigned char)f; + return; + } +#endif +} + + +size_t qlz_size_decompressed(const char *source) +{ + ui32 n, r; + n = (((*source) & 2) == 2) ? 4 : 1; + r = fast_read(source + 1 + n, n); + r = r & (0xffffffff >> ((4 - n)*8)); + return r; +} + +size_t qlz_size_compressed(const char *source) +{ + ui32 n, r; + n = (((*source) & 2) == 2) ? 4 : 1; + r = fast_read(source + 1, n); + r = r & (0xffffffff >> ((4 - n)*8)); + return r; +} + +size_t qlz_size_header(const char *source) +{ + size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1; + return n; +} + + +static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n) +{ + // Caution if modifying memcpy_up! Overlap of dst and src must be special handled. +#ifndef X86X64 + unsigned char *end = dst + n; + while(dst < end) + { + *dst = *src; + dst++; + src++; + } +#else + ui32 f = 0; + do + { + *(ui32 *)(dst + f) = *(ui32 *)(src + f); + f += MINOFFSET + 1; + } + while (f < n); +#endif +} + +static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s) +{ +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash; + hash = hashat(s); + state->hash[hash].offset = s; + state->hash_counter[hash] = 1; +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + hash = hashat(s); + c = state->hash_counter[hash]; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s; + c++; + state->hash_counter[hash] = c; +#endif + (void)state; + (void)s; +} + +#if QLZ_COMPRESSION_LEVEL <= 2 +static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max) +{ + while(*lh < max) + { + (*lh)++; + update_hash(state, *lh); + } +} +#endif + +static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state) +{ + const unsigned char *last_byte = source + size - 1; + const unsigned char *src = source; + unsigned char *cword_ptr = destination; + unsigned char *dst = destination + CWORD_LEN; + ui32 cword_val = 1U << 31; + const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; + ui32 fetch = 0; + unsigned int lits = 0; + + (void) lits; + + if(src <= last_matchstart) + fetch = fast_read(src, 3); + + while(src <= last_matchstart) + { + if ((cword_val & 1) == 1) + { + // store uncompressed if compression ratio is too low + if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5)) + return 0; + + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + + cword_ptr = dst; + dst += CWORD_LEN; + cword_val = 1U << 31; + fetch = fast_read(src, 3); + } +#if QLZ_COMPRESSION_LEVEL == 1 + { + const unsigned char *o; + ui32 hash, cached; + + hash = hash_func(fetch); + cached = fetch ^ state->hash[hash].cache; + state->hash[hash].cache = fetch; + + o = state->hash[hash].offset + OFFSET_BASE; + state->hash[hash].offset = CAST(src - OFFSET_BASE); + +#ifdef X86X64 + if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) + { + if(cached != 0) + { +#else + if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) + { + if (*(o + 3) != *(src + 3)) + { +#endif + hash <<= 4; + cword_val = (cword_val >> 1) | (1U << 31); + fast_write((3 - 2) | hash, dst, 2); + src += 3; + dst += 2; + } + else + { + const unsigned char *old_src = src; + size_t matchlen; + hash <<= 4; + + cword_val = (cword_val >> 1) | (1U << 31); + src += 4; + + if(*(o + (src - old_src)) == *src) + { + src++; + if(*(o + (src - old_src)) == *src) + { + size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1; + size_t remaining = q > 255 ? 255 : q; + src++; + while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining) + src++; + } + } + + matchlen = src - old_src; + if (matchlen < 18) + { + fast_write((ui32)(matchlen - 2) | hash, dst, 2); + dst += 2; + } + else + { + fast_write((ui32)(matchlen << 16) | hash, dst, 3); + dst += 3; + } + } + fetch = fast_read(src, 3); + lits = 0; + } + else + { + lits++; + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); +#ifdef X86X64 + fetch = fast_read(src, 3); +#else + fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16); +#endif + } + } +#elif QLZ_COMPRESSION_LEVEL >= 2 + { + const unsigned char *o, *offset2; + ui32 hash, matchlen, k, m, best_k = 0; + unsigned char c; + size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1); + (void)best_k; + + + //hash = hashat(src); + fetch = fast_read(src, 3); + hash = hash_func(fetch); + + c = state->hash_counter[hash]; + + offset2 = state->hash[hash].offset[0]; + if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0) + { + matchlen = 3; + if(*(offset2 + matchlen) == *(src + matchlen)) + { + matchlen = 4; + while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining) + matchlen++; + } + } + else + matchlen = 0; + for(k = 1; k < QLZ_POINTERS && c > k; k++) + { + o = state->hash[hash].offset[k]; +#if QLZ_COMPRESSION_LEVEL == 3 + if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) +#elif QLZ_COMPRESSION_LEVEL == 2 + if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) +#endif + { + m = 3; + while(*(o + m) == *(src + m) && m < remaining) + m++; +#if QLZ_COMPRESSION_LEVEL == 3 + if ((m > matchlen) || (m == matchlen && o > offset2)) +#elif QLZ_COMPRESSION_LEVEL == 2 + if (m > matchlen) +#endif + { + offset2 = o; + matchlen = m; + best_k = k; + } + } + } + o = offset2; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; + c++; + state->hash_counter[hash] = c; + +#if QLZ_COMPRESSION_LEVEL == 3 + if(matchlen > 2 && src - o < 131071) + { + ui32 u; + size_t offset = src - o; + + for(u = 1; u < matchlen; u++) + { + hash = hashat(src + u); + c = state->hash_counter[hash]++; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u; + } + + cword_val = (cword_val >> 1) | (1U << 31); + src += matchlen; + + if(matchlen == 3 && offset <= 63) + { + *dst = (unsigned char)(offset << 2); + dst++; + } + else if (matchlen == 3 && offset <= 16383) + { + ui32 f = (ui32)((offset << 2) | 1); + fast_write(f, dst, 2); + dst += 2; + } + else if (matchlen <= 18 && offset <= 1023) + { + ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2; + fast_write(f, dst, 2); + dst += 2; + } + + else if(matchlen <= 33) + { + ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3; + fast_write(f, dst, 3); + dst += 3; + } + else + { + ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3; + fast_write(f, dst, 4); + dst += 4; + } + } + else + { + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } +#elif QLZ_COMPRESSION_LEVEL == 2 + + if(matchlen > 2) + { + cword_val = (cword_val >> 1) | (1U << 31); + src += matchlen; + + if (matchlen < 10) + { + ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5); + fast_write(f, dst, 2); + dst += 2; + } + else + { + ui32 f = best_k | (matchlen << 16) | (hash << 5); + fast_write(f, dst, 3); + dst += 3; + } + } + else + { + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } +#endif + } +#endif + } + while (src <= last_byte) + { + if ((cword_val & 1) == 1) + { + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + cword_ptr = dst; + dst += CWORD_LEN; + cword_val = 1U << 31; + } +#if QLZ_COMPRESSION_LEVEL < 3 + if (src <= last_byte - 3) + { +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash, fetch; + fetch = fast_read(src, 3); + hash = hash_func(fetch); + state->hash[hash].offset = CAST(src - OFFSET_BASE); + state->hash[hash].cache = fetch; +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + hash = hashat(src); + c = state->hash_counter[hash]; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; + c++; + state->hash_counter[hash] = c; +#endif + } +#endif + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } + + while((cword_val & 1) != 1) + cword_val = (cword_val >> 1); + + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + + // min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument + return dst - destination < 9 ? 9 : dst - destination; +} + +static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history) +{ + const unsigned char *src = source + qlz_size_header((const char *)source); + unsigned char *dst = destination; + const unsigned char *last_destination_byte = destination + size - 1; + ui32 cword_val = 1; + const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; + unsigned char *last_hashed = destination - 1; + const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1; + static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; + + (void) last_source_byte; + (void) last_hashed; + (void) state; + (void) history; + + for(;;) + { + ui32 fetch; + + if (cword_val == 1) + { +#ifdef QLZ_MEMORY_SAFE + if(src + CWORD_LEN - 1 > last_source_byte) + return 0; +#endif + cword_val = fast_read(src, CWORD_LEN); + src += CWORD_LEN; + } + +#ifdef QLZ_MEMORY_SAFE + if(src + 4 - 1 > last_source_byte) + return 0; +#endif + + fetch = fast_read(src, 4); + + if ((cword_val & 1) == 1) + { + ui32 matchlen; + const unsigned char *offset2; + +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash; + cword_val = cword_val >> 1; + hash = (fetch >> 4) & 0xfff; + offset2 = (const unsigned char *)(size_t)state->hash[hash].offset; + + if((fetch & 0xf) != 0) + { + matchlen = (fetch & 0xf) + 2; + src += 2; + } + else + { + matchlen = *(src + 2); + src += 3; + } + +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + cword_val = cword_val >> 1; + hash = (fetch >> 5) & 0x7ff; + c = (unsigned char)(fetch & 0x3); + offset2 = state->hash[hash].offset[c]; + + if((fetch & (28)) != 0) + { + matchlen = ((fetch >> 2) & 0x7) + 2; + src += 2; + } + else + { + matchlen = *(src + 2); + src += 3; + } + +#elif QLZ_COMPRESSION_LEVEL == 3 + ui32 offset; + cword_val = cword_val >> 1; + if ((fetch & 3) == 0) + { + offset = (fetch & 0xff) >> 2; + matchlen = 3; + src++; + } + else if ((fetch & 2) == 0) + { + offset = (fetch & 0xffff) >> 2; + matchlen = 3; + src += 2; + } + else if ((fetch & 1) == 0) + { + offset = (fetch & 0xffff) >> 6; + matchlen = ((fetch >> 2) & 15) + 3; + src += 2; + } + else if ((fetch & 127) != 3) + { + offset = (fetch >> 7) & 0x1ffff; + matchlen = ((fetch >> 2) & 0x1f) + 2; + src += 3; + } + else + { + offset = (fetch >> 15); + matchlen = ((fetch >> 7) & 255) + 3; + src += 4; + } + + offset2 = dst - offset; +#endif + +#ifdef QLZ_MEMORY_SAFE + if(offset2 < history || offset2 > dst - MINOFFSET - 1) + return 0; + + if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1)) + return 0; +#endif + + memcpy_up(dst, offset2, matchlen); + dst += matchlen; + +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, dst - matchlen); + last_hashed = dst - 1; +#endif + } + else + { + if (dst < last_matchstart) + { + unsigned int n = bitlut[cword_val & 0xf]; +#ifdef X86X64 + *(ui32 *)dst = *(ui32 *)src; +#else + memcpy_up(dst, src, 4); +#endif + cword_val = cword_val >> n; + dst += n; + src += n; +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, dst - 3); +#endif + } + else + { + while(dst <= last_destination_byte) + { + if (cword_val == 1) + { + src += CWORD_LEN; + cword_val = 1U << 31; + } +#ifdef QLZ_MEMORY_SAFE + if(src >= last_source_byte + 1) + return 0; +#endif + *dst = *src; + dst++; + src++; + cword_val = cword_val >> 1; + } + +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant +#endif + return size; + } + + } + } +} + +size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state) +{ + size_t r; + ui32 compressed; + size_t base; + + if(size == 0 || size > 0xffffffff - 400) + return 0; + + if(size < 216) + base = 3; + else + base = 9; + +#if QLZ_STREAMING_BUFFER > 0 + if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER) +#endif + { + reset_table_compress(state); + r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state); +#if QLZ_STREAMING_BUFFER > 0 + reset_table_compress(state); +#endif + if(r == base) + { + memcpy(destination + base, source, size); + r = size + base; + compressed = 0; + } + else + { + compressed = 1; + } + state->stream_counter = 0; + } +#if QLZ_STREAMING_BUFFER > 0 + else + { + unsigned char *src = state->stream_buffer + state->stream_counter; + + memcpy(src, source, size); + r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state); + + if(r == base) + { + memcpy(destination + base, src, size); + r = size + base; + compressed = 0; + reset_table_compress(state); + } + else + { + compressed = 1; + } + state->stream_counter += size; + } +#endif + if(base == 3) + { + *destination = (unsigned char)(0 | compressed); + *(destination + 1) = (unsigned char)r; + *(destination + 2) = (unsigned char)size; + } + else + { + *destination = (unsigned char)(2 | compressed); + fast_write((ui32)r, destination + 1, 4); + fast_write((ui32)size, destination + 5, 4); + } + + *destination |= (QLZ_COMPRESSION_LEVEL << 2); + *destination |= (1 << 6); + *destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4); + +// 76543210 +// 01SSLLHC + + return r; +} + +size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state) +{ + size_t dsiz = qlz_size_decompressed(source); + +#if QLZ_STREAMING_BUFFER > 0 + if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER) +#endif + { + if((*source & 1) == 1) + { + reset_table_decompress(state); + dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination); + } + else + { + memcpy(destination, source + qlz_size_header(source), dsiz); + } + state->stream_counter = 0; + reset_table_decompress(state); + } +#if QLZ_STREAMING_BUFFER > 0 + else + { + unsigned char *dst = state->stream_buffer + state->stream_counter; + if((*source & 1) == 1) + { + dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer); + } + else + { + memcpy(dst, source + qlz_size_header(source), dsiz); + reset_table_decompress(state); + } + memcpy(destination, dst, dsiz); + state->stream_counter += dsiz; + } +#endif + return dsiz; +} + diff --git a/source/quicklz.h b/source/quicklz.h new file mode 100644 index 0000000..131a25b --- /dev/null +++ b/source/quicklz.h @@ -0,0 +1,151 @@ +#ifndef QLZ_HEADER +#define QLZ_HEADER + +// Fast data compression library +// Copyright (C) 2006-2011 Lasse Mikkel Reinhold +// lar@quicklz.com +// +// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything +// released into public must be open source) or under a commercial license if such +// has been acquired (see http://www.quicklz.com/order.html). The commercial license +// does not cover derived or ported versions created by third parties under GPL. + +// You can edit following user settings. Data must be decompressed with the same +// setting of QLZ_COMPRESSION_LEVEL and QLZ_STREAMING_BUFFER as it was compressed +// (see manual). If QLZ_STREAMING_BUFFER > 0, scratch buffers must be initially +// zeroed out (see manual). First #ifndef makes it possible to define settings from +// the outside like the compiler command line. + +// 1.5.0 final + +#ifndef QLZ_COMPRESSION_LEVEL + + // 1 gives fastest compression speed. 3 gives fastest decompression speed and best + // compression ratio. + //#define QLZ_COMPRESSION_LEVEL 1 + //#define QLZ_COMPRESSION_LEVEL 2 + #define QLZ_COMPRESSION_LEVEL 3 + + // If > 0, zero out both states prior to first call to qlz_compress() or qlz_decompress() + // and decompress packets in the same order as they were compressed + #define QLZ_STREAMING_BUFFER 0 + //#define QLZ_STREAMING_BUFFER 1000 + //#define QLZ_STREAMING_BUFFER 100000 + //#define QLZ_STREAMING_BUFFER 1000000 + + // Guarantees that decompression of corrupted data cannot crash. Decreases decompression + // speed 10-20%. Compression speed not affected. + //#define QLZ_MEMORY_SAFE +#endif + +#define QLZ_VERSION_MAJOR 1 +#define QLZ_VERSION_MINOR 5 +#define QLZ_VERSION_REVISION 0 + +// Using size_t, memset() and memcpy() +#include + +// Verify compression level +#if QLZ_COMPRESSION_LEVEL != 1 && QLZ_COMPRESSION_LEVEL != 2 && QLZ_COMPRESSION_LEVEL != 3 +#error QLZ_COMPRESSION_LEVEL must be 1, 2 or 3 +#endif + +typedef unsigned int ui32; +typedef unsigned short int ui16; + +// Decrease QLZ_POINTERS for level 3 to increase compression speed. Do not touch any other values! +#if QLZ_COMPRESSION_LEVEL == 1 +#define QLZ_POINTERS 1 +#define QLZ_HASH_VALUES 4096 +#elif QLZ_COMPRESSION_LEVEL == 2 +#define QLZ_POINTERS 4 +#define QLZ_HASH_VALUES 2048 +#elif QLZ_COMPRESSION_LEVEL == 3 +#define QLZ_POINTERS 16 +#define QLZ_HASH_VALUES 4096 +#endif + +// Detect if pointer size is 64-bit. It's not fatal if some 64-bit target is not detected because this is only for adding an optional 64-bit optimization. +#if defined _LP64 || defined __LP64__ || defined __64BIT__ || _ADDR64 || defined _WIN64 || defined __arch64__ || __WORDSIZE == 64 || (defined __sparc && defined __sparcv9) || defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 || defined __ia64 || defined __IA64__ + #define QLZ_PTR_64 +#endif + +// hash entry +typedef struct +{ +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 cache; +#if defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 + unsigned int offset; +#else + const unsigned char *offset; +#endif +#else + const unsigned char *offset[QLZ_POINTERS]; +#endif + +} qlz_hash_compress; + +typedef struct +{ +#if QLZ_COMPRESSION_LEVEL == 1 + const unsigned char *offset; +#else + const unsigned char *offset[QLZ_POINTERS]; +#endif +} qlz_hash_decompress; + + +// states +typedef struct +{ + #if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; + #endif + size_t stream_counter; + qlz_hash_compress hash[QLZ_HASH_VALUES]; + unsigned char hash_counter[QLZ_HASH_VALUES]; +} qlz_state_compress; + + +#if QLZ_COMPRESSION_LEVEL == 1 || QLZ_COMPRESSION_LEVEL == 2 + typedef struct + { +#if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; +#endif + qlz_hash_decompress hash[QLZ_HASH_VALUES]; + unsigned char hash_counter[QLZ_HASH_VALUES]; + size_t stream_counter; + } qlz_state_decompress; +#elif QLZ_COMPRESSION_LEVEL == 3 + typedef struct + { +#if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; +#endif +#if QLZ_COMPRESSION_LEVEL <= 2 + qlz_hash_decompress hash[QLZ_HASH_VALUES]; +#endif + size_t stream_counter; + } qlz_state_decompress; +#endif + + +#if defined (__cplusplus) +extern "C" { +#endif + +// Public functions of QuickLZ +size_t qlz_size_decompressed(const char *source); +size_t qlz_size_compressed(const char *source); +size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state); +size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state); +int qlz_get_setting(int setting); + +#if defined (__cplusplus) +} +#endif + +#endif + diff --git a/source/text.c b/source/text.c index 204d901..2fe5642 100644 --- a/source/text.c +++ b/source/text.c @@ -32,20 +32,46 @@ int drawCharacter(u8 *fb, font_s *f, char c, s16 x, s16 y, u16 w, u16 h) { } else if (y + ch > h)ch = h - y; fb += (x * h + cy) * 3; - const u8 r = f->color[0], g = f->color[1], b = f->color[2]; - for (i = 0; i < cd->w; i++) { - charData += cyo; - for (j = 0; j < ch; j++) { - u8 v = *(charData++); - if (v) { - fb[0] = (fb[0] * (0xFF - v) + (b * v)) >> 8; - fb[1] = (fb[1] * (0xFF - v) + (g * v)) >> 8; - fb[2] = (fb[2] * (0xFF - v) + (r * v)) >> 8; + const u8 r = f->color[0], g = f->color[1], b = f->color[2], a = f->color[3]; + if ( a == 0xFF ) + { + for (i = 0; i < cd->w; i++) { + charData += cyo; + for (j = 0; j < ch; j++) { + u8 v = *(charData++); + if (v) { + fb[0] = (fb[0] * (0xFF - v) + (b * v)) >> 8; + fb[1] = (fb[1] * (0xFF - v) + (g * v)) >> 8; + fb[2] = (fb[2] * (0xFF - v) + (r * v)) >> 8; + } + fb += 3; } - fb += 3; + charData += (cd->h - (cyo + ch)); + fb += (h - ch) * 3; + } + } + else if ( a > 0 ) + { + int alpha = a; + int one_minus_alpha = 255-alpha; + + for (i = 0; i < cd->w; i++) { + charData += cyo; + for (j = 0; j < ch; j++) { + u8 v = *(charData++); + if (v) { + u8 blend[3] = { (fb[0] * (0xFF - v) + (b * v)) >> 8, + (fb[1] * (0xFF - v) + (g * v)) >> 8, + (fb[2] * (0xFF - v) + (r * v)) >> 8 }; + fb[0] = (u8)((alpha*blend[0]+one_minus_alpha*fb[0])/255); + fb[1] = (u8)((alpha*blend[1]+one_minus_alpha*fb[1])/255); + fb[2] = (u8)((alpha*blend[2]+one_minus_alpha*fb[2])/255); + } + fb += 3; + } + charData += (cd->h - (cyo + ch)); + fb += (h - ch) * 3; } - charData += (cd->h - (cyo + ch)); - fb += (h - ch) * 3; } return cd->xa; } diff --git a/source/utility.c b/source/utility.c index ac07dac..c9e8a98 100644 --- a/source/utility.c +++ b/source/utility.c @@ -8,33 +8,173 @@ #include "arm9/source/hid.h" #include "arm9/source/i2c.h" #include "arm9/source/fatfs/ff.h" +#include "memory.h" #else + #include <3ds.h> #include +#include + #endif -#include "gfx.h" +#include "draw.h" #include "menu.h" #include "utility.h" -#include "memory.h" #ifdef ARM9 void waitcycles(uint32_t val); +#define SD_CARD_NAME "0:" +#define SD_CARD_PATH "sdmc:" +#define SD_CARD_PATH_SIZE 5 +#define SYSNAND_NAME "1:" +#define SYSNAND_PATH "nand:" +#define SYSNAND_PATH_SIZE 5 + +typedef struct +{ + char* driveNames[MANAGED_DRIVES_COUNT]; + char* drivePathes[MANAGED_DRIVES_COUNT]; + int drivePathSizes[MANAGED_DRIVES_COUNT]; + bool driveWritable[MANAGED_DRIVES_COUNT]; + FATFS driveFS[MANAGED_DRIVES_COUNT]; + int mountedDrives[MANAGED_DRIVES_COUNT]; + int curDrive; + int defaultDrive; +} file_systems_s; + +file_systems_s *fsConfig; + +void initFileSystems(bool isLoadedFromSD) +{ + fsConfig = (file_systems_s*)PTR_FILE_SYSTEMS; + + fsConfig->driveNames[SD_CARD_ID] = SD_CARD_NAME; + fsConfig->driveNames[SYSNAND_ID] = SYSNAND_NAME; + + fsConfig->drivePathes[SD_CARD_ID] = SD_CARD_PATH; + fsConfig->drivePathes[SYSNAND_ID] = SYSNAND_PATH; + + fsConfig->drivePathSizes[SD_CARD_ID] = SD_CARD_PATH_SIZE; + fsConfig->drivePathSizes[SYSNAND_ID] = SYSNAND_PATH_SIZE; + + fsConfig->driveWritable[SD_CARD_ID] = true; + fsConfig->driveWritable[SYSNAND_ID] = false; + + fsConfig->mountedDrives[SD_CARD_ID] = -1; + fsConfig->mountedDrives[SYSNAND_ID] = -1; + + fsConfig->defaultDrive = (isLoadedFromSD ? SD_CARD_ID : SYSNAND_ID); + fsConfig->curDrive = fsConfig->defaultDrive; + fsConfig->mountedDrives[fsConfig->curDrive] = + (f_mount(&fsConfig->driveFS[fsConfig->curDrive], fsConfig->driveNames[fsConfig->curDrive], 0) == FR_OK + && f_chdrive(fsConfig->driveNames[fsConfig->curDrive]) == FR_OK) ? 1 : 0; +} + +const char* prepareFileSystemForPath(const char* path) +{ + if (NULL == path) + return NULL; + + const char* relatPath = NULL; + if (path[0] == '/') + { + if (fsConfig->defaultDrive != fsConfig->curDrive) + { + // We assume default drive is always mounted. + fsConfig->curDrive = fsConfig->defaultDrive; + f_chdrive(fsConfig->driveNames[fsConfig->curDrive]); + } + relatPath = path; + } + else + { + int driveSize = 0; + while (path[driveSize] != '\0' && path[driveSize] != '/') + driveSize++; + if (path[driveSize] == '\0') + return NULL; + + for (int selDrive = 0 ; selDrive < MANAGED_DRIVES_COUNT ; selDrive++) + { + if (driveSize == fsConfig->drivePathSizes[selDrive] && memcmp(path, fsConfig->drivePathes[selDrive], driveSize) == 0) + { + relatPath = &path[driveSize]; + fsConfig->curDrive = selDrive; + if (fsConfig->mountedDrives[selDrive] < 0) + { + fsConfig->mountedDrives[selDrive] = + (f_mount(&fsConfig->driveFS[selDrive], fsConfig->driveNames[selDrive], 0) == FR_OK) ? 1 : 0; + } + f_chdrive(fsConfig->driveNames[selDrive]); + break; + } + } + } + return (1 == fsConfig->mountedDrives[fsConfig->curDrive]) ? relatPath : NULL; +} + +bool isDefaultDriveReadOnly() +{ + return !fsConfig->driveWritable[fsConfig->defaultDrive]; +} + +bool switchCurrentDrive(unsigned int driveID) +{ + if (driveID >= MANAGED_DRIVES_COUNT) + return false; + if (fsConfig->curDrive == driveID) + return true; + + if (fsConfig->mountedDrives[driveID] < 0) + { + fsConfig->mountedDrives[driveID] = + (f_mount(&fsConfig->driveFS[driveID], fsConfig->driveNames[driveID], 0) == FR_OK) ? 1 : 0; + } + if (fsConfig->mountedDrives[fsConfig->curDrive] < 1) + return false; + + fsConfig->curDrive = driveID; + f_chdrive(fsConfig->driveNames[driveID]); + return true; +} + +void setCurrentDriveAsDefault() +{ + switchCurrentDrive(fsConfig->defaultDrive); +} + +int getDefaultDrive() +{ + return fsConfig->defaultDrive; +} + +char* computeFullPath(const char* relative, char* absolute) +{ + if (NULL == relative || NULL == absolute) + return NULL; + if (relative[0] == '/') + sprintf(absolute, "%s%s", fsConfig->drivePathes[fsConfig->curDrive], relative); + else + sprintf(absolute, "%s", relative); + return absolute; +} + #else + FS_Archive sdmcArchive; extern void __appExit(); void openSDArchive() { - sdmcArchive = (FS_Archive) {0x00000009, (FS_Path) {PATH_EMPTY, 1, (u8 *) ""}}; - FSUSER_OpenArchive(&sdmcArchive); + FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); } void closeSDArchive() { - FSUSER_CloseArchive(&sdmcArchive); + FSUSER_CloseArchive(sdmcArchive); } + #endif void svcSleep(u32 millis) { @@ -115,9 +255,14 @@ void debug(const char *fmt, ...) { break; drawBg(); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, "Press (A) key to continue...", MENU_MIN_X + 16, MENU_MIN_Y + 64); - gfxSwap(); + drawText(GFX_TOP, GFX_LEFT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); + drawText(GFX_TOP, GFX_LEFT, &fontDefault, "Press (A) key to continue...", MENU_MIN_X + 16, MENU_MIN_Y + 64); + if ( IS3DACTIVE ) + { + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, "Press (A) key to continue...", MENU_MIN_X + 16, MENU_MIN_Y + 64); + } + swapFrameBuffers(); } } @@ -140,17 +285,25 @@ bool confirm(int confirmButton, const char *fmt, ...) { } drawBg(); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); - gfxDrawText(GFX_TOP, GFX_LEFT, &fontDefault, "Press (B) key to cancel...", MENU_MIN_X + 16, MENU_MIN_Y + 64); - gfxDrawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 84, "Press (%s) to confirm...", + drawText(GFX_TOP, GFX_LEFT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); + drawText(GFX_TOP, GFX_LEFT, &fontDefault, "Press (B) key to cancel...", MENU_MIN_X + 16, MENU_MIN_Y + 64); + drawTextf(GFX_TOP, GFX_LEFT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 84, "Press (%s) to confirm...", get_button(confirmButton)); - gfxSwap(); + if ( IS3DACTIVE ) + { + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, s, MENU_MIN_X + 16, MENU_MIN_Y + 16); + drawText(GFX_TOP, GFX_RIGHT, &fontDefault, "Press (B) key to cancel...", MENU_MIN_X + 16, MENU_MIN_Y + 64); + drawTextf(GFX_TOP, GFX_RIGHT, &fontDefault, MENU_MIN_X + 16, MENU_MIN_Y + 84, "Press (%s) to confirm...", + get_button(confirmButton)); + } + swapFrameBuffers(); } return false; } -bool fileExists(char *path) { +bool fileExists(const char *path) { #ifdef ARM9 + path = prepareFileSystemForPath(path); FIL file; if (f_open(&file, path, FA_READ) != FR_OK) { return false; @@ -175,6 +328,7 @@ bool fileExists(char *path) { size_t fileSize(const char *path) { size_t size = -1; #ifdef ARM9 + path = prepareFileSystemForPath(path); FIL file; if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) { return size; @@ -196,6 +350,7 @@ size_t fileSize(const char *path) { int fileReadOffset(const char *path, void *data, size_t size, u32 offset) { #ifdef ARM9 + path = prepareFileSystemForPath(path); FIL file; UINT bytes_read = 0; if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) { @@ -228,6 +383,83 @@ int fileRead(const char *path, void *data, size_t size) { return fileReadOffset(path, data, size, 0); } +int fileWrite(const char *path, void *data, size_t size) { +#ifdef ARM9 + path = prepareFileSystemForPath(path); + if (!fsConfig->driveWritable[fsConfig->curDrive]) + return -1; + + FIL file; + unsigned int br = 0; + f_unlink(path); + if(f_open(&file, path, FA_WRITE | FA_CREATE_ALWAYS) != FR_OK) { + return -1; + } + f_write(&file, data, size, &br); + f_close(&file); +#else + unlink(path); + FILE *file = fopen(path, "w"); + fwrite(data, 1 , size, file); + fclose(file); +#endif + return size; +} + +int getFileHandleSize() +{ +#ifdef ARM9 + return sizeof(FIL); +#else + return sizeof(FILE*); +#endif +} + +int fileHandleOpen(void* handlePtr, const char *filePath) +{ +#ifdef ARM9 + filePath = prepareFileSystemForPath(filePath); + return (f_open((FIL*)handlePtr, filePath, FA_READ | FA_OPEN_EXISTING) == FR_OK) ? 0 : -1; +#else + *(FILE**)handlePtr = fopen(filePath, "rb"); + return ( NULL != *(FILE**)handlePtr ) ? 0 : -1; +#endif +} + +size_t fileHandleSize(void* handlePtr) +{ +#ifdef ARM9 + return f_size((FIL*)handlePtr); +#else + fseek(*(FILE**)handlePtr, 0, SEEK_END); + return ftell(*(FILE**)handlePtr); +#endif +} + +int fileHandleRead(void* handlePtr, void *data, size_t size, u32 offset) +{ +#ifdef ARM9 + UINT bytes_read = 0; + f_lseek((FIL*)handlePtr, offset); + if (f_read((FIL*)handlePtr, data, size, &bytes_read) != FR_OK) + return -1; + return ( bytes_read > 0 ) ? bytes_read : -1; +#else + fseek(*(FILE**)handlePtr, offset, SEEK_SET); + u32 bytes_read = fread(data, 1, size, *(FILE**)handlePtr); + return ( bytes_read > 0 ) ? bytes_read : -1; +#endif +} + +int fileHandleClose(void* handlePtr) +{ +#ifdef ARM9 + return f_close((FIL*)handlePtr); +#else + return fclose(*(FILE**)handlePtr); +#endif +} + int load_homemenu() { #ifdef ARM9 debug("load_homemenu not implemented"); @@ -252,9 +484,9 @@ void reboot() { while (true); #else aptInit(); - aptOpenSession(); + //aptOpenSession(); APT_HardwareResetAsync(); - aptCloseSession(); + //aptCloseSession(); aptExit(); #endif } @@ -295,4 +527,11 @@ bool aptMainLoop() { return true; } +void* memAlloc(unsigned int iSize) { + static u8* nextFreeMem = PTR_DYNAMIC_MEM_START; + void* retPtr = (void*)nextFreeMem; + nextFreeMem += iSize; + return retPtr; +} + #endif diff --git a/source/utility.h b/source/utility.h index 9920713..0ecce38 100644 --- a/source/utility.h +++ b/source/utility.h @@ -7,9 +7,30 @@ extern "C" { #ifdef ARM9 +#define MANAGED_DRIVES_COUNT 2 +#define SD_CARD_ID 0 +#define SYSNAND_ID 1 + +void initFileSystems(bool isLoadedFromSD); + +bool isDefaultDriveReadOnly(); + +bool switchCurrentDrive(unsigned int driveID); + +void setCurrentDriveAsDefault(); + +int getDefaultDrive(); + +char* computeFullPath(const char* relative, char* absolute); + bool aptMainLoop(); +void* memAlloc(unsigned int iSize); + +#define START_DRIVE_RO (isDefaultDriveReadOnly()) + #else + #include "descriptor.h" typedef struct menuEntry_s { @@ -21,9 +42,12 @@ typedef struct menuEntry_s { void openSDArchive(); void closeSDArchive(); + +#define START_DRIVE_RO false + #endif -bool fileExists(char *path); +bool fileExists(const char *path); size_t fileSize(const char *path); @@ -31,6 +55,18 @@ int fileReadOffset(const char *path, void *data, size_t size, u32 offset); int fileRead(const char *path, void *data, size_t size); +int fileWrite(const char *path, void *data, size_t size); + +int getFileHandleSize(); + +int fileHandleOpen(void* handlePtr, const char *filePath); + +size_t fileHandleSize(void* handlePtr); + +int fileHandleRead(void* handlePtr, void *data, size_t size, u32 offset); + +int fileHandleClose(void* handlePtr); + void svcSleep(u32 millis); const char *get_filename_ext(const char *filename);