diff --git a/Makefile b/Makefile index a92ace7..5f71be7 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,11 @@ VERSION_MICRO := 0 APP_TITLE := nxdumptool APP_AUTHOR := DarkMatterCore -APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} +APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} + +ifneq ($(origin BUILD_TYPE),undefined) +APP_TITLE := ${BUILD_TYPE} +endif TARGET := ${APP_TITLE} BUILD := build @@ -65,24 +69,24 @@ BOREALIS_RESOURCES := romfs:/ #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE -CFLAGS := -g -Wall -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__ -CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO} -CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\" -CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" -CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\"" -CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags` -CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags` -CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags` -CFLAGS += `aarch64-none-elf-pkg-config libturbojpeg --cflags` +CFLAGS := -g -Wall -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__ +CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO} +CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\" +CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" +CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\"" +CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags` +CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags` +CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags` +CFLAGS += `aarch64-none-elf-pkg-config libturbojpeg --cflags` -CXXFLAGS := $(CFLAGS) -std=c++1z -O2 -Wno-volatile -Wno-unused-parameter +CXXFLAGS := $(CFLAGS) -std=c++1z -O2 -Wno-volatile -Wno-unused-parameter -ASFLAGS := -g $(ARCH) -LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) +ASFLAGS := -g $(ARCH) +LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfs -lntfs-3g -llwext4 -lnx -ljson-c -lturbojpeg +LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfs -lntfs-3g -llwext4 -lnx -ljson-c -lturbojpeg #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/build.sh b/build.sh index 7f67f60..19e0c46 100644 --- a/build.sh +++ b/build.sh @@ -25,12 +25,17 @@ for f in ./code_templates/*.c; do rm -f ./source/main.c cp $f ./source/main.c - make clean - make -j$(nproc) + cp ./romfs/icon/nxdumptool.jpg ./romfs/icon/$filename.jpg + + make BUILD_TYPE="$filename" -j$(nproc) + + rm -f ./romfs/icon/$filename.jpg mkdir ./code_templates/tmp/$filename - cp ./nxdumptool.nro ./code_templates/tmp/$filename/nxdumptool.nro - #cp ./nxdumptool.elf ./code_templates/tmp/$filename/nxdumptool.elf + cp ./$filename.nro ./code_templates/tmp/$filename/$filename.nro + #cp ./$filename.elf ./code_templates/tmp/$filename/$filename.elf + + make BUILD_TYPE="$filename" clean done make clean_all diff --git a/code_templates/nsp_dumper_stor.c b/code_templates/nsp_dumper_stor.c index e5d9391..1e1fce0 100644 --- a/code_templates/nsp_dumper_stor.c +++ b/code_templates/nsp_dumper_stor.c @@ -32,10 +32,6 @@ #define BLOCK_SIZE 0x800000 #define OUTPATH "/nsp/" -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; static const char *dump_type_strings[] = { @@ -783,12 +779,9 @@ end: int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/code_templates/nsp_dumper_usb.c b/code_templates/nsp_dumper_usb.c index e209def..8159551 100644 --- a/code_templates/nsp_dumper_usb.c +++ b/code_templates/nsp_dumper_usb.c @@ -31,10 +31,6 @@ #define BLOCK_SIZE 0x800000 -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; typedef struct @@ -941,12 +937,9 @@ static void nspDump(TitleInfo *title_info) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/code_templates/sd_romfs_dumper.c b/code_templates/sd_romfs_dumper.c index 467f833..dd82880 100644 --- a/code_templates/sd_romfs_dumper.c +++ b/code_templates/sd_romfs_dumper.c @@ -26,10 +26,6 @@ #define BLOCK_SIZE 0x800000 -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; static Mutex g_fileMutex = 0; @@ -358,12 +354,9 @@ u8 get_program_id_offset(TitleInfo *info, u32 program_count) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/code_templates/system_title_dumper.c b/code_templates/system_title_dumper.c index 95d499a..37d356c 100644 --- a/code_templates/system_title_dumper.c +++ b/code_templates/system_title_dumper.c @@ -28,10 +28,6 @@ #define BLOCK_SIZE 0x800000 #define OUTPATH "sdmc:/systitle_dumps" -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; static u8 *buf = NULL; @@ -255,12 +251,9 @@ static void dumpFsSection(TitleInfo *info, NcaFsSectionContext *nca_fs_ctx) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/code_templates/usb_gc_dumper.c b/code_templates/usb_gc_dumper.c index 005b5d9..ea328a4 100644 --- a/code_templates/usb_gc_dumper.c +++ b/code_templates/usb_gc_dumper.c @@ -27,10 +27,6 @@ #define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; /* Type definitions. */ @@ -153,7 +149,7 @@ static Menu g_xciMenu = { static MenuElement *g_rootMenuElements[] = { &(MenuElement){ - .str = "dump key area", + .str = "dump key area (initial data)", .child_menu = NULL, .task_func = &sendGameCardKeyAreaViaUsb, .element_options = NULL @@ -215,15 +211,12 @@ static void utilsWaitForButtonPress(u64 flag) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; Menu *cur_menu = &g_rootMenu; u32 element_count = menuGetElementCount(cur_menu), page_size = 30; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; @@ -453,10 +446,10 @@ static bool sendGameCardKeyAreaViaUsb(void) if (!dumpGameCardKeyArea(&gc_key_area) || !filename) goto end; - crc32FastCalculate(&gc_key_area, sizeof(GameCardKeyArea), &crc); - snprintf(path, MAX_ELEMENTS(path), "%s (Key Area) (%08X).bin", filename, crc); + crc32FastCalculate(&(gc_key_area.initial_data), sizeof(GameCardInitialData), &crc); + snprintf(path, MAX_ELEMENTS(path), "%s (Initial Data) (%08X).bin", filename, crc); - if (!sendFileData(path, &gc_key_area, sizeof(GameCardKeyArea))) goto end; + if (!sendFileData(path, &(gc_key_area.initial_data), sizeof(GameCardInitialData))) goto end; printf("successfully sent key area as \"%s\"\n", path); success = true; diff --git a/code_templates/usb_romfs_dumper.c b/code_templates/usb_romfs_dumper.c index 91db974..cbd3800 100644 --- a/code_templates/usb_romfs_dumper.c +++ b/code_templates/usb_romfs_dumper.c @@ -27,10 +27,6 @@ #define BLOCK_SIZE USB_TRANSFER_BUFFER_SIZE -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; static Mutex g_fileMutex = 0; @@ -337,12 +333,9 @@ u8 get_program_id_offset(TitleInfo *info, u32 program_count) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/code_templates/xml_generator.c b/code_templates/xml_generator.c index c77b08b..01d0ac9 100644 --- a/code_templates/xml_generator.c +++ b/code_templates/xml_generator.c @@ -27,10 +27,6 @@ #include "nacp.h" #include "legal_info.h" -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - static PadState g_padState = {0}; static void utilsScanPads(void) @@ -83,12 +79,9 @@ static void writeFile(void *buf, size_t buf_size, const char *path) int main(int argc, char *argv[]) { - g_argc = argc; - g_argv = argv; - int ret = 0; - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { ret = -1; goto out; diff --git a/include/core/common.h b/include/core/common.h index e212732..da306f0 100644 --- a/include/core/common.h +++ b/include/core/common.h @@ -93,11 +93,4 @@ typedef struct { NXDT_ASSERT(VersionType2, 0x4); -/// These are set in main(). -extern int g_argc; -extern char **g_argv; - -/// This is set in utilsInitializeResources(). -extern const char *g_appLaunchPath; - #endif /* __COMMON_H__ */ diff --git a/include/core/lz4.h b/include/core/lz4.h index 32108e2..7ab1e48 100644 --- a/include/core/lz4.h +++ b/include/core/lz4.h @@ -100,7 +100,7 @@ extern "C" { /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 2 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) @@ -186,7 +186,8 @@ LZ4LIB_API int LZ4_compressBound(int inputSize); The larger the acceleration value, the faster the algorithm, but also the lesser the compression. It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). */ LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); @@ -212,7 +213,18 @@ LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* d * New value is necessarily <= input value. * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) * or 0 if compression fails. -*/ + * + * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): + * the produced compressed content could, in specific circumstances, + * require to be decompressed into a destination buffer larger + * by at least 1 byte than the content to decompress. + * If an application uses `LZ4_compress_destSize()`, + * it's highly recommended to update liblz4 to v1.9.2 or better. + * If this can't be done or ensured, + * the receiving decompression function should provide + * a dstCapacity which is > decompressedSize, by at least 1 byte. + * See https://github.com/lz4/lz4/issues/859 for details + */ LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); @@ -220,25 +232,35 @@ LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePt * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', * into destination buffer 'dst' of size 'dstCapacity'. * Up to 'targetOutputSize' bytes will be decoded. - * The function stops decoding on reaching this objective, - * which can boost performance when only the beginning of a block is required. + * The function stops decoding on reaching this objective. + * This can be useful to boost performance + * whenever only the beginning of a block is required. * - * @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity) + * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) * If source stream is detected malformed, function returns a negative result. * - * Note : @return can be < targetOutputSize, if compressed block contains less data. + * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. * - * Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity, - * and expects targetOutputSize <= dstCapacity. - * It effectively stops decoding on reaching targetOutputSize, + * Note 2 : targetOutputSize must be <= dstCapacity + * + * Note 3 : this function effectively stops decoding on reaching targetOutputSize, * so dstCapacity is kind of redundant. - * This is because in a previous version of this function, - * decoding operation would not "break" a sequence in the middle. - * As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize, + * This is because in older versions of this function, + * decoding operation would still write complete sequences. + * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, * it could write more bytes, though only up to dstCapacity. * Some "margin" used to be required for this operation to work properly. - * This is no longer necessary. - * The function nonetheless keeps its signature, in an effort to not break API. + * Thankfully, this is no longer necessary. + * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. + * + * Note 4 : If srcSize is the exact size of the block, + * then targetOutputSize can be any value, + * including larger than the block's decompressed size. + * The function will, at most, generate block's decompressed size. + * + * Note 5 : If srcSize is _larger_ than block's compressed size, + * then targetOutputSize **MUST** be <= block's decompressed size. + * Otherwise, *silent corruption will occur*. */ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); @@ -547,74 +569,64 @@ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const #define LZ4_H_98237428734687 /*-************************************************************ - * PRIVATE DEFINITIONS + * Private Definitions ************************************************************** * Do not use these definitions directly. * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Accessing members will expose code to API and/or ABI break in future versions of the library. + * Accessing members will expose user code to API and/or ABI break in future versions of the library. **************************************************************/ #define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) #define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) #define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -#include - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - uint32_t hashTable[LZ4_HASH_SIZE_U32]; - uint32_t currentOffset; - uint16_t dirty; - uint16_t tableType; - const uint8_t* dictionary; - const LZ4_stream_t_internal* dictCtx; - uint32_t dictSize; -}; - -typedef struct { - const uint8_t* externalDict; - size_t extDictSize; - const uint8_t* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - +# include + typedef int8_t LZ4_i8; + typedef uint8_t LZ4_byte; + typedef uint16_t LZ4_u16; + typedef uint32_t LZ4_u32; #else - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - unsigned int hashTable[LZ4_HASH_SIZE_U32]; - unsigned int currentOffset; - unsigned short dirty; - unsigned short tableType; - const unsigned char* dictionary; - const LZ4_stream_t_internal* dictCtx; - unsigned int dictSize; -}; - -typedef struct { - const unsigned char* externalDict; - const unsigned char* prefixEnd; - size_t extDictSize; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - + typedef signed char LZ4_i8; + typedef unsigned char LZ4_byte; + typedef unsigned short LZ4_u16; + typedef unsigned int LZ4_u32; #endif +typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; +struct LZ4_stream_t_internal { + LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; + LZ4_u32 currentOffset; + LZ4_u32 tableType; + const LZ4_byte* dictionary; + const LZ4_stream_t_internal* dictCtx; + LZ4_u32 dictSize; +}; + +typedef struct { + const LZ4_byte* externalDict; + size_t extDictSize; + const LZ4_byte* prefixEnd; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + + /*! LZ4_stream_t : - * information structure to track an LZ4 stream. + * Do not use below internal definitions directly ! + * Declare or allocate an LZ4_stream_t instead. * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. * The structure definition can be convenient for static allocation * (on stack, or as part of larger structure). * Init this structure with LZ4_initStream() before first use. * note : only use this definition in association with static linking ! - * this definition is not API/ABI safe, and may change in a future version. + * this definition is not API/ABI safe, and may change in future versions. */ -#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ ) -#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) +#define LZ4_STREAMSIZE 16416 /* static size, for inter-version compatibility */ +#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*)) union LZ4_stream_u { - unsigned long long table[LZ4_STREAMSIZE_U64]; + void* table[LZ4_STREAMSIZE_VOIDP]; LZ4_stream_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_stream_t */ +}; /* previously typedef'd to LZ4_stream_t */ + /*! LZ4_initStream() : v1.9.0+ * An LZ4_stream_t structure must be initialized at least once. @@ -667,22 +679,21 @@ union LZ4_streamDecode_u { #ifdef LZ4_DISABLE_DEPRECATE_WARNINGS # define LZ4_DEPRECATED(message) /* disable deprecation warnings */ #else -# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ # define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif (LZ4_GCC_VERSION >= 301) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) # elif defined(_MSC_VER) # define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) # else -# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") -# define LZ4_DEPRECATED(message) +# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") +# define LZ4_DEPRECATED(message) /* disabled */ # endif #endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ -/* Obsolete compression functions */ +/*! Obsolete compression functions (since v1.7.3) */ LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); @@ -690,11 +701,12 @@ LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_co LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); -/* Obsolete decompression functions */ +/*! Obsolete decompression functions (since v1.8.0) */ LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); -/* Obsolete streaming functions; degraded functionality; do not use! +/* Obsolete streaming functions (since v1.7.0) + * degraded functionality; do not use! * * In order to perform streaming compression, these functions depended on data * that is no longer tracked in the state. They have been preserved as well as @@ -708,23 +720,22 @@ LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStre LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); -/* Obsolete streaming decoding functions */ +/*! Obsolete streaming decoding functions (since v1.7.0) */ LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); -/*! LZ4_decompress_fast() : **unsafe!** +/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : * These functions used to be faster than LZ4_decompress_safe(), - * but it has changed, and they are now slower than LZ4_decompress_safe(). + * but this is no longer the case. They are now slower. * This is because LZ4_decompress_fast() doesn't know the input size, - * and therefore must progress more cautiously in the input buffer to not read beyond the end of block. + * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. * * The last remaining LZ4_decompress_fast() specificity is that * it can decompress a block without knowing its compressed size. - * Such functionality could be achieved in a more secure manner, - * by also providing the maximum size of input buffer, - * but it would require new prototypes, and adaptation of the implementation to this new use case. + * Such functionality can be achieved in a more secure manner + * by employing LZ4_decompress_safe_partial(). * * Parameters: * originalSize : is the uncompressed size to regenerate. @@ -739,7 +750,6 @@ LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4 * But they may happen if input data is invalid (error or intentional tampering). * As a consequence, use these functions in trusted environments with trusted data **only**. */ - LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") diff --git a/include/core/nca.h b/include/core/nca.h index 815b372..7a54ec1 100644 --- a/include/core/nca.h +++ b/include/core/nca.h @@ -91,11 +91,19 @@ typedef enum { NcaKeyGeneration_700_801 = 8, NcaKeyGeneration_810_811 = 9, NcaKeyGeneration_900_901 = 10, - NcaKeyGeneration_910_1201 = 11, - NcaKeyGeneration_Current = NcaKeyGeneration_910_1201, + NcaKeyGeneration_910_1202 = 11, + NcaKeyGeneration_Current = NcaKeyGeneration_910_1202, NcaKeyGeneration_Max = 32 } NcaKeyGeneration; +/// 'NcaMainSignatureKeyGeneration_Current' will always point to the last known key generation value. +typedef enum { + NcaMainSignatureKeyGeneration_100_811 = 0, + NcaMainSignatureKeyGeneration_900_1202 = 1, + NcaMainSignatureKeyGeneration_Current = NcaMainSignatureKeyGeneration_900_1202, + NcaMainSignatureKeyGeneration_Max = (NcaMainSignatureKeyGeneration_Current + 1) +} NcaMainSignatureKeyGeneration; + typedef struct { u32 start_sector; ///< Expressed in NCA_FS_SECTOR_SIZE sectors. u32 end_sector; ///< Expressed in NCA_FS_SECTOR_SIZE sectors. @@ -139,7 +147,7 @@ typedef struct { u32 content_index; VersionType2 sdk_addon_version; u8 key_generation; ///< NcaKeyGeneration. - u8 main_signature_key_generation; + u8 main_signature_key_generation; ///< NcaMainSignatureKeyGeneration. u8 reserved[0xE]; FsRightsId rights_id; ///< Used for titlekey crypto. NcaFsInfo fs_info[NCA_FS_HEADER_COUNT]; ///< Start and end sectors for each NCA FS section. diff --git a/include/core/npdm.h b/include/core/npdm.h index 203c260..71a5a3e 100644 --- a/include/core/npdm.h +++ b/include/core/npdm.h @@ -437,7 +437,8 @@ typedef enum { NpdmSystemCallId_CreateResourceLimit = BIT(5), NpdmSystemCallId_SetResourceLimitLimitValue = BIT(6), NpdmSystemCallId_CallSecureMonitor = BIT(7), - NpdmSystemCallId_Count = 0x80 ///< Total values supported by this enum. + + NpdmSystemCallId_Count = 0xC0 ///< Total values supported by this enum. } NpdmSystemCallId; /// EnableSystemCalls entry for the KernelCapability descriptor. diff --git a/include/core/nso.h b/include/core/nso.h index 31fb3e6..cfcaffb 100644 --- a/include/core/nso.h +++ b/include/core/nso.h @@ -85,7 +85,7 @@ typedef struct { NXDT_ASSERT(NsoHeader, 0x100); -/// Usually placed right after NsoHeader, but it's actual offset may vary. +/// Usually placed right after NsoHeader, but its actual offset may vary. /// If the 'module_name_size' member from NsoHeader is greater than 1 and the 'name_length' element from NsoModuleName is greater than 0, 'name' will hold the module name. typedef struct { u8 name_length; diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index a625265..ab525bc 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -32,6 +32,8 @@ extern "C" { #define APP_BASE_PATH "sdmc:/switch/" APP_TITLE "/" +#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:" + #define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member) #define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0]))) @@ -44,7 +46,14 @@ extern "C" { #define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) -#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:" +#define SCOPED_LOCK(mtx) for(UtilsScopedLock scoped_lock __attribute__((__cleanup__(utilsUnlockScope))) = utilsLockScope(mtx); scoped_lock.cond; scoped_lock.cond = 0) + +/// Used by scoped locks. +typedef struct { + Mutex *mtx; + bool lock; + int cond; +} UtilsScopedLock; /// Used to determine which CFW is the application running under. typedef enum { @@ -54,18 +63,46 @@ typedef enum { UtilsCustomFirmwareType_ReiNX = 3 } UtilsCustomFirmwareType; -/// Resource (de)initialization. -/// Called at program startup and exit. -bool utilsInitializeResources(void); +/// Resource initialization. +/// Called at program startup. +bool utilsInitializeResources(const int program_argc, const char **program_argv); + +/// Resource deinitialization. +/// Called at program exit. void utilsCloseResources(void); +/// Returns a pointer to the application launch path. +const char *utilsGetLaunchPath(void); + +/// Returns a pointer to the FsFileSystem object for the SD card. +FsFileSystem *utilsGetSdCardFileSystemObject(void); + +/// Commits SD card filesystem changes. +/// Must be used after closing a file handle from the SD card. +bool utilsCommitSdCardFileSystemChanges(void); + +/// Returns a UtilsCustomFirmwareType value. +u8 utilsGetCustomFirmwareType(void); + +/// Returns true if the application is running under a development unit. +bool utilsIsDevelopmentUnit(void); + +/// Returns true if the application is running under applet mode. +bool utilsAppletModeCheck(void); + +/// Returns a pointer to the FsStorage object for the eMMC BIS System partition. +FsStorage *utilsGetEmmcBisSystemPartitionStorage(void); + +/// Enables/disables CPU/MEM overclocking. +void utilsOverclockSystem(bool overclock); + +/// (Un)blocks HOME button presses. +void utilsChangeHomeButtonBlockStatus(bool block); + /// Thread management functions. bool utilsCreateThread(Thread *out_thread, ThreadFunc func, void *arg, int cpu_id); void utilsJoinThread(Thread *thread); -/// Returns true if the application is running under a development unit. -bool utilsIsDevelopmentUnit(void); - /// Formats a string and appends it to the provided buffer. /// If the buffer isn't big enough to hold both its current contents and the new formatted string, it will be resized. __attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...); @@ -89,13 +126,6 @@ void utilsGenerateFormattedSizeString(u64 size, char *dst, size_t dst_size); /// Returns false if there's an error. bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_free); -/// Returns a pointer to the FsFileSystem object for the SD card. -FsFileSystem *utilsGetSdCardFileSystemObject(void); - -/// Commits SD card filesystem changes. -/// Must be used after closing a file handle from the SD card. -bool utilsCommitSdCardFileSystemChanges(void); - /// Returns true if a file exists. bool utilsCheckIfFileExists(const char *path); @@ -112,27 +142,25 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element); /// Returns a pointer to a dynamically allocated string that holds the full path formed by the provided arguments. char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension); -/// Returns true if the application is running under Applet Mode. -bool utilsAppletModeCheck(void); - -/// (Un)blocks HOME button presses. -void utilsChangeHomeButtonBlockStatus(bool block); - -/// Returns a UtilsCustomFirmwareType value. -u8 utilsGetCustomFirmwareType(void); - -/// Returns a pointer to the FsStorage object for the eMMC BIS System partition. -FsStorage *utilsGetEmmcBisSystemPartitionStorage(void); - -/// Enables/disables CPU/MEM overclocking. -void utilsOverclockSystem(bool overclock); - /// Simple wrapper to sleep the current thread for a specific number of full seconds. NX_INLINE void utilsSleep(u64 seconds) { if (seconds) svcSleepThread(seconds * (u64)1000000000); } +/// Wrappers used in scoped locks. +NX_INLINE UtilsScopedLock utilsLockScope(Mutex *mtx) +{ + UtilsScopedLock scoped_lock = { mtx, !mutexIsLockedByCurrentThread(mtx), 1 }; + if (scoped_lock.lock) mutexLock(scoped_lock.mtx); + return scoped_lock; +} + +NX_INLINE void utilsUnlockScope(UtilsScopedLock *scoped_lock) +{ + if (scoped_lock->lock) mutexUnlock(scoped_lock->mtx); +} + #ifdef __cplusplus } #endif diff --git a/include/core/usb.h b/include/core/usb.h index 9884452..8288ef8 100644 --- a/include/core/usb.h +++ b/include/core/usb.h @@ -42,10 +42,10 @@ void usbExit(void); /// Returns a pointer to a dynamically allocated, page aligned memory buffer that's suitable for USB transfers. void *usbAllocatePageAlignedBuffer(size_t size); -/// Used to check if the console has been connected to an USB host device and if a valid USB session has been established. -/// Bear in mind this call will block the calling thread if the console is connected to an USB host device but no USB session has been established. +/// Used to check if the console has been connected to a USB host device and if a valid USB session has been established. +/// Bear in mind this call will block the calling thread if the console is connected to a USB host device but no USB session has been established. /// If the console is disconnected during this block, the function will return false. -/// If the console isn't connected to an USB host device when this function is called, false will be returned right away. +/// If the console isn't connected to a USB host device when this function is called, false will be returned right away. bool usbIsReady(void); /// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData(). diff --git a/include/fatfs/ff.h b/include/fatfs/ff.h index 5225041..4866576 100644 --- a/include/fatfs/ff.h +++ b/include/fatfs/ff.h @@ -1,8 +1,8 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem module R0.14 / +/ FatFs - Generic FAT Filesystem module R0.14b / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2021, 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 @@ -20,7 +20,7 @@ #ifndef FF_DEFINED -#define FF_DEFINED 86606 /* Revision ID */ +#define FF_DEFINED 86631 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -35,10 +35,14 @@ extern "C" { /* Integer types used for FatFs API */ -#if defined(_WIN32) /* Main development platform */ +#if defined(_WIN32) /* Windows VC++ (for development only) */ #define FF_INTDEF 2 #include typedef unsigned __int64 QWORD; +#include +#define isnan(v) _isnan(v) +#define isinf(v) (!_finite(v)) + #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ #define FF_INTDEF 2 #include @@ -48,6 +52,7 @@ typedef uint16_t WORD; /* 16-bit unsigned integer */ typedef uint32_t DWORD; /* 32-bit unsigned integer */ typedef uint64_t QWORD; /* 64-bit unsigned integer */ typedef WORD WCHAR; /* UTF-16 character type */ + #else /* Earlier than C99 */ #define FF_INTDEF 1 typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ @@ -58,28 +63,29 @@ typedef WORD WCHAR; /* UTF-16 character type */ #endif -/* Definitions of volume management */ +/* Type of file size and LBA variables */ -#if FF_MULTI_PARTITION /* Multiple partition configuration */ -typedef struct { - BYTE pd; /* Physical drive number */ - BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ -} PARTITION; -extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ +#if FF_FS_EXFAT +#if FF_INTDEF != 2 +#error exFAT feature wants C99 or later #endif - -#if FF_STR_VOLUME_ID -#ifndef FF_VOLUME_STRS -extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ +typedef QWORD FSIZE_t; +#if FF_LBA64 +typedef QWORD LBA_t; +#else +typedef DWORD LBA_t; #endif +#else +#if FF_LBA64 +#error exFAT needs to be enabled when enable 64-bit LBA +#endif +typedef DWORD FSIZE_t; +typedef DWORD LBA_t; #endif -/* Type of path name strings on FatFs API */ - -#ifndef _INC_TCHAR -#define _INC_TCHAR +/* Type of path name strings on FatFs API (TCHAR) */ #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ typedef WCHAR TCHAR; @@ -101,28 +107,22 @@ typedef char TCHAR; #define _TEXT(x) x #endif -#endif +/* Definitions of volume management */ -/* Type of file size and LBA variables */ +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ +#endif -#if FF_FS_EXFAT -#if FF_INTDEF != 2 -#error exFAT feature wants C99 or later +#if FF_STR_VOLUME_ID +#ifndef FF_VOLUME_STRS +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ #endif -typedef QWORD FSIZE_t; -#if FF_LBA64 -typedef QWORD LBA_t; -#else -typedef DWORD LBA_t; -#endif -#else -#if FF_LBA64 -#error exFAT needs to be enabled when enable 64-bit LBA -#endif -typedef DWORD FSIZE_t; -typedef DWORD LBA_t; #endif @@ -345,10 +345,6 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil #define f_rmdir(path) f_unlink(path) #define f_unmount(path) f_mount(0, path, 0) -#ifndef EOF -#define EOF (-1) -#endif - diff --git a/include/fatfs/ffconf.h b/include/fatfs/ffconf.h index 1d2af0c..3c5f8fe 100644 --- a/include/fatfs/ffconf.h +++ b/include/fatfs/ffconf.h @@ -2,7 +2,7 @@ / FatFs Functional Configurations /---------------------------------------------------------------------------*/ -#define FFCONF_DEF 86606 /* Revision ID */ +#define FFCONF_DEF 86631 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -25,14 +25,6 @@ / 3: f_lseek() function is removed in addition to 2. */ -#define FF_USE_STRFUNC 0 -/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). -/ -/ 0: Disable string functions. -/ 1: Enable without LF-CRLF conversion. -/ 2: Enable with LF-CRLF conversion. */ - - #define FF_USE_FIND 0 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ @@ -64,6 +56,30 @@ /* This option switches f_forward() function. (0:Disable or 1:Enable) */ +#define FF_USE_STRFUNC 0 +#define FF_PRINT_LLI 0 +#define FF_PRINT_FLOAT 0 +#define FF_STRF_ENCODE 0 +/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. +/ +/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 + makes f_printf() support floating point argument. These features want C99 or later. +/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character +/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE +/ to be read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + /*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ @@ -137,19 +153,6 @@ / on character encoding. When LFN is not enabled, these options have no effect. */ -#define FF_STRF_ENCODE 3 -/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), -/ f_putc(), f_puts and f_printf() convert the character encoding in it. -/ This option selects assumption of character encoding ON THE FILE to be -/ read/written via those functions. -/ -/ 0: ANSI/OEM in current CP -/ 1: Unicode in UTF-16LE -/ 2: Unicode in UTF-16BE -/ 3: Unicode in UTF-8 -*/ - - #define FF_FS_RPATH 0 /* This option configures support for relative path. / @@ -194,19 +197,19 @@ #define FF_MAX_SS 512 /* This set of options configures the range of sector size to be supported. (512, / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and -/ harddisk. But a larger value may be required for on-board flash memory and some +/ harddisk, but a larger value may be required for on-board flash memory and some / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ -#define FF_LBA64 0 +#define FF_LBA64 1 /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ -#define FF_MIN_GPT 0x100000000 -/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and +#define FF_MIN_GPT 0x10000000 +/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ @@ -228,7 +231,7 @@ / buffer in the filesystem object (FATFS) is used for the file data transfer. */ -#define FF_FS_EXFAT 0 +#define FF_FS_EXFAT 1 /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) / Note that enabling exFAT discards ANSI C (C89) compatibility. */ diff --git a/source/core/bfttf.c b/source/core/bfttf.c index 89292d1..44ede6a 100644 --- a/source/core/bfttf.c +++ b/source/core/bfttf.c @@ -58,136 +58,142 @@ static bool bfttfDecodeFont(BfttfFontInfo *font_info); bool bfttfInitialize(void) { - mutexLock(&g_bfttfMutex); - - u32 count = 0; NcaContext *nca_ctx = NULL; - TitleInfo *title_info = NULL; - u64 prev_title_id = 0; - RomFileSystemContext romfs_ctx = {0}; - RomFileSystemFileEntry *romfs_file_entry = NULL; + bool ret = false; - bool ret = g_bfttfInterfaceInit; - if (ret) goto end; - - /* Allocate memory for a temporary NCA context. */ - nca_ctx = calloc(1, sizeof(NcaContext)); - if (!nca_ctx) + SCOPED_LOCK(&g_bfttfMutex) { - LOG_MSG("Failed to allocate memory for temporary NCA context!"); - goto end; + ret = g_bfttfInterfaceInit; + if (ret) break; + + u32 count = 0; + u64 prev_title_id = 0; + + /* Allocate memory for a temporary NCA context. */ + nca_ctx = calloc(1, sizeof(NcaContext)); + if (!nca_ctx) + { + LOG_MSG("Failed to allocate memory for temporary NCA context!"); + break; + } + + /* Retrieve BFTTF data. */ + for(u32 i = 0; i < g_fontInfoCount; i++) + { + BfttfFontInfo *font_info = &(g_fontInfo[i]); + TitleInfo *title_info = NULL; + RomFileSystemFileEntry *romfs_file_entry = NULL; + + /* Check if the title ID for the current font container matches the one from the previous font container. */ + /* We won't have to reinitialize both NCA and RomFS contexts if that's the case. */ + if (font_info->title_id != prev_title_id) + { + /* Get title info. */ + if (!(title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, font_info->title_id))) + { + LOG_MSG("Failed to get title info for %016lX!", font_info->title_id); + continue; + } + + /* Initialize NCA context. */ + if (!ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Data, 0), NULL)) + { + LOG_MSG("Failed to initialize Data NCA context for %016lX!", font_info->title_id); + continue; + } + + /* Initialize RomFS context. */ + /* This will also free a previous RomFS context, if available. */ + if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[0]))) + { + LOG_MSG("Failed to initialize RomFS context for Data NCA from %016lX!", font_info->title_id); + continue; + } + + /* Update previous title ID. */ + prev_title_id = font_info->title_id; + } + + /* Get RomFS file entry. */ + if (!(romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, font_info->path))) + { + LOG_MSG("Failed to retrieve RomFS file entry in %016lX!", font_info->title_id); + continue; + } + + /* Check file size. */ + if (!romfs_file_entry->size) + { + LOG_MSG("File size for \"%s\" in %016lX is zero!", font_info->path, font_info->title_id); + continue; + } + + /* Allocate memory for BFTTF data. */ + if (!(font_info->data = malloc(romfs_file_entry->size))) + { + LOG_MSG("Failed to allocate 0x%lX bytes for \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); + continue; + } + + /* Read BFTFF data. */ + if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, font_info->data, romfs_file_entry->size, 0)) + { + LOG_MSG("Failed to read 0x%lX bytes long \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); + free(font_info->data); + font_info->data = NULL; + continue; + } + + /* Update BFTTF size. */ + font_info->size = (u32)romfs_file_entry->size; + + /* Decode BFTTF data. */ + if (!bfttfDecodeFont(font_info)) + { + LOG_MSG("Failed to decode 0x%lX bytes long \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); + free(font_info->data); + font_info->data = NULL; + font_info->size = 0; + continue; + } + + /* Increase retrieved BFTTF count. */ + count++; + } + + /* Update flags. */ + ret = g_bfttfInterfaceInit = (count > 0); + if (!ret) LOG_MSG("No BFTTF fonts retrieved!"); } - /* Retrieve BFTTF data. */ - for(u32 i = 0; i < g_fontInfoCount; i++) - { - BfttfFontInfo *font_info = &(g_fontInfo[i]); - - /* Check if the title ID for the current font container matches the one from the previous font container. */ - /* We won't have to reinitialize both NCA and RomFS contexts if that's the case. */ - if (font_info->title_id != prev_title_id) - { - /* Get title info. */ - if (!(title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, font_info->title_id))) - { - LOG_MSG("Failed to get title info for %016lX!", font_info->title_id); - continue; - } - - /* Initialize NCA context. */ - if (!ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Data, 0), NULL)) - { - LOG_MSG("Failed to initialize Data NCA context for %016lX!", font_info->title_id); - continue; - } - - /* Initialize RomFS context. */ - if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[0]))) - { - LOG_MSG("Failed to initialize RomFS context for Data NCA from %016lX!", font_info->title_id); - continue; - } - - /* Update previous title ID. */ - prev_title_id = font_info->title_id; - } - - /* Get RomFS file entry. */ - if (!(romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, font_info->path))) - { - LOG_MSG("Failed to retrieve RomFS file entry in %016lX!", font_info->title_id); - continue; - } - - /* Check file size. */ - if (!romfs_file_entry->size) - { - LOG_MSG("File size for \"%s\" in %016lX is zero!", font_info->path, font_info->title_id); - continue; - } - - /* Allocate memory for BFTTF data. */ - if (!(font_info->data = malloc(romfs_file_entry->size))) - { - LOG_MSG("Failed to allocate 0x%lX bytes for \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); - continue; - } - - /* Read BFTFF data. */ - if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, font_info->data, romfs_file_entry->size, 0)) - { - LOG_MSG("Failed to read 0x%lX bytes long \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); - free(font_info->data); - font_info->data = NULL; - continue; - } - - /* Update BFTTF size. */ - font_info->size = (u32)romfs_file_entry->size; - - /* Decode BFTTF data. */ - if (!bfttfDecodeFont(font_info)) - { - LOG_MSG("Failed to decode 0x%lX bytes long \"%s\" in %016lX!", romfs_file_entry->size, font_info->path, font_info->title_id); - free(font_info->data); - font_info->data = NULL; - font_info->size = 0; - continue; - } - - /* Increase retrieved BFTTF count. */ - count++; - } - - ret = g_bfttfInterfaceInit = (count > 0); - if (!ret) LOG_MSG("No BFTTF fonts retrieved!"); - -end: romfsFreeContext(&romfs_ctx); if (nca_ctx) free(nca_ctx); - mutexUnlock(&g_bfttfMutex); - return ret; } void bfttfExit(void) { - mutexLock(&g_bfttfMutex); - - /* Free BFTTF data. */ - for(u32 i = 0; i < g_fontInfoCount; i++) + SCOPED_LOCK(&g_bfttfMutex) { - BfttfFontInfo *font_info = &(g_fontInfo[i]); - font_info->size = 0; - if (font_info->data) free(font_info->data); + /* Free BFTTF data. */ + for(u32 i = 0; i < g_fontInfoCount; i++) + { + BfttfFontInfo *font_info = &(g_fontInfo[i]); + + font_info->size = 0; + + if (font_info->data) + { + free(font_info->data); + font_info->data = NULL; + } + } + + g_bfttfInterfaceInit = false; } - - g_bfttfInterfaceInit = false; - - mutexUnlock(&g_bfttfMutex); } bool bfttfGetFontByType(BfttfFontData *font_data, u8 font_type) @@ -198,18 +204,25 @@ bool bfttfGetFontByType(BfttfFontData *font_data, u8 font_type) return false; } - BfttfFontInfo *font_info = &(g_fontInfo[font_type]); - if (font_info->size <= 8 || !font_info->data) + bool ret = false; + + SCOPED_LOCK(&g_bfttfMutex) { - LOG_MSG("BFTTF font data unavailable for type 0x%02X!", font_type); - return false; + BfttfFontInfo *font_info = &(g_fontInfo[font_type]); + if (font_info->size <= 8 || !font_info->data) + { + LOG_MSG("BFTTF font data unavailable for type 0x%02X!", font_type); + break; + } + + font_data->type = font_type; + font_data->size = (font_info->size - 8); + font_data->address = (font_info->data + 8); + + ret = true; } - font_data->type = font_type; - font_data->size = (font_info->size - 8); - font_data->address = (font_info->data + 8); - - return true; + return ret; } static bool bfttfDecodeFont(BfttfFontInfo *font_info) diff --git a/source/core/cert.c b/source/core/cert.c index 9b8dbe9..8fe4b71 100644 --- a/source/core/cert.c +++ b/source/core/cert.c @@ -51,48 +51,40 @@ static void certCopyCertificateChainDataToMemoryBuffer(void *dst, const Certific bool certRetrieveCertificateByName(Certificate *dst, const char *name) { - mutexLock(&g_esCertSaveMutex); - - bool ret = false; - if (!dst || !name || !*name) { LOG_MSG("Invalid parameters!"); - goto end; + return false; } - if (!certOpenEsCertSaveFile()) goto end; + bool ret = false; - ret = _certRetrieveCertificateByName(dst, name); - - certCloseEsCertSaveFile(); - -end: - mutexUnlock(&g_esCertSaveMutex); + SCOPED_LOCK(&g_esCertSaveMutex) + { + if (!certOpenEsCertSaveFile()) break; + ret = _certRetrieveCertificateByName(dst, name); + certCloseEsCertSaveFile(); + } return ret; } bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer) { - mutexLock(&g_esCertSaveMutex); - - bool ret = false; - if (!dst || !issuer || strncmp(issuer, "Root-", 5) != 0) { LOG_MSG("Invalid parameters!"); - goto end; + return false; } - if (!certOpenEsCertSaveFile()) goto end; + bool ret = false; - ret = _certRetrieveCertificateChainBySignatureIssuer(dst, issuer); - - certCloseEsCertSaveFile(); - -end: - mutexUnlock(&g_esCertSaveMutex); + SCOPED_LOCK(&g_esCertSaveMutex) + { + if (!certOpenEsCertSaveFile()) break; + ret = _certRetrieveCertificateChainBySignatureIssuer(dst, issuer); + certCloseEsCertSaveFile(); + } return ret; } diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 3c24def..bb8a747 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -123,7 +123,7 @@ static bool gamecardGetHandleAndStorage(u32 partition); NX_INLINE void gamecardCloseHandle(void); static bool gamecardOpenStorageArea(u8 area); -static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool lock); +static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset); static void gamecardCloseStorageArea(void); static bool gamecardGetStorageAreasSizes(void); @@ -134,228 +134,247 @@ static HashFileSystemContext *_gamecardGetHashFileSystemContext(u8 hfs_partition bool gamecardInitialize(void) { - mutexLock(&g_gameCardMutex); - Result rc = 0; + bool ret = false; - bool ret = g_gameCardInterfaceInit; - if (ret) goto end; - - /* Allocate memory for the gamecard read buffer. */ - g_gameCardReadBuf = malloc(GAMECARD_READ_BUFFER_SIZE); - if (!g_gameCardReadBuf) + SCOPED_LOCK(&g_gameCardMutex) { - LOG_MSG("Unable to allocate memory for the gamecard read buffer!"); - goto end; + ret = g_gameCardInterfaceInit; + if (ret) break; + + /* Allocate memory for the gamecard read buffer. */ + g_gameCardReadBuf = malloc(GAMECARD_READ_BUFFER_SIZE); + if (!g_gameCardReadBuf) + { + LOG_MSG("Unable to allocate memory for the gamecard read buffer!"); + break; + } + + /* Open device operator. */ + rc = fsOpenDeviceOperator(&g_deviceOperator); + if (R_FAILED(rc)) + { + LOG_MSG("fsOpenDeviceOperator failed! (0x%08X).", rc); + break; + } + + g_openDeviceOperator = true; + + /* Open gamecard detection event notifier. */ + rc = fsOpenGameCardDetectionEventNotifier(&g_gameCardEventNotifier); + if (R_FAILED(rc)) + { + LOG_MSG("fsOpenGameCardDetectionEventNotifier failed! (0x%08X)", rc); + break; + } + + g_openEventNotifier = true; + + /* Retrieve gamecard detection kernel event. */ + rc = fsEventNotifierGetEventHandle(&g_gameCardEventNotifier, &g_gameCardKernelEvent, true); + if (R_FAILED(rc)) + { + LOG_MSG("fsEventNotifierGetEventHandle failed! (0x%08X)", rc); + break; + } + + g_loadKernelEvent = true; + + /* Create user-mode exit event. */ + ueventCreate(&g_gameCardDetectionThreadExitEvent, true); + + /* Create user-mode gamecard status change event. */ + ueventCreate(&g_gameCardStatusChangeEvent, true); + + /* Create gamecard detection thread. */ + if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) break; + + /* Update flags. */ + ret = g_gameCardInterfaceInit = true; } - /* Open device operator. */ - rc = fsOpenDeviceOperator(&g_deviceOperator); - if (R_FAILED(rc)) - { - LOG_MSG("fsOpenDeviceOperator failed! (0x%08X).", rc); - goto end; - } - - g_openDeviceOperator = true; - - /* Open gamecard detection event notifier. */ - rc = fsOpenGameCardDetectionEventNotifier(&g_gameCardEventNotifier); - if (R_FAILED(rc)) - { - LOG_MSG("fsOpenGameCardDetectionEventNotifier failed! (0x%08X)", rc); - goto end; - } - - g_openEventNotifier = true; - - /* Retrieve gamecard detection kernel event. */ - rc = fsEventNotifierGetEventHandle(&g_gameCardEventNotifier, &g_gameCardKernelEvent, true); - if (R_FAILED(rc)) - { - LOG_MSG("fsEventNotifierGetEventHandle failed! (0x%08X)", rc); - goto end; - } - - g_loadKernelEvent = true; - - /* Create user-mode exit event. */ - ueventCreate(&g_gameCardDetectionThreadExitEvent, true); - - /* Create user-mode gamecard status change event. */ - ueventCreate(&g_gameCardStatusChangeEvent, true); - - /* Create gamecard detection thread. */ - if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) goto end; - - ret = g_gameCardInterfaceInit = true; - -end: - mutexUnlock(&g_gameCardMutex); - return ret; } void gamecardExit(void) { - mutexLock(&g_gameCardMutex); - - /* Destroy gamecard detection thread. */ - if (g_gameCardDetectionThreadCreated) + SCOPED_LOCK(&g_gameCardMutex) { - gamecardDestroyDetectionThread(); - g_gameCardDetectionThreadCreated = false; + /* Destroy gamecard detection thread. */ + if (g_gameCardDetectionThreadCreated) + { + gamecardDestroyDetectionThread(); + g_gameCardDetectionThreadCreated = false; + } + + /* Close gamecard detection kernel event. */ + if (g_loadKernelEvent) + { + eventClose(&g_gameCardKernelEvent); + g_loadKernelEvent = false; + } + + /* Close gamecard detection event notifier. */ + if (g_openEventNotifier) + { + fsEventNotifierClose(&g_gameCardEventNotifier); + g_openEventNotifier = false; + } + + /* Close device operator. */ + if (g_openDeviceOperator) + { + fsDeviceOperatorClose(&g_deviceOperator); + g_openDeviceOperator = false; + } + + /* Free gamecard read buffer. */ + if (g_gameCardReadBuf) + { + free(g_gameCardReadBuf); + g_gameCardReadBuf = NULL; + } + + g_gameCardInterfaceInit = false; } - - /* Close gamecard detection kernel event. */ - if (g_loadKernelEvent) - { - eventClose(&g_gameCardKernelEvent); - g_loadKernelEvent = false; - } - - /* Close gamecard detection event notifier. */ - if (g_openEventNotifier) - { - fsEventNotifierClose(&g_gameCardEventNotifier); - g_openEventNotifier = false; - } - - /* Close device operator. */ - if (g_openDeviceOperator) - { - fsDeviceOperatorClose(&g_deviceOperator); - g_openDeviceOperator = false; - } - - /* Free gamecard read buffer. */ - if (g_gameCardReadBuf) - { - free(g_gameCardReadBuf); - g_gameCardReadBuf = NULL; - } - - g_gameCardInterfaceInit = false; - - mutexUnlock(&g_gameCardMutex); } UEvent *gamecardGetStatusChangeUserEvent(void) { - mutexLock(&g_gameCardMutex); - UEvent *event = (g_gameCardInterfaceInit ? &g_gameCardStatusChangeEvent : NULL); - mutexUnlock(&g_gameCardMutex); + UEvent *event = NULL; + + SCOPED_LOCK(&g_gameCardMutex) + { + if (g_gameCardInterfaceInit) event = &g_gameCardStatusChangeEvent; + } + return event; } u8 gamecardGetStatus(void) { - mutexLock(&g_gameCardMutex); - u8 status = (g_gameCardInserted ? (g_gameCardInfoLoaded ? GameCardStatus_InsertedAndInfoLoaded : GameCardStatus_InsertedAndInfoNotLoaded) : GameCardStatus_NotInserted); - mutexUnlock(&g_gameCardMutex); + u8 status = GameCardStatus_NotInserted; + + SCOPED_LOCK(&g_gameCardMutex) + { + if (g_gameCardInserted) status = (g_gameCardInfoLoaded ? GameCardStatus_InsertedAndInfoLoaded : GameCardStatus_InsertedAndInfoNotLoaded); + } + return status; } bool gamecardReadStorage(void *out, u64 read_size, u64 offset) { - return gamecardReadStorageArea(out, read_size, offset, true); + bool ret = false; + SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadStorageArea(out, read_size, offset); + return ret; } +/* Read full FS program memory to retrieve the GameCardInitialData block, which is part of the GameCardKeyArea block. */ +/* In FS program memory, this is stored as part of the GameCardSecurityInformation struct, which is returned by Lotus command "ChangeToSecureMode" (0xF). */ +/* This means it is only available *after* the gamecard secure area has been mounted, which is taken care of in gamecardReadInitialData(). */ +/* The GameCardSecurityInformation struct is only kept for documentation purposes. It isn't used at all to retrieve the GameCardInitialData block. */ bool gamecardGetKeyArea(GameCardKeyArea *out) { - /* Read full FS program memory to retrieve the GameCardInitialData block, which is part of the GameCardKeyArea block. */ - /* In FS program memory, this is stored as part of the GameCardSecurityInformation struct, which is returned by Lotus command "ChangeToSecureMode" (0xF). */ - /* This means it is only available *after* the gamecard secure area has been mounted, which is taken care of in gamecardReadInitialData(). */ - /* The GameCardSecurityInformation struct is only kept for documentation purposes. It isn't used at all to retrieve the GameCardInitialData block. */ - mutexLock(&g_gameCardMutex); - bool ret = gamecardReadInitialData(out); - mutexUnlock(&g_gameCardMutex); + bool ret = false; + SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadInitialData(out); return ret; } bool gamecardGetHeader(GameCardHeader *out) { - mutexLock(&g_gameCardMutex); - bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); - if (ret) memcpy(out, &g_gameCardHeader, sizeof(GameCardHeader)); - mutexUnlock(&g_gameCardMutex); + bool ret = false; + + SCOPED_LOCK(&g_gameCardMutex) + { + ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) memcpy(out, &g_gameCardHeader, sizeof(GameCardHeader)); + } + return ret; } bool gamecardGetCertificate(FsGameCardCertificate *out) { - Result rc = 0; bool ret = false; - mutexLock(&g_gameCardMutex); - - if (g_gameCardInserted && g_gameCardHandle.value && out) + SCOPED_LOCK(&g_gameCardMutex) { + if (!g_gameCardInserted || !g_gameCardHandle.value || !out) break; + /* Read the gamecard certificate using the official IPC call. */ - rc = fsDeviceOperatorGetGameCardDeviceCertificate(&g_deviceOperator, &g_gameCardHandle, out); + Result rc = fsDeviceOperatorGetGameCardDeviceCertificate(&g_deviceOperator, &g_gameCardHandle, out); if (R_FAILED(rc)) { LOG_MSG("fsDeviceOperatorGetGameCardDeviceCertificate failed! (0x%08X)", rc); /* Attempt to manually read the gamecard certificate. */ - if (gamecardReadStorageArea(out, sizeof(FsGameCardCertificate), GAMECARD_CERTIFICATE_OFFSET, false)) rc = 0; + if (gamecardReadStorageArea(out, sizeof(FsGameCardCertificate), GAMECARD_CERTIFICATE_OFFSET)) rc = 0; } ret = R_SUCCEEDED(rc); } - mutexUnlock(&g_gameCardMutex); - return ret; } bool gamecardGetTotalSize(u64 *out) { - mutexLock(&g_gameCardMutex); - bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); - if (ret) *out = g_gameCardStorageTotalSize; - mutexUnlock(&g_gameCardMutex); + bool ret = false; + + SCOPED_LOCK(&g_gameCardMutex) + { + ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = g_gameCardStorageTotalSize; + } + return ret; } bool gamecardGetTrimmedSize(u64 *out) { - mutexLock(&g_gameCardMutex); - bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); - if (ret) *out = (sizeof(GameCardHeader) + GAMECARD_PAGE_OFFSET(g_gameCardHeader.valid_data_end_address)); - mutexUnlock(&g_gameCardMutex); + bool ret = false; + + SCOPED_LOCK(&g_gameCardMutex) + { + ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = (sizeof(GameCardHeader) + GAMECARD_PAGE_OFFSET(g_gameCardHeader.valid_data_end_address)); + } + return ret; } bool gamecardGetRomCapacity(u64 *out) { - mutexLock(&g_gameCardMutex); - bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); - if (ret) *out = g_gameCardCapacity; - mutexUnlock(&g_gameCardMutex); + bool ret = false; + + SCOPED_LOCK(&g_gameCardMutex) + { + ret = (g_gameCardInserted && g_gameCardInfoLoaded && out); + if (ret) *out = g_gameCardCapacity; + } + return ret; } bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out) { - Result rc = 0; - u64 update_id = 0; - u32 update_version = 0; bool ret = false; - mutexLock(&g_gameCardMutex); - - if (g_gameCardInserted && g_gameCardHandle.value && out) + SCOPED_LOCK(&g_gameCardMutex) { - rc = fsDeviceOperatorUpdatePartitionInfo(&g_deviceOperator, &g_gameCardHandle, &update_version, &update_id); + if (!g_gameCardInserted || !g_gameCardHandle.value || !out) break; + + u64 update_id = 0; + u32 update_version = 0; + + Result rc = fsDeviceOperatorUpdatePartitionInfo(&g_deviceOperator, &g_gameCardHandle, &update_version, &update_id); if (R_FAILED(rc)) LOG_MSG("fsDeviceOperatorUpdatePartitionInfo failed! (0x%08X)", rc); ret = (R_SUCCEEDED(rc) && update_id == GAMECARD_UPDATE_TID); if (ret) out->value = update_version; } - mutexUnlock(&g_gameCardMutex); - return ret; } @@ -367,49 +386,46 @@ bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemConte return false; } - HashFileSystemContext *fs_ctx = NULL; - bool success = false; - - mutexLock(&g_gameCardMutex); + bool ret = false; /* Free Hash FS context. */ hfsFreeContext(out); - /* Get pointer to the Hash FS context for the requested partition. */ - fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type); - if (!fs_ctx) goto end; - - /* Fill Hash FS context. */ - out->name = strdup(fs_ctx->name); - if (!out->name) + SCOPED_LOCK(&g_gameCardMutex) { - LOG_MSG("Failed to duplicate Hash FS partition name! (%s).", fs_ctx->name); - goto end; + /* Get pointer to the Hash FS context for the requested partition. */ + HashFileSystemContext *fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type); + if (!fs_ctx) break; + + /* Fill Hash FS context. */ + out->name = strdup(fs_ctx->name); + if (!out->name) + { + LOG_MSG("Failed to duplicate Hash FS partition name! (%s).", fs_ctx->name); + break; + } + + out->type = fs_ctx->type; + out->offset = fs_ctx->offset; + out->size = fs_ctx->size; + out->header_size = fs_ctx->header_size; + + out->header = calloc(fs_ctx->header_size, sizeof(u8)); + if (!out->header) + { + LOG_MSG("Failed to duplicate Hash FS partition header! (%s).", fs_ctx->name); + break; + } + + memcpy(out->header, fs_ctx->header, fs_ctx->header_size); + + /* Update flag. */ + ret = true; } - out->type = fs_ctx->type; - out->offset = fs_ctx->offset; - out->size = fs_ctx->size; - out->header_size = fs_ctx->header_size; + if (!ret) hfsFreeContext(out); - out->header = calloc(fs_ctx->header_size, sizeof(u8)); - if (!out->header) - { - LOG_MSG("Failed to duplicate Hash FS partition header! (%s).", fs_ctx->name); - goto end; - } - - memcpy(out->header, fs_ctx->header, fs_ctx->header_size); - - /* Update flag. */ - success = true; - -end: - if (!success) hfsFreeContext(out); - - mutexUnlock(&g_gameCardMutex); - - return success; + return ret; } bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size) @@ -420,31 +436,27 @@ bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char return false; } - HashFileSystemContext *fs_ctx = NULL; - HashFileSystemEntry *fs_entry = NULL; - bool success = false; + bool ret = false; - mutexLock(&g_gameCardMutex); + SCOPED_LOCK(&g_gameCardMutex) + { + /* Get pointer to the Hash FS context for the requested partition. */ + HashFileSystemContext *fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type); + if (!fs_ctx) break; + + /* Get Hash FS entry by name. */ + HashFileSystemEntry *fs_entry = hfsGetEntryByName(fs_ctx, entry_name); + if (!fs_entry) break; + + /* Update output variables. */ + if (out_offset) *out_offset = (fs_ctx->offset + fs_ctx->header_size + fs_entry->offset); + if (out_size) *out_size = fs_entry->size; + + /* Update flag. */ + ret = true; + } - /* Get pointer to the Hash FS context for the requested partition. */ - fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type); - if (!fs_ctx) goto end; - - /* Get Hash FS entry by name. */ - fs_entry = hfsGetEntryByName(fs_ctx, entry_name); - if (!fs_entry) goto end; - - /* Update output variables. */ - if (out_offset) *out_offset = (fs_ctx->offset + fs_ctx->header_size + fs_entry->offset); - if (out_size) *out_size = fs_entry->size; - - /* Update flag. */ - success = true; - -end: - mutexUnlock(&g_gameCardMutex); - - return success; + return ret; } static bool gamecardCreateDetectionThread(void) @@ -479,10 +491,11 @@ static void gamecardDetectionThreadFunc(void *arg) /* Retrieve initial gamecard insertion status. */ /* Load gamecard info right away if a gamecard is inserted, then signal the user mode gamecard status change event. */ - mutexLock(&g_gameCardMutex); - g_gameCardInserted = gamecardIsInserted(); - if (g_gameCardInserted) gamecardLoadInfo(); - mutexUnlock(&g_gameCardMutex); + SCOPED_LOCK(&g_gameCardMutex) + { + g_gameCardInserted = gamecardIsInserted(); + if (g_gameCardInserted) gamecardLoadInfo(); + } ueventSignal(&g_gameCardStatusChangeEvent); @@ -495,24 +508,24 @@ static void gamecardDetectionThreadFunc(void *arg) /* Exit event triggered. */ if (idx == 1) break; - mutexLock(&g_gameCardMutex); - - /* Retrieve current gamecard insertion status. */ - /* Only proceed if we're dealing with a status change. */ - g_gameCardInserted = gamecardIsInserted(); - gamecardFreeInfo(); - - if (g_gameCardInserted) + SCOPED_LOCK(&g_gameCardMutex) { - /* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules. */ - utilsSleep(GAMECARD_ACCESS_WAIT_TIME); + /* Free gamecard info before proceeding. */ + gamecardFreeInfo(); - /* Load gamecard info. */ - gamecardLoadInfo(); + /* Retrieve current gamecard insertion status. */ + /* Only proceed if we're dealing with a status change. */ + g_gameCardInserted = gamecardIsInserted(); + if (g_gameCardInserted) + { + /* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules. */ + utilsSleep(GAMECARD_ACCESS_WAIT_TIME); + + /* Load gamecard info. */ + gamecardLoadInfo(); + } } - mutexUnlock(&g_gameCardMutex); - /* Signal user mode gamecard status change event. */ ueventSignal(&g_gameCardStatusChangeEvent); } @@ -550,7 +563,7 @@ static void gamecardLoadInfo(void) } /* Read gamecard header. */ - if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0, false)) + if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0)) { LOG_MSG("Failed to read gamecard header!"); goto end; @@ -798,21 +811,18 @@ static bool gamecardOpenStorageArea(u8 area) return true; } -static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool lock) +static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset) { - if (lock) mutexLock(&g_gameCardMutex); - - bool success = false; - if (!g_gameCardInserted || !g_gameCardStorageNormalAreaSize || !g_gameCardStorageSecureAreaSize || !out || !read_size || (offset + read_size) > g_gameCardStorageTotalSize) { LOG_MSG("Invalid parameters!"); - goto end; + return false; } Result rc = 0; u8 *out_u8 = (u8*)out; u8 area = (offset < g_gameCardStorageNormalAreaSize ? GameCardStorageArea_Normal : GameCardStorageArea_Secure); + bool success = false; /* Handle reads that span both the normal and secure gamecard storage areas. */ if (area == GameCardStorageArea_Normal && (offset + read_size) > g_gameCardStorageNormalAreaSize) @@ -820,7 +830,8 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l /* Calculate normal storage area size difference. */ u64 diff_size = (g_gameCardStorageNormalAreaSize - offset); - if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto end; + /* Read normal storage area data. */ + if (!gamecardReadStorageArea(out_u8, diff_size, offset)) goto end; /* Adjust variables to read right from the start of the secure storage area. */ read_size -= diff_size; @@ -870,12 +881,10 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l memcpy(out_u8, g_gameCardReadBuf + data_start_offset, out_chunk_size); - success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, false) : true); + success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size) : true); } end: - if (lock) mutexUnlock(&g_gameCardMutex); - return success; } @@ -1014,7 +1023,7 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char fs_ctx->type = i; /* Read partial Hash FS header. */ - if (!gamecardReadStorageArea(&fs_header, sizeof(HashFileSystemHeader), offset, false)) + if (!gamecardReadStorageArea(&fs_header, sizeof(HashFileSystemHeader), offset)) { LOG_MSG("Failed to read partial Hash FS header! (\"%s\", offset 0x%lX).", fs_ctx->name, offset); goto end; @@ -1050,7 +1059,7 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char } /* Read full Hash FS header. */ - if (!gamecardReadStorageArea(fs_ctx->header, fs_ctx->header_size, offset, false)) + if (!gamecardReadStorageArea(fs_ctx->header, fs_ctx->header_size, offset)) { LOG_MSG("Failed to read full Hash FS header! (\"%s\", offset 0x%lX).", fs_ctx->name, offset); goto end; diff --git a/source/core/keys.c b/source/core/keys.c index eb07e90..8383fb3 100644 --- a/source/core/keys.c +++ b/source/core/keys.c @@ -33,11 +33,14 @@ /* Type definitions. */ +typedef bool (*KeysIsKeyMandatoryFunction)(void); /* Used to determine if a key is mandatory or not at runtime. */ + typedef struct { char name[64]; u8 hash[SHA256_HASH_SIZE]; u64 size; void *dst; + KeysIsKeyMandatoryFunction mandatory_func; ///< If NULL, key is mandatory. } KeysMemoryKey; typedef struct { @@ -53,6 +56,10 @@ typedef struct { u8 nca_header_kek_sealed[AES_128_KEY_SIZE]; ///< Generated from nca_header_kek_source. Sealed by the SMC AES engine. u8 nca_header_key[AES_128_KEY_SIZE * 2]; ///< Generated from nca_header_kek_sealed and nca_header_key_source. + ///< RSA-2048-PSS moduli used to verify the main signature from NCA headers. + u8 nca_main_signature_moduli_prod[NcaMainSignatureKeyGeneration_Max][RSA2048_PUBKEY_SIZE]; ///< Moduli used in retail units. Retrieved from the .rodata segment in the FS sysmodule. + u8 nca_main_signature_moduli_dev[NcaMainSignatureKeyGeneration_Max][RSA2048_PUBKEY_SIZE]; ///< Moduli used in development units. Retrieved from the .rodata segment in the FS sysmodule. + ///< AES-128-ECB keys needed to handle key area crypto from NCA headers. u8 nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Count][AES_128_KEY_SIZE]; ///< Retrieved from the .rodata segment in the FS sysmodule. u8 nca_kaek_sealed[NcaKeyAreaEncryptionKeyIndex_Count][NcaKeyGeneration_Max][AES_128_KEY_SIZE]; ///< Generated from nca_kaek_sources. Sealed by the SMC AES engine. @@ -80,6 +87,27 @@ typedef struct { NXDT_ASSERT(EticketRsaDeviceKey, 0x240); +/* Function prototypes. */ + +static bool keysIsProductionModulus1xMandatory(void); +static bool keysIsProductionModulus9xMandatory(void); + +static bool keysIsDevelopmentModulus1xMandatory(void); +static bool keysIsDevelopmentModulus9xMandatory(void); + +static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info); + +static bool keysDeriveNcaHeaderKey(void); +static bool keysDeriveSealedNcaKeyAreaEncryptionKeys(void); + +static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value); +static char keysConvertHexCharToBinary(char c); +static bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size); +static bool keysReadKeysFromFile(void); + +static bool keysGetDecryptedEticketRsaDeviceKey(void); +static bool keysTestEticketRsaDeviceKey(const void *e, const void *d, const void *n); + /* Global variables. */ static KeysNcaKeyset g_ncaKeyset = {0}; @@ -101,7 +129,7 @@ static KeysMemoryInfo g_fsRodataMemoryInfo = { .data = NULL, .data_size = 0 }, - .key_count = 4, + .key_count = 8, .keys = { { .name = "nca_header_kek_source", @@ -109,8 +137,49 @@ static KeysMemoryInfo g_fsRodataMemoryInfo = { 0x18, 0x88, 0xCA, 0xED, 0x55, 0x51, 0xB3, 0xED, 0xE0, 0x14, 0x99, 0xE8, 0x7C, 0xE0, 0xD8, 0x68, 0x27, 0xF8, 0x08, 0x20, 0xEF, 0xB2, 0x75, 0x92, 0x10, 0x55, 0xAA, 0x4E, 0x2A, 0xBD, 0xFF, 0xC2 }, - .size = AES_128_KEY_SIZE, - .dst = g_ncaKeyset.nca_header_kek_source + .size = sizeof(g_ncaKeyset.nca_header_kek_source), + .dst = g_ncaKeyset.nca_header_kek_source, + .mandatory_func = NULL + }, + { + .name = "nca_main_signature_modulus_prod_00", + .hash = { + 0xF9, 0x2E, 0x84, 0x98, 0x17, 0x2C, 0xAF, 0x9C, 0x20, 0xE3, 0xF1, 0xF7, 0xD3, 0xE7, 0x2C, 0x62, + 0x50, 0xA9, 0x40, 0x7A, 0xE7, 0x84, 0xE0, 0x03, 0x58, 0x07, 0x85, 0xA5, 0x68, 0x0B, 0x80, 0x33 + }, + .size = sizeof(g_ncaKeyset.nca_main_signature_moduli_prod[NcaMainSignatureKeyGeneration_100_811]), + .dst = g_ncaKeyset.nca_main_signature_moduli_prod[NcaMainSignatureKeyGeneration_100_811], + .mandatory_func = &keysIsProductionModulus1xMandatory + }, + { + .name = "nca_main_signature_modulus_prod_01", + .hash = { + 0x5F, 0x6B, 0xE3, 0x1C, 0x31, 0x6E, 0x7C, 0xB2, 0x1C, 0xA7, 0xB9, 0xA1, 0x70, 0x6A, 0x9D, 0x58, + 0x04, 0xEB, 0x90, 0x53, 0x72, 0xEF, 0xCB, 0x56, 0xD1, 0x93, 0xF2, 0xAF, 0x9E, 0x8A, 0xD1, 0xFA + }, + .size = sizeof(g_ncaKeyset.nca_main_signature_moduli_prod[NcaMainSignatureKeyGeneration_900_1202]), + .dst = g_ncaKeyset.nca_main_signature_moduli_prod[NcaMainSignatureKeyGeneration_900_1202], + .mandatory_func = &keysIsProductionModulus9xMandatory + }, + { + .name = "nca_main_signature_modulus_dev_00", + .hash = { + 0x50, 0xF8, 0x26, 0xBB, 0x13, 0xFE, 0xB2, 0x6D, 0x83, 0xCF, 0xFF, 0xD8, 0x38, 0x45, 0xC3, 0x51, + 0x4D, 0xCB, 0x06, 0x91, 0x83, 0x52, 0x06, 0x35, 0x7A, 0xC1, 0xDA, 0x6B, 0xF1, 0x60, 0x9F, 0x18 + }, + .size = sizeof(g_ncaKeyset.nca_main_signature_moduli_dev[NcaMainSignatureKeyGeneration_100_811]), + .dst = g_ncaKeyset.nca_main_signature_moduli_dev[NcaMainSignatureKeyGeneration_100_811], + .mandatory_func = &keysIsDevelopmentModulus1xMandatory + }, + { + .name = "nca_main_signature_modulus_dev_01", + .hash = { + 0x56, 0xF5, 0x06, 0xEF, 0x8E, 0xCA, 0x2A, 0x29, 0x6F, 0x65, 0x45, 0xE1, 0x87, 0x60, 0x01, 0x11, + 0xBC, 0xC7, 0x38, 0x56, 0x99, 0x16, 0xAD, 0xA5, 0xDD, 0x89, 0xF2, 0xE9, 0xAB, 0x28, 0x5B, 0x18 + }, + .size = sizeof(g_ncaKeyset.nca_main_signature_moduli_dev[NcaMainSignatureKeyGeneration_900_1202]), + .dst = g_ncaKeyset.nca_main_signature_moduli_dev[NcaMainSignatureKeyGeneration_900_1202], + .mandatory_func = &keysIsDevelopmentModulus9xMandatory }, { .name = "nca_kaek_application_source", @@ -118,8 +187,9 @@ static KeysMemoryInfo g_fsRodataMemoryInfo = { 0x04, 0xAD, 0x66, 0x14, 0x3C, 0x72, 0x6B, 0x2A, 0x13, 0x9F, 0xB6, 0xB2, 0x11, 0x28, 0xB4, 0x6F, 0x56, 0xC5, 0x53, 0xB2, 0xB3, 0x88, 0x71, 0x10, 0x30, 0x42, 0x98, 0xD8, 0xD0, 0x09, 0x2D, 0x9E }, - .size = AES_128_KEY_SIZE, - .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Application] + .size = sizeof(g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Application]), + .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Application], + .mandatory_func = NULL }, { .name = "nca_kaek_ocean_source", @@ -127,8 +197,9 @@ static KeysMemoryInfo g_fsRodataMemoryInfo = { 0xFD, 0x43, 0x40, 0x00, 0xC8, 0xFF, 0x2B, 0x26, 0xF8, 0xE9, 0xA9, 0xD2, 0xD2, 0xC1, 0x2F, 0x6B, 0xE5, 0x77, 0x3C, 0xBB, 0x9D, 0xC8, 0x63, 0x00, 0xE1, 0xBD, 0x99, 0xF8, 0xEA, 0x33, 0xA4, 0x17 }, - .size = AES_128_KEY_SIZE, - .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Ocean] + .size = sizeof(g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Ocean]), + .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_Ocean], + .mandatory_func = NULL }, { .name = "nca_kaek_system_source", @@ -136,8 +207,9 @@ static KeysMemoryInfo g_fsRodataMemoryInfo = { 0x1F, 0x17, 0xB1, 0xFD, 0x51, 0xAD, 0x1C, 0x23, 0x79, 0xB5, 0x8F, 0x15, 0x2C, 0xA4, 0x91, 0x2E, 0xC2, 0x10, 0x64, 0x41, 0xE5, 0x17, 0x22, 0xF3, 0x87, 0x00, 0xD5, 0x93, 0x7A, 0x11, 0x62, 0xF7 }, - .size = AES_128_KEY_SIZE, - .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_System] + .size = sizeof(g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_System]), + .dst = g_ncaKeyset.nca_kaek_sources[NcaKeyAreaEncryptionKeyIndex_System], + .mandatory_func = NULL } } }; @@ -157,94 +229,84 @@ static KeysMemoryInfo g_fsDataMemoryInfo = { 0x8F, 0x78, 0x3E, 0x46, 0x85, 0x2D, 0xF6, 0xBE, 0x0B, 0xA4, 0xE1, 0x92, 0x73, 0xC4, 0xAD, 0xBA, 0xEE, 0x16, 0x38, 0x00, 0x43, 0xE1, 0xB8, 0xC4, 0x18, 0xC4, 0x08, 0x9A, 0x8B, 0xD6, 0x4A, 0xA6 }, - .size = (AES_128_KEY_SIZE * 2), - .dst = g_ncaKeyset.nca_header_key_source + .size = sizeof(g_ncaKeyset.nca_header_key_source), + .dst = g_ncaKeyset.nca_header_key_source, + .mandatory_func = NULL } } }; -/* Function prototypes. */ - -static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info); - -static bool keysDeriveNcaHeaderKey(void); -static bool keysDeriveSealedNcaKeyAreaEncryptionKeys(void); - -static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value); -static char keysConvertHexCharToBinary(char c); -static bool keysParseHexKey(u8 *out, const char *key, const char *value, u32 size); -static bool keysReadKeysFromFile(void); - -static bool keysGetDecryptedEticketRsaDeviceKey(void); -static bool keysTestEticketRsaDeviceKey(const void *e, const void *d, const void *n); - bool keysLoadNcaKeyset(void) { - mutexLock(&g_ncaKeysetMutex); + bool ret = false; - bool ret = g_ncaKeysetLoaded; - if (ret) goto end; - - /* Retrieve FS .rodata keys. */ - if (!keysRetrieveKeysFromProgramMemory(&g_fsRodataMemoryInfo)) + SCOPED_LOCK(&g_ncaKeysetMutex) { - LOG_MSG("Unable to retrieve keys from FS .rodata segment!"); - goto end; + ret = g_ncaKeysetLoaded; + if (ret) break; + + /* Retrieve FS .rodata keys. */ + if (!keysRetrieveKeysFromProgramMemory(&g_fsRodataMemoryInfo)) + { + LOG_MSG("Unable to retrieve keys from FS .rodata segment!"); + break; + } + + /* Retrieve FS .data keys. */ + if (!keysRetrieveKeysFromProgramMemory(&g_fsDataMemoryInfo)) + { + LOG_MSG("Unable to retrieve keys from FS .data segment!"); + break; + } + + /* Derive NCA header key. */ + if (!keysDeriveNcaHeaderKey()) + { + LOG_MSG("Unable to derive NCA header key!"); + break; + } + + /* Derive sealed NCA KAEKs. */ + if (!keysDeriveSealedNcaKeyAreaEncryptionKeys()) + { + LOG_MSG("Unable to derive sealed NCA KAEKs!"); + break; + } + + /* Read additional keys from the keys file. */ + if (!keysReadKeysFromFile()) break; + + /* Get decrypted eTicket RSA device key. */ + if (!keysGetDecryptedEticketRsaDeviceKey()) break; + + /* Update flags. */ + ret = g_ncaKeysetLoaded = true; } - /* Retrieve FS .data keys. */ - if (!keysRetrieveKeysFromProgramMemory(&g_fsDataMemoryInfo)) - { - LOG_MSG("Unable to retrieve keys from FS .data segment!"); - goto end; - } - - /* Derive NCA header key. */ - if (!keysDeriveNcaHeaderKey()) - { - LOG_MSG("Unable to derive NCA header key!"); - goto end; - } - - /* Derive sealed NCA KAEKs. */ - if (!keysDeriveSealedNcaKeyAreaEncryptionKeys()) - { - LOG_MSG("Unable to derive sealed NCA KAEKs!"); - goto end; - } - - /* Read additional keys from the keys file. */ - if (!keysReadKeysFromFile()) goto end; - - /* Get decrypted eTicket RSA device key. */ - if (!keysGetDecryptedEticketRsaDeviceKey()) goto end; - - ret = g_ncaKeysetLoaded = true; - -end: /*if (ret) { LOG_DATA(&g_ncaKeyset, sizeof(KeysNcaKeyset), "NCA keyset dump:"); LOG_DATA(&g_eTicketRsaDeviceKey, sizeof(SetCalRsa2048DeviceKey), "eTicket RSA device key dump:"); }*/ - mutexUnlock(&g_ncaKeysetMutex); - return ret; } const u8 *keysGetNcaHeaderKey(void) { - mutexLock(&g_ncaKeysetMutex); - const u8 *ptr = (g_ncaKeysetLoaded ? (const u8*)(g_ncaKeyset.nca_header_key) : NULL); - mutexUnlock(&g_ncaKeysetMutex); - return ptr; + const u8 *ret = NULL; + + SCOPED_LOCK(&g_ncaKeysetMutex) + { + if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_header_key); + } + + return ret; } bool keysDecryptNcaKeyAreaEntry(u8 kaek_index, u8 key_generation, void *dst, const void *src) { - Result rc = 0; - bool success = false; + bool ret = false; u8 key_gen_val = (key_generation ? (key_generation - 1) : key_generation); if (kaek_index >= NcaKeyAreaEncryptionKeyIndex_Count) @@ -265,23 +327,20 @@ bool keysDecryptNcaKeyAreaEntry(u8 kaek_index, u8 key_generation, void *dst, con goto end; } - mutexLock(&g_ncaKeysetMutex); - - if (g_ncaKeysetLoaded) + SCOPED_LOCK(&g_ncaKeysetMutex) { - rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_kaek_sealed[kaek_index][key_gen_val], src, dst); - if (!(success = R_SUCCEEDED(rc))) LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X).", rc); + if (!g_ncaKeysetLoaded) break; + Result rc = splCryptoGenerateAesKey(g_ncaKeyset.nca_kaek_sealed[kaek_index][key_gen_val], src, dst); + if (!(ret = R_SUCCEEDED(rc))) LOG_MSG("splCryptoGenerateAesKey failed! (0x%08X).", rc); } - mutexUnlock(&g_ncaKeysetMutex); - end: - return success; + return ret; } const u8 *keysGetNcaKeyAreaEncryptionKey(u8 kaek_index, u8 key_generation) { - const u8 *ptr = NULL; + const u8 *ret = NULL; u8 key_gen_val = (key_generation ? (key_generation - 1) : key_generation); if (kaek_index >= NcaKeyAreaEncryptionKeyIndex_Count) @@ -296,14 +355,13 @@ const u8 *keysGetNcaKeyAreaEncryptionKey(u8 kaek_index, u8 key_generation) goto end; } - mutexLock(&g_ncaKeysetMutex); - - if (g_ncaKeysetLoaded) ptr = (const u8*)(g_ncaKeyset.nca_kaek[kaek_index][key_gen_val]); - - mutexUnlock(&g_ncaKeysetMutex); + SCOPED_LOCK(&g_ncaKeysetMutex) + { + if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.nca_kaek[kaek_index][key_gen_val]); + } end: - return ptr; + return ret; } bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *out_titlekey) @@ -314,22 +372,22 @@ bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *o return false; } - size_t out_keydata_size = 0; - u8 out_keydata[0x100] = {0}; - EticketRsaDeviceKey *eticket_rsa_key = NULL; - bool success = false; + bool ret = false; - mutexLock(&g_ncaKeysetMutex); - - if (g_ncaKeysetLoaded) + SCOPED_LOCK(&g_ncaKeysetMutex) { + if (!g_ncaKeysetLoaded) break; + + size_t out_keydata_size = 0; + u8 out_keydata[0x100] = {0}; + /* Get eTicket RSA device key. */ - eticket_rsa_key = (EticketRsaDeviceKey*)g_eTicketRsaDeviceKey.key; + EticketRsaDeviceKey *eticket_rsa_key = (EticketRsaDeviceKey*)g_eTicketRsaDeviceKey.key; /* Perform a RSA-OAEP unwrap operation to get the encrypted titlekey. */ - success = (rsa2048OaepDecryptAndVerify(out_keydata, sizeof(out_keydata), rsa_wrapped_titlekey, eticket_rsa_key->modulus, eticket_rsa_key->exponent, sizeof(eticket_rsa_key->exponent), \ + ret = (rsa2048OaepDecryptAndVerify(out_keydata, sizeof(out_keydata), rsa_wrapped_titlekey, eticket_rsa_key->modulus, eticket_rsa_key->exponent, sizeof(eticket_rsa_key->exponent), \ g_nullHash, &out_keydata_size) && out_keydata_size >= AES_128_KEY_SIZE); - if (success) + if (ret) { /* Copy RSA-OAEP unwrapped titlekey. */ memcpy(out_titlekey, out_keydata, AES_128_KEY_SIZE); @@ -338,14 +396,12 @@ bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *o } } - mutexUnlock(&g_ncaKeysetMutex); - - return success; + return ret; } const u8 *keysGetTicketCommonKey(u8 key_generation) { - const u8 *ptr = NULL; + const u8 *ret = NULL; u8 key_gen_val = (key_generation ? (key_generation - 1) : key_generation); if (key_gen_val >= NcaKeyGeneration_Max) @@ -354,14 +410,33 @@ const u8 *keysGetTicketCommonKey(u8 key_generation) goto end; } - mutexLock(&g_ncaKeysetMutex); - - if (g_ncaKeysetLoaded) ptr = (const u8*)(g_ncaKeyset.ticket_common_keys[key_gen_val]); - - mutexUnlock(&g_ncaKeysetMutex); + SCOPED_LOCK(&g_ncaKeysetMutex) + { + if (g_ncaKeysetLoaded) ret = (const u8*)(g_ncaKeyset.ticket_common_keys[key_gen_val]); + } end: - return ptr; + return ret; +} + +static bool keysIsProductionModulus1xMandatory(void) +{ + return !utilsIsDevelopmentUnit(); +} + +static bool keysIsProductionModulus9xMandatory(void) +{ + return (!utilsIsDevelopmentUnit() && hosversionAtLeast(9, 0, 0)); +} + +static bool keysIsDevelopmentModulus1xMandatory(void) +{ + return utilsIsDevelopmentUnit(); +} + +static bool keysIsDevelopmentModulus9xMandatory(void) +{ + return (utilsIsDevelopmentUnit() && hosversionAtLeast(9, 0, 0)); } static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info) @@ -372,7 +447,6 @@ static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info) return false; } - bool found; u8 tmp_hash[SHA256_HASH_SIZE]; bool success = false; @@ -380,14 +454,13 @@ static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info) for(u32 i = 0; i < info->key_count; i++) { - found = false; - KeysMemoryKey *key = &(info->keys[i]); + bool found = false, mandatory = (key->mandatory_func != NULL ? key->mandatory_func() : true); if (!key->dst) { LOG_MSG("Invalid destination pointer for key \"%s\" in program %016lX!", key->name, info->location.program_id); - goto end; + if (mandatory) goto end; } /* Hash every key length-sized byte chunk in the process memory buffer until a match is found. */ @@ -409,7 +482,7 @@ static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info) if (!found) { LOG_MSG("Unable to locate key \"%s\" in process memory from program %016lX!", key->name, info->location.program_id); - goto end; + if (mandatory) goto end; } } diff --git a/source/core/lz4.c b/source/core/lz4.c index 9808d70..9f5e9bf 100644 --- a/source/core/lz4.c +++ b/source/core/lz4.c @@ -45,10 +45,16 @@ #endif /* - * ACCELERATION_DEFAULT : + * LZ4_ACCELERATION_DEFAULT : * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 */ -#define ACCELERATION_DEFAULT 1 +#define LZ4_ACCELERATION_DEFAULT 1 +/* + * LZ4_ACCELERATION_MAX : + * Any "acceleration" value higher than this threshold + * get treated as LZ4_ACCELERATION_MAX instead (fix #876) + */ +#define LZ4_ACCELERATION_MAX 65537 /*-************************************ @@ -82,6 +88,7 @@ * Define this parameter if your target system or compiler does not support hardware bit count */ #if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ +# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ # define LZ4_FORCE_SW_BITCOUNT #endif @@ -114,10 +121,9 @@ /*-************************************ * Compiler Options **************************************/ -#ifdef _MSC_VER /* Visual Studio */ -# include -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ +# include /* only present in VS2005+ */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE @@ -136,7 +142,7 @@ # endif /* _MSC_VER */ #endif /* LZ4_FORCE_INLINE */ -/* LZ4_FORCE_O2_GCC_PPC64LE and LZ4_FORCE_O2_INLINE_GCC_PPC64LE +/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, * together with a simple 8-byte copy loop as a fall-back path. * However, this optimization hurts the decompression speed by >30%, @@ -151,11 +157,11 @@ * of LZ4_wildCopy8 does not affect the compression speed. */ #if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) -# define LZ4_FORCE_O2_GCC_PPC64LE __attribute__((optimize("O2"))) -# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE __attribute__((optimize("O2"))) LZ4_FORCE_INLINE +# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) +# undef LZ4_FORCE_INLINE +# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) #else -# define LZ4_FORCE_O2_GCC_PPC64LE -# define LZ4_FORCE_O2_INLINE_GCC_PPC64LE static +# define LZ4_FORCE_O2 #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) @@ -171,14 +177,33 @@ #define unlikely(expr) expect((expr) != 0, 0) #endif +/* Should the alignment test prove unreliable, for some reason, + * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# define LZ4_ALIGN_TEST 1 +#endif + /*-************************************ * Memory routines **************************************/ -#include /* malloc, calloc, free */ -#define ALLOC(s) malloc(s) -#define ALLOC_AND_ZERO(s) calloc(1,s) -#define FREEMEM(p) free(p) +#ifdef LZ4_USER_MEMORY_FUNCTIONS +/* memory management functions can be customized by user project. + * Below functions must exist somewhere in the Project + * and be available at link time */ +void* LZ4_malloc(size_t s); +void* LZ4_calloc(size_t n, size_t s); +void LZ4_free(void* p); +# define ALLOC(s) LZ4_malloc(s) +# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) +# define FREEMEM(p) LZ4_free(p) +#else +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,s) +# define FREEMEM(p) free(p) +#endif + #include /* memset, memcpy */ #define MEM_INIT(p,v,s) memset((p),(v),(s)) @@ -225,21 +250,27 @@ static const int LZ4_minLength = (MFLIMIT+1); #if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) # include -static int g_debuglog_enable = 1; -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ - } } + static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } #else -# define DEBUGLOG(l, ...) {} /* disabled */ +# define DEBUGLOG(l, ...) {} /* disabled */ #endif +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + /*-************************************ * Types **************************************/ +#include #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # include typedef uint8_t BYTE; @@ -249,6 +280,9 @@ static int g_debuglog_enable = 1; typedef uint64_t U64; typedef uintptr_t uptrval; #else +# if UINT_MAX != 4294967295UL +# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" +# endif typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; @@ -273,6 +307,21 @@ typedef enum { /*-************************************ * Reading and writing into memory **************************************/ + +/** + * LZ4 relies on memcpy with a constant size being inlined. In freestanding + * environments, the compiler can't assume the implementation of memcpy() is + * standard compliant, so it can't apply its specialized memcpy() inlining + * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze + * memcpy() as if it were standard compliant, so it can inline it in freestanding + * environments. This is needed when decompressing the Linux Kernel, for example. + */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +#else +#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +#endif + static unsigned LZ4_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ @@ -307,27 +356,27 @@ static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = val static U16 LZ4_read16(const void* memPtr) { - U16 val; memcpy(&val, memPtr, sizeof(val)); return val; + U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; } static U32 LZ4_read32(const void* memPtr) { - U32 val; memcpy(&val, memPtr, sizeof(val)); return val; + U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; } static reg_t LZ4_read_ARCH(const void* memPtr) { - reg_t val; memcpy(&val, memPtr, sizeof(val)); return val; + reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; } static void LZ4_write16(void* memPtr, U16 value) { - memcpy(memPtr, &value, sizeof(value)); + LZ4_memcpy(memPtr, &value, sizeof(value)); } static void LZ4_write32(void* memPtr, U32 value) { - memcpy(memPtr, &value, sizeof(value)); + LZ4_memcpy(memPtr, &value, sizeof(value)); } #endif /* LZ4_FORCE_MEMORY_ACCESS */ @@ -355,14 +404,14 @@ static void LZ4_writeLE16(void* memPtr, U16 value) } /* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -LZ4_FORCE_O2_INLINE_GCC_PPC64LE +LZ4_FORCE_INLINE void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; - do { memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ -LZ4_FORCE_O2_INLINE_GCC_PPC64LE void +LZ4_FORCE_INLINE void LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) { BYTE* d = (BYTE*)dstPtr; const BYTE* s = (const BYTE*)srcPtr; BYTE* const e = (BYTE*)dstEnd; - do { memcpy(d,s,16); memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH * - there is at least 8 bytes available to write after dstEnd */ -LZ4_FORCE_O2_INLINE_GCC_PPC64LE void +LZ4_FORCE_INLINE void LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) { BYTE v[8]; assert(dstEnd >= dstPtr + MINMATCH); - LZ4_write32(dstPtr, 0); /* silence an msan warning when offset==0 */ switch(offset) { case 1: - memset(v, *srcPtr, 8); + MEM_INIT(v, *srcPtr, 8); break; case 2: - memcpy(v, srcPtr, 2); - memcpy(&v[2], srcPtr, 2); - memcpy(&v[4], &v[0], 4); + LZ4_memcpy(v, srcPtr, 2); + LZ4_memcpy(&v[2], srcPtr, 2); + LZ4_memcpy(&v[4], v, 4); break; case 4: - memcpy(v, srcPtr, 4); - memcpy(&v[4], srcPtr, 4); + LZ4_memcpy(v, srcPtr, 4); + LZ4_memcpy(&v[4], srcPtr, 4); break; default: LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); return; } - memcpy(dstPtr, v, 8); + LZ4_memcpy(dstPtr, v, 8); dstPtr += 8; while (dstPtr < dstEnd) { - memcpy(dstPtr, v, 8); + LZ4_memcpy(dstPtr, v, 8); dstPtr += 8; } } @@ -462,75 +512,92 @@ LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const si **************************************/ static unsigned LZ4_NbCommonBytes (reg_t val) { + assert(val != 0); if (LZ4_isLittleEndian()) { - if (sizeof(val)==8) { -# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + if (sizeof(val) == 8) { +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && defined(_M_AMD64) && !defined(LZ4_FORCE_SW_BITCOUNT) + /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ + return (unsigned)_tzcnt_u64(val) >> 3; +# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; - _BitScanForward64( &r, (U64)val ); - return (int)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + _BitScanForward64(&r, (U64)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctzll((U64)val) >> 3; # else - static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, - 0, 3, 1, 3, 1, 4, 2, 7, - 0, 2, 3, 6, 1, 5, 3, 5, - 1, 3, 4, 4, 2, 5, 6, 7, - 7, 0, 1, 2, 3, 3, 4, 6, - 2, 6, 5, 5, 3, 4, 5, 6, - 7, 1, 2, 4, 6, 4, 4, 5, - 7, 2, 6, 5, 7, 6, 7, 7 }; - return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; + const U64 m = 0x0101010101010101ULL; + val ^= val - 1; + return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); # endif } else /* 32 bits */ { -# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r; - _BitScanForward( &r, (U32)val ); - return (int)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + _BitScanForward(&r, (U32)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_ctz((U32)val) >> 3; # else - static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, - 3, 2, 2, 1, 3, 2, 0, 1, - 3, 3, 1, 2, 2, 2, 2, 0, - 3, 1, 2, 0, 1, 0, 1, 1 }; - return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; # endif } } else /* Big Endian CPU */ { - if (sizeof(val)==8) { /* 64-bits */ -# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanReverse64( &r, val ); - return (unsigned)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + if (sizeof(val)==8) { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clzll((U64)val) >> 3; # else +#if 1 + /* this method is probably faster, + * but adds a 128 bytes lookup table */ + static const unsigned char ctz7_tab[128] = { + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + }; + U64 const mask = 0x0101010101010101ULL; + U64 const t = (((val >> 8) - mask) | val) & mask; + return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; +#else + /* this method doesn't consume memory space like the previous one, + * but it contains several branches, + * that may end up slowing execution */ static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. - Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. - Note that this code path is never triggered in 32-bits mode. */ + Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. + Note that this code path is never triggered in 32-bits mode. */ unsigned r; if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } r += (!val); return r; +#endif # endif } else /* 32 bits */ { -# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanReverse( &r, (unsigned long)val ); - return (unsigned)(r>>3); -# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) return (unsigned)__builtin_clz((U32)val) >> 3; # else - unsigned r; - if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } - r += (!val); - return r; + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; # endif } } } + #define STEPSIZE sizeof(reg_t) LZ4_FORCE_INLINE unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) @@ -605,7 +672,7 @@ typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState() { return LZ4_STREAMSIZE; } +int LZ4_sizeofState(void) { return LZ4_STREAMSIZE; } /*-************************************ @@ -628,7 +695,7 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, /*-****************************** * Compression functions ********************************/ -static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) { if (tableType == byU16) return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); @@ -636,7 +703,7 @@ static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); } -static U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) { const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; if (LZ4_isLittleEndian()) { @@ -654,7 +721,7 @@ LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tab return LZ4_hash4(LZ4_read32(p), tableType); } -static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) +LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { @@ -666,7 +733,7 @@ static void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) } } -static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) +LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) { switch (tableType) { @@ -678,7 +745,7 @@ static void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t cons } } -static void LZ4_putPositionOnHash(const BYTE* p, U32 h, +LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) { @@ -703,7 +770,7 @@ LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_ * Assumption 1 : only valid if tableType == byU32 or byU16. * Assumption 2 : h is presumed valid (within limits of hash table) */ -static U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) +LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) { LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); if (tableType == byU32) { @@ -739,22 +806,13 @@ LZ4_FORCE_INLINE void LZ4_prepareTable(LZ4_stream_t_internal* const cctx, const int inputSize, const tableType_t tableType) { - /* If compression failed during the previous step, then the context - * is marked as dirty, therefore, it has to be fully reset. - */ - if (cctx->dirty) { - DEBUGLOG(5, "LZ4_prepareTable: Full reset for %p", cctx); - MEM_INIT(cctx, 0, sizeof(LZ4_stream_t_internal)); - return; - } - /* If the table hasn't been used, it's guaranteed to be zeroed out, and is * therefore safe to use no matter what mode we're in. Otherwise, we figure * out if it's safe to leave as is or whether it needs to be reset. */ - if (cctx->tableType != clearedTable) { + if ((tableType_t)cctx->tableType != clearedTable) { assert(inputSize >= 0); - if (cctx->tableType != tableType + if ((tableType_t)cctx->tableType != tableType || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) || ((tableType == byU32) && cctx->currentOffset > 1 GB) || tableType == byPtr @@ -763,7 +821,7 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx, DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); cctx->currentOffset = 0; - cctx->tableType = clearedTable; + cctx->tableType = (U32)clearedTable; } else { DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); } @@ -785,8 +843,12 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx, } /** LZ4_compress_generic() : - inlined, to ensure branches are decided at compilation time */ -LZ4_FORCE_INLINE int LZ4_compress_generic( + * inlined, to ensure branches are decided at compilation time. + * Presumed already validated at this stage: + * - source != NULL + * - inputSize > 0 + */ +LZ4_FORCE_INLINE int LZ4_compress_generic_validated( LZ4_stream_t_internal* const cctx, const char* const source, char* const dest, @@ -815,7 +877,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic( int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ - const BYTE* const dictEnd = dictionary + dictSize; + const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; const BYTE* anchor = (const BYTE*) source; const BYTE* const iend = ip + inputSize; const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; @@ -823,7 +885,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic( /* the dictCtx currentOffset is indexed on the start of the dictionary, * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = (dictDirective == usingDictCtx) ? + const BYTE* dictBase = !dictionary ? NULL : (dictDirective == usingDictCtx) ? dictionary + dictSize - dictCtx->currentOffset : dictionary + dictSize - startIndex; @@ -833,11 +895,11 @@ LZ4_FORCE_INLINE int LZ4_compress_generic( U32 offset = 0; U32 forwardH; - DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, tableType=%u", inputSize, tableType); + DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); + assert(ip != NULL); /* If init conditions are not met, we don't have to mark stream * as having dirty context, since no action was taken yet */ if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ - if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported inputSize, too large (or negative) */ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ assert(acceleration >= 1); @@ -854,7 +916,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic( cctx->dictSize += (U32)inputSize; } cctx->currentOffset += (U32)inputSize; - cctx->tableType = (U16)tableType; + cctx->tableType = (U32)tableType; if (inputSize= op); - lastRun = (size_t)(olimit-op) - 1; - lastRun -= (lastRun+240)/255; + lastRun = (size_t)(olimit-op) - 1/*token*/; + lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ } else { assert(outputDirective == limitedOutput); return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ } } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); if (lastRun >= RUN_MASK) { size_t accumulator = lastRun - RUN_MASK; *op++ = RUN_MASK << ML_BITS; @@ -1162,7 +1225,7 @@ _last_literals: } else { *op++ = (BYTE)(lastRun< 0); + DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); return result; } +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time; + * takes care of src == (NULL, 0) + * and forward the rest to LZ4_compress_generic_validated */ +LZ4_FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const src, + char* const dst, + const int srcSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int dstCapacity, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", + srcSize, dstCapacity); + + if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ + if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ + if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ + DEBUGLOG(5, "Generating an empty block"); + assert(outputDirective == notLimited || dstCapacity >= 1); + assert(dst != NULL); + dst[0] = 0; + if (outputDirective == fillOutput) { + assert (inputConsumed != NULL); + *inputConsumed = 0; + } + return 1; + } + assert(src != NULL); + + return LZ4_compress_generic_validated(cctx, src, dst, srcSize, + inputConsumed, /* only written into if outputDirective == fillOutput */ + dstCapacity, outputDirective, + tableType, dictDirective, dictIssue, acceleration); +} + int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) { LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; assert(ctx != NULL); - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; if (maxOutputSize >= LZ4_compressBound(inputSize)) { if (inputSize < LZ4_64Klimit) { return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); @@ -1211,7 +1316,8 @@ int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) { LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; if (dstCapacity >= LZ4_compressBound(srcSize)) { if (srcSize < LZ4_64Klimit) { @@ -1270,22 +1376,6 @@ int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputS } -/* hidden debug function */ -/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ -int LZ4_compress_fast_force(const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) -{ - LZ4_stream_t ctx; - LZ4_initStream(&ctx, sizeof(ctx)); - - if (srcSize < LZ4_64Klimit) { - return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, byU16, noDict, noDictIssue, acceleration); - } else { - tableType_t const addrMode = (sizeof(void*) > 4) ? byU32 : byPtr; - return LZ4_compress_generic(&ctx.internal_donotuse, src, dst, srcSize, NULL, dstCapacity, limitedOutput, addrMode, noDict, noDictIssue, acceleration); - } -} - - /* Note!: This function leaves the stream in an unclean/broken state! * It is not safe to subsequently use the same state with a _fastReset() or * _continue() call without resetting it. */ @@ -1340,27 +1430,23 @@ LZ4_stream_t* LZ4_createStream(void) return lz4s; } -#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : - it reports an aligment of 8-bytes, - while actually aligning LZ4_stream_t on 4 bytes. */ static size_t LZ4_stream_t_alignment(void) { - struct { char c; LZ4_stream_t t; } t_a; - return sizeof(t_a) - sizeof(t_a.t); -} +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_stream_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_stream_t); +#else + return 1; /* effectively disabled */ #endif +} LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) { DEBUGLOG(5, "LZ4_initStream"); if (buffer == NULL) { return NULL; } if (size < sizeof(LZ4_stream_t)) { return NULL; } -#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : - it reports an aligment of 8-bytes, - while actually aligning LZ4_stream_t on 4 bytes. */ - if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */ -#endif - MEM_INIT(buffer, 0, sizeof(LZ4_stream_t)); + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; + MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); return (LZ4_stream_t*)buffer; } @@ -1369,7 +1455,7 @@ LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) void LZ4_resetStream (LZ4_stream_t* LZ4_stream) { DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); } void LZ4_resetStream_fast(LZ4_stream_t* ctx) { @@ -1418,7 +1504,7 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) base = dictEnd - dict->currentOffset; dict->dictionary = p; dict->dictSize = (U32)(dictEnd - p); - dict->tableType = tableType; + dict->tableType = (U32)tableType; while (p <= dictEnd-HASH_UNIT) { LZ4_putPosition(p, dict->hashTable, tableType, base); @@ -1436,12 +1522,6 @@ void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dict workingStream, dictionaryStream, dictCtx != NULL ? dictCtx->dictSize : 0); - /* Calling LZ4_resetStream_fast() here makes sure that changes will not be - * erased by subsequent calls to LZ4_resetStream_fast() in case stream was - * marked as having dirty context, e.g. requiring full reset. - */ - LZ4_resetStream_fast(workingStream); - if (dictCtx != NULL) { /* If the current offset is zero, we will never look in the * external dictionary context, since there is no value a table @@ -1493,9 +1573,9 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize); - if (streamPtr->dirty) { return 0; } /* Uninitialized structure detected */ LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */ - if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; /* invalidate tiny dictionaries */ if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */ @@ -1538,7 +1618,7 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, * cost to copy the dictionary's tables into the active context, * so that the compression loop is only looking into one table. */ - memcpy(streamPtr, streamPtr->dictCtx, sizeof(LZ4_stream_t)); + LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); } else { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); @@ -1593,7 +1673,9 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } - memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) + memmove(safeBuffer, previousDictEnd - dictSize, dictSize); dict->dictionary = (const BYTE*)safeBuffer; dict->dictSize = (U32)dictSize; @@ -1623,25 +1705,27 @@ typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; */ typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; LZ4_FORCE_INLINE unsigned -read_variable_length(const BYTE**ip, const BYTE* lencheck, int loop_check, int initial_check, variable_length_error* error) +read_variable_length(const BYTE**ip, const BYTE* lencheck, + int loop_check, int initial_check, + variable_length_error* error) { - unsigned length = 0; - unsigned s; - if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = initial_error; - return length; - } - do { - s = **ip; - (*ip)++; - length += s; - if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = loop_error; - return length; + U32 length = 0; + U32 s; + if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ + *error = initial_error; + return length; } - } while (s==255); + do { + s = **ip; + (*ip)++; + length += s; + if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ + *error = loop_error; + return length; + } + } while (s==255); - return length; + return length; } /*! LZ4_decompress_generic() : @@ -1722,7 +1806,7 @@ LZ4_decompress_generic( /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); + length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ @@ -1746,12 +1830,12 @@ LZ4_decompress_generic( /* We don't need to check oend, since we check it once for each loop below */ if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ - memcpy(op, ip, 16); + LZ4_memcpy(op, ip, 16); } else { /* LZ4_decompress_fast() */ /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : * it doesn't know input length, and relies on end-of-block properties */ - memcpy(op, ip, 8); - if (length > 8) { memcpy(op+8, ip+8, 8); } + LZ4_memcpy(op, ip, 8); + if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); } } ip += length; op = cpy; } @@ -1765,10 +1849,10 @@ LZ4_decompress_generic( length = token & ML_MASK; if (length == ML_MASK) { - variable_length_error error = ok; - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); - if (error != ok) { goto _output_error; } + variable_length_error error = ok; + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); + if (error != ok) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ length += MINMATCH; if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { @@ -1787,19 +1871,20 @@ LZ4_decompress_generic( assert(match <= op); assert(op + 18 <= oend); - memcpy(op, match, 8); - memcpy(op+8, match+8, 8); - memcpy(op+16, match+16, 2); + LZ4_memcpy(op, match, 8); + LZ4_memcpy(op+8, match+8, 8); + LZ4_memcpy(op+16, match+16, 2); op += length; continue; } } } - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) { - length = MIN(length, (size_t)(oend-op)); /* reach end of buffer */ + DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); + length = MIN(length, (size_t)(oend-op)); } else { goto _output_error; /* end-of-block condition violated */ } } @@ -1812,14 +1897,14 @@ LZ4_decompress_generic( /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; - memcpy(op, dictEnd - copySize, copySize); + LZ4_memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) { *op++ = *copyFrom++; } } else { - memcpy(op, lowPrefix, restSize); + LZ4_memcpy(op, lowPrefix, restSize); op += restSize; } } continue; @@ -1860,7 +1945,7 @@ LZ4_decompress_generic( /* strictly "less than" on input, to re-enter the loop with at least one byte */ && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { /* Copy the literals */ - memcpy(op, ip, endOnInput ? 16 : 8); + LZ4_memcpy(op, ip, endOnInput ? 16 : 8); op += length; ip += length; /* The second stage: prepare for match copying, decode full info. @@ -1875,9 +1960,9 @@ LZ4_decompress_generic( && (offset >= 8) && (dict==withPrefix64k || match >= lowPrefix) ) { /* Copy the match. */ - memcpy(op + 0, match + 0, 8); - memcpy(op + 8, match + 8, 8); - memcpy(op +16, match +16, 2); + LZ4_memcpy(op + 0, match + 0, 8); + LZ4_memcpy(op + 8, match + 8, 8); + LZ4_memcpy(op +16, match +16, 2); op += length + MINMATCH; /* Both stages worked, load the next token. */ continue; @@ -1891,7 +1976,7 @@ LZ4_decompress_generic( /* decode literal length */ if (length == RUN_MASK) { variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, endOnInput, endOnInput, &error); + length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); if (error == initial_error) { goto _output_error; } if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ @@ -1907,29 +1992,34 @@ LZ4_decompress_generic( || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) { /* We've either hit the input parsing restriction or the output parsing restriction. - * If we've hit the input parsing condition then this must be the last sequence. - * If we've hit the output parsing condition then we are either using partialDecoding - * or we've hit the output parsing condition. + * In the normal scenario, decoding a full block, it must be the last sequence, + * otherwise it's an error (invalid input or dimensions). + * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. */ if (partialDecoding) { /* Since we are partial decoding we may be in this block because of the output parsing * restriction, which is not valid since the output buffer is allowed to be undersized. */ assert(endOnInput); - /* If we're in this block because of the input parsing condition, then we must be on the - * last sequence (or invalid), so we must check that we exactly consume the input. + DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") + DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); + DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); + DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); + /* Finishing in the middle of a literals segment, + * due to lack of input. */ - if ((ip+length>iend-(2+1+LASTLITERALS)) && (ip+length != iend)) { goto _output_error; } - assert(ip+length <= iend); - /* We are finishing in the middle of a literals segment. - * Break after the copy. + if (ip+length > iend) { + length = (size_t)(iend-ip); + cpy = op + length; + } + /* Finishing in the middle of a literals segment, + * due to lack of output space. */ if (cpy > oend) { cpy = oend; assert(op<=oend); length = (size_t)(oend-op); } - assert(ip+length <= iend); } else { /* We must be on the last sequence because of the parsing limitations so check * that we exactly regenerate the original size (must be exact when !endOnInput). @@ -1938,16 +2028,22 @@ LZ4_decompress_generic( /* We must be on the last sequence (or invalid) because of the parsing limitations * so check that we exactly consume the input and don't overrun the output buffer. */ - if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { goto _output_error; } + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { + DEBUGLOG(6, "should have been last run of literals") + DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + goto _output_error; + } } - memmove(op, ip, length); /* supports overlapping memory regions, which only matters for in-place decompression scenarios */ + memmove(op, ip, length); /* supports overlapping memory regions; only matters for in-place decompression scenarios */ ip += length; op += length; - /* Necessarily EOF when !partialDecoding. When partialDecoding - * it is EOF if we've either filled the output buffer or hit - * the input parsing restriction. + /* Necessarily EOF when !partialDecoding. + * When partialDecoding, it is EOF if we've either + * filled the output buffer or + * can't proceed with reading an offset for following match. */ - if (!partialDecoding || (cpy == oend) || (ip == iend)) { + if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { break; } } else { @@ -1965,7 +2061,7 @@ LZ4_decompress_generic( _copy_match: if (length == ML_MASK) { variable_length_error error = ok; - length += read_variable_length(&ip, iend - LASTLITERALS + 1, endOnInput, 0, &error); + length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); if (error != ok) goto _output_error; if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ } @@ -1990,14 +2086,14 @@ LZ4_decompress_generic( /* match stretches into both external dictionary and current block */ size_t const copySize = (size_t)(lowPrefix - match); size_t const restSize = length - copySize; - memcpy(op, dictEnd - copySize, copySize); + LZ4_memcpy(op, dictEnd - copySize, copySize); op += copySize; if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ BYTE* const endOfMatch = op + restSize; const BYTE* copyFrom = lowPrefix; while (op < endOfMatch) *op++ = *copyFrom++; } else { - memcpy(op, lowPrefix, restSize); + LZ4_memcpy(op, lowPrefix, restSize); op += restSize; } } continue; @@ -2016,7 +2112,7 @@ LZ4_decompress_generic( if (matchEnd > op) { /* overlap copy */ while (op < copyEnd) { *op++ = *match++; } } else { - memcpy(op, match, mlen); + LZ4_memcpy(op, match, mlen); } op = copyEnd; if (op == oend) { break; } @@ -2030,10 +2126,10 @@ LZ4_decompress_generic( op[2] = match[2]; op[3] = match[3]; match += inc32table[offset]; - memcpy(op+4, match, 4); + LZ4_memcpy(op+4, match, 4); match -= dec64table[offset]; } else { - memcpy(op, match, 8); + LZ4_memcpy(op, match, 8); match += 8; } op += 8; @@ -2048,7 +2144,7 @@ LZ4_decompress_generic( } while (op < cpy) { *op++ = *match++; } } else { - memcpy(op, match, 8); + LZ4_memcpy(op, match, 8); if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } } op = cpy; /* wildcopy correction */ @@ -2056,6 +2152,7 @@ LZ4_decompress_generic( /* end of decoding */ if (endOnInput) { + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ } else { return (int) (((const char*)ip)-src); /* Nb of input bytes read */ @@ -2070,7 +2167,7 @@ LZ4_decompress_generic( /*===== Instantiate the API decoding functions. =====*/ -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, @@ -2078,7 +2175,7 @@ int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int (BYTE*)dest, NULL, 0); } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) { dstCapacity = MIN(targetOutputSize, dstCapacity); @@ -2087,7 +2184,7 @@ int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, noDict, (BYTE*)dst, NULL, 0); } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_fast(const char* source, char* dest, int originalSize) { return LZ4_decompress_generic(source, dest, 0, originalSize, @@ -2097,7 +2194,7 @@ int LZ4_decompress_fast(const char* source, char* dest, int originalSize) /*===== Instantiate a few more decoding cases, used more than once. =====*/ -LZ4_FORCE_O2_GCC_PPC64LE /* Exported, an obsolete API function. */ +LZ4_FORCE_O2 /* Exported, an obsolete API function. */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, @@ -2113,7 +2210,7 @@ int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int origin return LZ4_decompress_fast(source, dest, originalSize); } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, size_t prefixSize) { @@ -2122,7 +2219,7 @@ static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, i (BYTE*)dest-prefixSize, NULL, 0); } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const void* dictStart, size_t dictSize) @@ -2132,7 +2229,7 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, const void* dictStart, size_t dictSize) { @@ -2221,7 +2318,7 @@ int LZ4_decoderRingBufferSize(int maxBlockSize) If it's not possible, save the relevant part of decoded data into a safe buffer, and indicate where it stands using LZ4_setStreamDecode() */ -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; @@ -2261,7 +2358,7 @@ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const ch return result; } -LZ4_FORCE_O2_GCC_PPC64LE +LZ4_FORCE_O2 int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; @@ -2374,7 +2471,7 @@ int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, /* Obsolete Streaming functions */ -int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } +int LZ4_sizeofStreamState(void) { return LZ4_STREAMSIZE; } int LZ4_resetStreamState(void* state, char* inputBuffer) { diff --git a/source/core/mem.c b/source/core/mem.c index 746d9f1..22091ec 100644 --- a/source/core/mem.c +++ b/source/core/mem.c @@ -45,10 +45,8 @@ bool memRetrieveProgramMemorySegment(MemoryLocation *location) return false; } - mutexLock(&g_memMutex); - bool ret = memRetrieveProgramMemory(location, true); - mutexUnlock(&g_memMutex); - + bool ret = false; + SCOPED_LOCK(&g_memMutex) ret = memRetrieveProgramMemory(location, true); return ret; } @@ -60,10 +58,8 @@ bool memRetrieveFullProgramMemory(MemoryLocation *location) return false; } - mutexLock(&g_memMutex); - bool ret = memRetrieveProgramMemory(location, false); - mutexUnlock(&g_memMutex); - + bool ret = false; + SCOPED_LOCK(&g_memMutex) ret = memRetrieveProgramMemory(location, false); return ret; } diff --git a/source/core/nca.c b/source/core/nca.c index 78f97b3..d19a769 100644 --- a/source/core/nca.c +++ b/source/core/nca.c @@ -52,32 +52,35 @@ NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx); NX_INLINE u8 ncaGetKeyGenerationValue(NcaContext *ctx); NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx); -static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, bool lock); -static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool lock); +static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset); +static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val); static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch); static bool ncaWritePatchToMemoryBuffer(NcaContext *ctx, const void *patch, u64 patch_size, u64 patch_offset, void *buf, u64 buf_size, u64 buf_offset); -static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock); +static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset); bool ncaAllocateCryptoBuffer(void) { - mutexLock(&g_ncaCryptoBufferMutex); - if (!g_ncaCryptoBuffer) g_ncaCryptoBuffer = malloc(NCA_CRYPTO_BUFFER_SIZE); - bool ret = (g_ncaCryptoBuffer != NULL); - mutexUnlock(&g_ncaCryptoBufferMutex); + bool ret = false; + + SCOPED_LOCK(&g_ncaCryptoBufferMutex) + { + if (!g_ncaCryptoBuffer) g_ncaCryptoBuffer = malloc(NCA_CRYPTO_BUFFER_SIZE); + ret = (g_ncaCryptoBuffer != NULL); + } + return ret; } void ncaFreeCryptoBuffer(void) { - mutexLock(&g_ncaCryptoBufferMutex); - if (g_ncaCryptoBuffer) + SCOPED_LOCK(&g_ncaCryptoBufferMutex) { + if (!g_ncaCryptoBuffer) break; free(g_ncaCryptoBuffer); g_ncaCryptoBuffer = NULL; } - mutexUnlock(&g_ncaCryptoBufferMutex); } bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik) @@ -296,22 +299,30 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset) bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset) { - return _ncaReadFsSection(ctx, out, read_size, offset, true); + bool ret = false; + SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaReadFsSection(ctx, out, read_size, offset); + return ret; } bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val) { - return _ncaReadAesCtrExStorageFromBktrSection(ctx, out, read_size, offset, ctr_val, true); + bool ret = false; + SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaReadAesCtrExStorageFromBktrSection(ctx, out, read_size, offset, ctr_val); + return ret; } void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset) { - return _ncaGenerateEncryptedFsSectionBlock(ctx, data, data_size, data_offset, out_block_size, out_block_offset, true); + void *ret = NULL; + SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaGenerateEncryptedFsSectionBlock(ctx, data, data_size, data_offset, out_block_size, out_block_offset); + return ret; } bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out) { - return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, false); + bool ret = false; + SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, false); + return ret; } void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset) @@ -333,7 +344,9 @@ void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchi bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out) { - return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, true); + bool ret = false; + SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, true); + return ret; } void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset) @@ -748,18 +761,14 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx) return false; } -static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, bool lock) +static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset) { - if (lock) mutexLock(&g_ncaCryptoBufferMutex); - - bool ret = false; - if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !out || !read_size || \ (offset + read_size) > ctx->section_size) { LOG_MSG("Invalid NCA FS section header parameters!"); - goto end; + return false; } size_t crypt_res = 0; @@ -771,6 +780,8 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size u64 block_start_offset = 0, block_end_offset = 0, block_size = 0; u64 data_start_offset = 0, chunk_size = 0, out_chunk_size = 0; + bool ret = false; + if (!*(nca_ctx->content_id_str) || (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \ (nca_ctx->format_version != NcaVersion_Nca0 && nca_ctx->format_version != NcaVersion_Nca2 && nca_ctx->format_version != NcaVersion_Nca3) || (content_offset + read_size) > nca_ctx->content_size) { @@ -861,25 +872,19 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size /* Copy decrypted data. */ memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size); - ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, false) : true); + ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size) : true); end: - if (lock) mutexUnlock(&g_ncaCryptoBufferMutex); - return ret; } -static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool lock) +static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val) { - if (lock) mutexLock(&g_ncaCryptoBufferMutex); - - bool ret = false; - if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !out || !read_size || (offset + read_size) > ctx->section_size) { LOG_MSG("Invalid NCA FS section header parameters!"); - goto end; + return false; } NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx; @@ -888,6 +893,8 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi u64 block_start_offset = 0, block_end_offset = 0, block_size = 0; u64 data_start_offset = 0, chunk_size = 0, out_chunk_size = 0; + bool ret = false; + if (!*(nca_ctx->content_id_str) || (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \ (content_offset + read_size) > nca_ctx->content_size) { @@ -939,19 +946,15 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi /* Copy decrypted data. */ memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size); - ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val, false) : true); + ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val) : true); end: - if (lock) mutexUnlock(&g_ncaCryptoBufferMutex); - return ret; } /* In this function, the term "layer" is used as a generic way to refer to both HierarchicalSha256 hash regions and HierarchicalIntegrity verification levels. */ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch) { - mutexLock(&g_ncaCryptoBufferMutex); - NcaContext *nca_ctx = NULL; NcaHierarchicalSha256Patch *hierarchical_sha256_patch = (!is_integrity_patch ? ((NcaHierarchicalSha256Patch*)out) : NULL); NcaHierarchicalIntegrityPatch *hierarchical_integrity_patch = (is_integrity_patch ? ((NcaHierarchicalIntegrityPatch*)out) : NULL); @@ -1067,7 +1070,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, } /* Read current layer block. */ - if (!_ncaReadFsSection(ctx, cur_layer_block, cur_layer_read_size, cur_layer_read_start_offset, false)) + if (!_ncaReadFsSection(ctx, cur_layer_block, cur_layer_read_size, cur_layer_read_start_offset)) { LOG_MSG("Failed to read 0x%lX bytes long hierarchical layer #%u data block from offset 0x%lX! (current).", cur_layer_read_size, i - 1, cur_layer_read_start_offset); goto end; @@ -1088,7 +1091,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, } /* Read parent layer block. */ - if (!_ncaReadFsSection(ctx, parent_layer_block, parent_layer_read_size, parent_layer_offset + parent_layer_read_start_offset, false)) + if (!_ncaReadFsSection(ctx, parent_layer_block, parent_layer_read_size, parent_layer_offset + parent_layer_read_start_offset)) { LOG_MSG("Failed to read 0x%lX bytes long hierarchical layer #%u data block from offset 0x%lX! (parent).", parent_layer_read_size, i - 2, parent_layer_read_start_offset); goto end; @@ -1110,7 +1113,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, /* Reencrypt current layer block. */ cur_layer_patch->data = _ncaGenerateEncryptedFsSectionBlock(ctx, cur_layer_block + cur_layer_read_patch_offset, cur_data_size, cur_layer_offset + cur_data_offset, \ - &(cur_layer_patch->size), &(cur_layer_patch->offset), false); + &(cur_layer_patch->size), &(cur_layer_patch->offset)); if (!cur_layer_patch->data) { LOG_MSG("Failed to generate encrypted 0x%lX bytes long hierarchical layer #%u data block!", cur_data_size, i - 1); @@ -1160,8 +1163,6 @@ end: } } - mutexUnlock(&g_ncaCryptoBufferMutex); - return success; } @@ -1188,10 +1189,8 @@ static bool ncaWritePatchToMemoryBuffer(NcaContext *ctx, const void *patch, u64 return ((patch_block_offset + buf_block_size) == patch_size); } -static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock) +static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset) { - if (lock) mutexLock(&g_ncaCryptoBufferMutex); - u8 *out = NULL; bool success = false; @@ -1278,7 +1277,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const } /* Read decrypted data using aligned offset and size. */ - if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset, false)) + if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset)) { LOG_MSG("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_num); goto end; @@ -1318,7 +1317,5 @@ end: out = NULL; } - if (lock) mutexUnlock(&g_ncaCryptoBufferMutex); - return out; } diff --git a/source/core/nxdt_log.c b/source/core/nxdt_log.c index 0725185..0a9768b 100644 --- a/source/core/nxdt_log.c +++ b/source/core/nxdt_log.c @@ -38,29 +38,29 @@ static char *g_logBuffer = NULL; static size_t g_logBufferLength = 0; static const char *g_utf8Bom = "\xEF\xBB\xBF"; -static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%lu] %s -> "; +static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%09lu] %s -> "; static const char *g_logLineBreak = "\r\n"; /* Function prototypes. */ -static void _logWriteStringToLogFile(const char *src, bool lock); -static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args, bool lock); +static void _logWriteStringToLogFile(const char *src); +static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args); -static void _logFlushLogFile(bool lock); +static void _logFlushLogFile(void); static bool logAllocateLogBuffer(void); static bool logOpenLogFile(void); void logWriteStringToLogFile(const char *src) { - _logWriteStringToLogFile(src, true); + SCOPED_LOCK(&g_logMutex) _logWriteStringToLogFile(src); } __attribute__((format(printf, 2, 3))) void logWriteFormattedStringToLogFile(const char *func_name, const char *fmt, ...) { va_list args; va_start(args, fmt); - _logWriteFormattedStringToLogFile(true, func_name, fmt, args, true); + SCOPED_LOCK(&g_logMutex) _logWriteFormattedStringToLogFile(true, func_name, fmt, args); va_end(args); } @@ -135,8 +135,6 @@ __attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const voi size_t data_str_size = ((data_size * 2) + 3); char *data_str = NULL; - mutexLock(&g_logMutex); - /* Allocate memory for the hex string representation of the provided binary data. */ data_str = calloc(data_str_size, sizeof(char)); if (!data_str) goto end; @@ -145,91 +143,93 @@ __attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const voi utilsGenerateHexStringFromData(data_str, data_str_size, data, data_size, true); strcat(data_str, g_logLineBreak); - /* Write formatted string. */ - va_start(args, fmt); - _logWriteFormattedStringToLogFile(false, func_name, fmt, args, false); - va_end(args); - - /* Write hex string representation. */ - _logWriteStringToLogFile(data_str, false); + SCOPED_LOCK(&g_logMutex) + { + /* Write formatted string. */ + va_start(args, fmt); + _logWriteFormattedStringToLogFile(false, func_name, fmt, args); + va_end(args); + + /* Write hex string representation. */ + _logWriteStringToLogFile(data_str); + } end: if (data_str) free(data_str); - - mutexUnlock(&g_logMutex); } void logFlushLogFile(void) { - _logFlushLogFile(true); + SCOPED_LOCK(&g_logMutex) _logFlushLogFile(); } void logCloseLogFile(void) { - mutexLock(&g_logMutex); - - /* Flush log buffer. */ - _logFlushLogFile(false); - - /* Close logfile. */ - if (serviceIsActive(&(g_logFile.s))) + SCOPED_LOCK(&g_logMutex) { - fsFileClose(&g_logFile); - memset(&g_logFile, 0, sizeof(FsFile)); + /* Flush log buffer. */ + _logFlushLogFile(); - /* Commit SD card filesystem changes. */ - utilsCommitSdCardFileSystemChanges(); + /* Close logfile. */ + if (serviceIsActive(&(g_logFile.s))) + { + fsFileClose(&g_logFile); + memset(&g_logFile, 0, sizeof(FsFile)); + + /* Commit SD card filesystem changes. */ + utilsCommitSdCardFileSystemChanges(); + } + + /* Free log buffer. */ + if (g_logBuffer) + { + free(g_logBuffer); + g_logBuffer = NULL; + } + + /* Reset logfile offset. */ + g_logFileOffset = 0; } - - /* Free log buffer. */ - if (g_logBuffer) - { - free(g_logBuffer); - g_logBuffer = NULL; - } - - g_logFileOffset = 0; - - mutexUnlock(&g_logMutex); } void logGetLastMessage(char *dst, size_t dst_size) { - mutexLock(&g_logMutex); - if (dst && dst_size > 1 && *g_lastLogMsg) snprintf(dst, dst_size, "%s", g_lastLogMsg); - mutexUnlock(&g_logMutex); + SCOPED_LOCK(&g_logMutex) + { + if (dst && dst_size > 1 && *g_lastLogMsg) snprintf(dst, dst_size, "%s", g_lastLogMsg); + } } void logControlMutex(bool lock) { - if (lock) + bool locked = mutexIsLockedByCurrentThread(&g_logMutex); + + if (!locked && lock) { mutexLock(&g_logMutex); - } else { + } else + if (locked && !lock) + { mutexUnlock(&g_logMutex); } } -static void _logWriteStringToLogFile(const char *src, bool lock) +static void _logWriteStringToLogFile(const char *src) { - if (!src || !*src) return; - - if (lock) mutexLock(&g_logMutex); + /* Make sure we have allocated memory for the log buffer and opened the logfile. */ + if (!src || !*src || !logAllocateLogBuffer() || !logOpenLogFile()) return; Result rc = 0; size_t src_len = strlen(src), tmp_len = 0; - /* Make sure we have allocated memory for the log buffer and opened the logfile. */ - if (!logAllocateLogBuffer() || !logOpenLogFile()) goto end; - /* Check if the formatted string length is lower than the log buffer size. */ if (src_len < LOG_BUF_SIZE) { /* Flush log buffer contents (if needed). */ if ((g_logBufferLength + src_len) >= LOG_BUF_SIZE) { - _logFlushLogFile(false); - if (g_logBufferLength) goto end; + _logFlushLogFile(); + if (g_logBufferLength) return; } /* Copy string into the log buffer. */ @@ -237,14 +237,14 @@ static void _logWriteStringToLogFile(const char *src, bool lock) g_logBufferLength += src_len; } else { /* Flush log buffer. */ - _logFlushLogFile(false); - if (g_logBufferLength) goto end; + _logFlushLogFile(); + if (g_logBufferLength) return; /* Write string data until it no longer exceeds the log buffer size. */ while(src_len >= LOG_BUF_SIZE) { rc = fsFileWrite(&g_logFile, g_logFileOffset, src + tmp_len, LOG_BUF_SIZE, FsWriteOption_Flush); - if (R_FAILED(rc)) goto end; + if (R_FAILED(rc)) return; g_logFileOffset += LOG_BUF_SIZE; tmp_len += LOG_BUF_SIZE; @@ -261,18 +261,14 @@ static void _logWriteStringToLogFile(const char *src, bool lock) #if LOG_FORCE_FLUSH == 1 /* Flush log buffer. */ - _logFlushLogFile(false); + _logFlushLogFile(); #endif - -end: - if (lock) mutexUnlock(&g_logMutex); } -static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args, bool lock) +static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, const char *fmt, va_list args) { - if (!func_name || !*func_name || !fmt || !*fmt) return; - - if (lock) mutexLock(&g_logMutex); + /* Make sure we have allocated memory for the log buffer and opened the logfile. */ + if (!func_name || !*func_name || !fmt || !*fmt || !logAllocateLogBuffer() || !logOpenLogFile()) return; Result rc = 0; @@ -293,10 +289,10 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, /* Get formatted string length. */ str1_len = snprintf(NULL, 0, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name); - if (str1_len <= 0) goto end; + if (str1_len <= 0) return; str2_len = vsnprintf(NULL, 0, fmt, args); - if (str2_len <= 0) goto end; + if (str2_len <= 0) return; log_str_len = (size_t)(str1_len + str2_len + 2); @@ -313,17 +309,14 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, tmp_len = 0; } - /* Make sure we have allocated memory for the log buffer and opened the logfile. */ - if (!logAllocateLogBuffer() || !logOpenLogFile()) goto end; - /* Check if the formatted string length is less than the log buffer size. */ if (log_str_len < LOG_BUF_SIZE) { /* Flush log buffer contents (if needed). */ if ((g_logBufferLength + log_str_len) >= LOG_BUF_SIZE) { - _logFlushLogFile(false); - if (g_logBufferLength) goto end; + _logFlushLogFile(); + if (g_logBufferLength) return; } /* Nice and easy string formatting using the log buffer. */ @@ -333,12 +326,12 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, g_logBufferLength += log_str_len; } else { /* Flush log buffer. */ - _logFlushLogFile(false); - if (g_logBufferLength) goto end; + _logFlushLogFile(); + if (g_logBufferLength) return; /* Allocate memory for a temporary buffer. This will hold the formatted string. */ tmp_str = calloc(log_str_len + 1, sizeof(char)); - if (!tmp_str) goto end; + if (!tmp_str) return; /* Generate formatted string. */ sprintf(tmp_str, g_logStrFormat, ts->tm_year, ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, now.tv_nsec, func_name); @@ -366,20 +359,16 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name, #if LOG_FORCE_FLUSH == 1 /* Flush log buffer. */ - _logFlushLogFile(false); + _logFlushLogFile(); #endif end: if (tmp_str) free(tmp_str); - - if (lock) mutexUnlock(&g_logMutex); } -static void _logFlushLogFile(bool lock) +static void _logFlushLogFile(void) { - if (lock) mutexLock(&g_logMutex); - - if (!serviceIsActive(&(g_logFile.s)) || !g_logBuffer || !g_logBufferLength) goto end; + if (!serviceIsActive(&(g_logFile.s)) || !g_logBuffer || !g_logBufferLength) return; /* Write log buffer contents and flush the written data right away. */ Result rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logBuffer, g_logBufferLength, FsWriteOption_Flush); @@ -390,9 +379,6 @@ static void _logFlushLogFile(bool lock) *g_logBuffer = '\0'; g_logBufferLength = 0; } - -end: - if (lock) mutexUnlock(&g_logMutex); } static bool logAllocateLogBuffer(void) @@ -407,6 +393,8 @@ static bool logOpenLogFile(void) if (serviceIsActive(&(g_logFile.s))) return true; Result rc = 0; + bool use_root = true; + const char *launch_path = utilsGetLaunchPath(); char path[FS_MAX_PATH] = {0}, *ptr1 = NULL, *ptr2 = NULL; /* Get SD card FsFileSystem object. */ @@ -414,27 +402,22 @@ static bool logOpenLogFile(void) if (!sdmc_fs) return false; /* Generate logfile path. */ - if (g_appLaunchPath) + if (launch_path) { - ptr1 = strchr(g_appLaunchPath, '/'); - ptr2 = strrchr(g_appLaunchPath, '/'); + ptr1 = strchr(launch_path, '/'); + ptr2 = strrchr(launch_path, '/'); - if (ptr1 != ptr2) + if (ptr1 && ptr2 && ptr1 != ptr2) { /* Create logfile in the current working directory. */ - snprintf(path, sizeof(path), "%.*s", (int)((ptr2 - ptr1) + 1), ptr1); - - size_t path_len = strlen(path); - snprintf(path + path_len, sizeof(path) - path_len, LOG_FILE_NAME); - } else { - /* Create logfile in the SD card root directory. */ - sprintf(path, "/" LOG_FILE_NAME); + snprintf(path, sizeof(path), "%.*s" LOG_FILE_NAME, (int)((ptr2 - ptr1) + 1), ptr1); + use_root = false; } - } else { - /* Create logfile in the SD card root directory. */ - sprintf(path, "/" LOG_FILE_NAME); } + /* Create logfile in the SD card root directory. */ + if (use_root) sprintf(path, "/" LOG_FILE_NAME); + /* Create file. This will fail if the logfile exists, so we don't check its return value. */ fsFsCreateFile(sdmc_fs, path, 0, 0); diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index 3a3a9b3..01aae29 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -33,22 +33,26 @@ /* Global variables. */ -static bool g_resourcesInit = false, g_isDevUnit = false; +static bool g_resourcesInit = false; static Mutex g_resourcesMutex = 0; static FsFileSystem *g_sdCardFileSystem = NULL; -static FsStorage g_emmcBisSystemPartitionStorage = {0}; -static FATFS *g_emmcBisSystemPartitionFatFsObj = NULL; - -static AppletType g_programAppletType = 0; -static bool g_homeButtonBlocked = false; -static Mutex g_homeButtonMutex = 0; +static const char *g_appLaunchPath = NULL; static u8 g_customFirmwareType = UtilsCustomFirmwareType_Unknown; +static bool g_isDevUnit = false; + +static AppletType g_programAppletType = AppletType_None; + +static FsStorage g_emmcBisSystemPartitionStorage = {0}; +static FATFS *g_emmcBisSystemPartitionFatFsObj = NULL; + static AppletHookCookie g_systemOverclockCookie = {0}; +static bool g_homeButtonBlocked = false; + static int g_nxLinkSocketFd = -1; static const char *g_sizeSuffixes[] = { "B", "KiB", "MiB", "GiB" }; @@ -59,10 +63,14 @@ static const size_t g_illegalFileSystemCharsLength = (MAX_ELEMENTS(g_illegalFile /* Function prototypes. */ +static void _utilsGetLaunchPath(int program_argc, const char **program_argv); + static void _utilsGetCustomFirmwareType(void); static bool _utilsIsDevelopmentUnit(void); +static bool _utilsAppletModeCheck(void); + static bool utilsMountEmmcBisSystemPartitionStorage(void); static void utilsUnmountEmmcBisSystemPartitionStorage(void); @@ -70,129 +78,99 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param); static void utilsPrintConsoleError(void); -bool utilsInitializeResources(void) +bool utilsInitializeResources(const int program_argc, const char **program_argv) { - mutexLock(&g_resourcesMutex); - Result rc = 0; + bool ret = false; - bool ret = g_resourcesInit; - if (ret) goto end; - - /* Retrieve pointer to the application launch path. */ - if (g_argc && g_argv) + SCOPED_LOCK(&g_resourcesMutex) { - for(int i = 0; i < g_argc; i++) + ret = g_resourcesInit; + if (ret) break; + + /* Retrieve pointer to the application launch path. */ + _utilsGetLaunchPath(program_argc, program_argv); + + /* Retrieve pointer to the SD card FsFileSystem element. */ + if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"))) { - if (g_argv[i] && !strncmp(g_argv[i], "sdmc:/", 6)) - { - g_appLaunchPath = (const char*)g_argv[i]; - break; - } + LOG_MSG("Failed to retrieve FsFileSystem object for the SD card!"); + break; } + + /* Create logfile. */ + logWriteStringToLogFile("________________________________________________________________\r\n"); + LOG_MSG(APP_TITLE " v%u.%u.%u starting. Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + if (g_appLaunchPath) LOG_MSG("Launch path: \"%s\".", g_appLaunchPath); + + /* Log Horizon OS version. */ + u32 hos_version = hosversionGet(); + LOG_MSG("Horizon OS version: %u.%u.%u.", HOSVER_MAJOR(hos_version), HOSVER_MINOR(hos_version), HOSVER_MICRO(hos_version)); + + /* Initialize needed services. */ + if (!servicesInitialize()) break; + + /* Retrieve custom firmware type. */ + _utilsGetCustomFirmwareType(); + if (g_customFirmwareType != UtilsCustomFirmwareType_Unknown) LOG_MSG("Detected %s CFW.", (g_customFirmwareType == UtilsCustomFirmwareType_Atmosphere ? "Atmosphère" : \ + (g_customFirmwareType == UtilsCustomFirmwareType_SXOS ? "SX OS" : "ReiNX"))); + + /* Check if we're not running under a development unit. */ + if (!_utilsIsDevelopmentUnit()) break; + LOG_MSG("Running under %s unit.", g_isDevUnit ? "development" : "retail"); + + /* Get applet type. */ + g_programAppletType = appletGetAppletType(); + LOG_MSG("Running under %s mode.", _utilsAppletModeCheck() ? "applet" : "title override"); + + /* Initialize USB interface. */ + if (!usbInitialize()) break; + + /* Initialize USB Mass Storage interface. */ + if (!umsInitialize()) break; + + /* Load NCA keyset. */ + if (!keysLoadNcaKeyset()) break; + + /* Allocate NCA crypto buffer. */ + if (!ncaAllocateCryptoBuffer()) + { + LOG_MSG("Unable to allocate memory for NCA crypto buffer!"); + break; + } + + /* Initialize gamecard interface. */ + if (!gamecardInitialize()) break; + + /* Initialize title interface. */ + if (!titleInitialize()) break; + + /* Initialize BFTTF interface. */ + if (!bfttfInitialize()) break; + + /* Mount eMMC BIS System partition. */ + if (!utilsMountEmmcBisSystemPartitionStorage()) break; + + /* Disable screen dimming and auto sleep. */ + appletSetMediaPlaybackState(true); + + /* Overclock system. */ + utilsOverclockSystem(true); + + /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked). */ + appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL); + + /* Mount application RomFS. */ + romfsInit(); + + /* Redirect stdout and stderr over network to nxlink. */ + rc = socketInitializeDefault(); + if (R_SUCCEEDED(rc)) g_nxLinkSocketFd = nxlinkConnectToHost(true, true); + + /* Update flags. */ + ret = g_resourcesInit = true; } - /* Retrieve pointer to the SD card FsFileSystem element. */ - if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"))) goto end; - - /* Create logfile. */ - logWriteStringToLogFile("________________________________________________________________\r\n"); - LOG_MSG(APP_TITLE " v%u.%u.%u starting. Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); - if (g_appLaunchPath) LOG_MSG("Launch path: \"%s\".", g_appLaunchPath); - - /* Log Horizon OS version. */ - u32 hos_version = hosversionGet(); - LOG_MSG("Horizon OS version: %u.%u.%u.", HOSVER_MAJOR(hos_version), HOSVER_MINOR(hos_version), HOSVER_MICRO(hos_version)); - - /* Initialize needed services. */ - if (!servicesInitialize()) - { - LOG_MSG("Failed to initialize needed services!"); - goto end; - } - - /* Retrieve custom firmware type. */ - _utilsGetCustomFirmwareType(); - LOG_MSG("Detected %s CFW.", (g_customFirmwareType == UtilsCustomFirmwareType_Atmosphere ? "Atmosphère" : (g_customFirmwareType == UtilsCustomFirmwareType_SXOS ? "SX OS" : "ReiNX"))); - - /* Check if we're not running under a development unit. */ - if (!_utilsIsDevelopmentUnit()) goto end; - LOG_MSG("Running under %s unit.", g_isDevUnit ? "development" : "retail"); - - /* Get applet type. */ - g_programAppletType = appletGetAppletType(); - LOG_MSG("Running under %s mode.", utilsAppletModeCheck() ? "applet" : "title override"); - - /* Initialize USB interface. */ - if (!usbInitialize()) - { - LOG_MSG("Failed to initialize USB interface!"); - goto end; - } - - /* Initialize USB Mass Storage interface. */ - if (!umsInitialize()) goto end; - - /* Load NCA keyset. */ - if (!keysLoadNcaKeyset()) - { - LOG_MSG("Failed to load NCA keyset!"); - goto end; - } - - /* Allocate NCA crypto buffer. */ - if (!ncaAllocateCryptoBuffer()) - { - LOG_MSG("Unable to allocate memory for NCA crypto buffer!"); - goto end; - } - - /* Initialize gamecard interface. */ - if (!gamecardInitialize()) - { - LOG_MSG("Failed to initialize gamecard interface!"); - goto end; - } - - /* Initialize title interface. */ - if (!titleInitialize()) - { - LOG_MSG("Failed to initialize the title interface!"); - goto end; - } - - /* Initialize BFTTF interface. */ - if (!bfttfInitialize()) - { - LOG_MSG("Failed to initialize BFTTF interface!"); - goto end; - } - - /* Mount eMMC BIS System partition. */ - if (!utilsMountEmmcBisSystemPartitionStorage()) goto end; - - /* Disable screen dimming and auto sleep. */ - appletSetMediaPlaybackState(true); - - /* Overclock system. */ - utilsOverclockSystem(true); - - /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked). */ - appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL); - - /* Mount application RomFS. */ - romfsInit(); - - /* Redirect stdout and stderr over network to nxlink. */ - rc = socketInitializeDefault(); - if (R_SUCCEEDED(rc)) g_nxLinkSocketFd = nxlinkConnectToHost(true, true); - - /* Update flags. */ - ret = g_resourcesInit = true; - -end: - mutexUnlock(&g_resourcesMutex); - if (!ret) utilsPrintConsoleError(); return ret; @@ -200,62 +178,121 @@ end: void utilsCloseResources(void) { - mutexLock(&g_resourcesMutex); - - /* Close nxlink socket. */ - if (g_nxLinkSocketFd >= 0) + SCOPED_LOCK(&g_resourcesMutex) { - close(g_nxLinkSocketFd); - g_nxLinkSocketFd = -1; + /* Close nxlink socket. */ + if (g_nxLinkSocketFd >= 0) + { + close(g_nxLinkSocketFd); + g_nxLinkSocketFd = -1; + } + + socketExit(); + + /* Unmount application RomFS. */ + romfsExit(); + + /* Unset our overclock applet hook. */ + appletUnhook(&g_systemOverclockCookie); + + /* Restore hardware clocks. */ + utilsOverclockSystem(false); + + /* Enable screen dimming and auto sleep. */ + appletSetMediaPlaybackState(false); + + /* Unblock HOME button presses. */ + utilsChangeHomeButtonBlockStatus(false); + + /* Unmount eMMC BIS System partition. */ + utilsUnmountEmmcBisSystemPartitionStorage(); + + /* Deinitialize BFTTF interface. */ + bfttfExit(); + + /* Deinitialize title interface. */ + titleExit(); + + /* Deinitialize gamecard interface. */ + gamecardExit(); + + /* Free NCA crypto buffer. */ + ncaFreeCryptoBuffer(); + + /* Close USB Mass Storage interface. */ + umsExit(); + + /* Close USB interface. */ + usbExit(); + + /* Close initialized services. */ + servicesClose(); + + /* Close logfile. */ + logCloseLogFile(); + + g_resourcesInit = false; + } +} + +const char *utilsGetLaunchPath(void) +{ + return g_appLaunchPath; +} + +FsFileSystem *utilsGetSdCardFileSystemObject(void) +{ + return g_sdCardFileSystem; +} + +bool utilsCommitSdCardFileSystemChanges(void) +{ + return (g_sdCardFileSystem ? R_SUCCEEDED(fsFsCommit(g_sdCardFileSystem)) : false); +} + +u8 utilsGetCustomFirmwareType(void) +{ + return g_customFirmwareType; +} + +bool utilsIsDevelopmentUnit(void) +{ + return g_isDevUnit; +} + +bool utilsAppletModeCheck(void) +{ + return _utilsAppletModeCheck(); +} + +FsStorage *utilsGetEmmcBisSystemPartitionStorage(void) +{ + return &g_emmcBisSystemPartitionStorage; +} + +void utilsOverclockSystem(bool overclock) +{ + u32 cpu_rate = ((overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL) * 1000000); + u32 mem_rate = ((overclock ? MEM_CLKRT_OVERCLOCKED : MEM_CLKRT_NORMAL) * 1000000); + servicesChangeHardwareClockRates(cpu_rate, mem_rate); +} + +void utilsChangeHomeButtonBlockStatus(bool block) +{ + SCOPED_LOCK(&g_resourcesMutex) + { + /* Only change HOME button blocking status if we're running as a regular application or a system application, and if its current blocking status is different than the requested one. */ + if (_utilsAppletModeCheck() || block == g_homeButtonBlocked) break; + + if (block) + { + appletBeginBlockingHomeButtonShortAndLongPressed(0); + } else { + appletEndBlockingHomeButtonShortAndLongPressed(); + } + + g_homeButtonBlocked = block; } - - socketExit(); - - /* Unmount application RomFS. */ - romfsExit(); - - /* Unset our overclock applet hook. */ - appletUnhook(&g_systemOverclockCookie); - - /* Restore hardware clocks. */ - utilsOverclockSystem(false); - - /* Enable screen dimming and auto sleep. */ - appletSetMediaPlaybackState(false); - - /* Unblock HOME button presses. */ - utilsChangeHomeButtonBlockStatus(false); - - /* Unmount eMMC BIS System partition. */ - utilsUnmountEmmcBisSystemPartitionStorage(); - - /* Deinitialize BFTTF interface. */ - bfttfExit(); - - /* Deinitialize title interface. */ - titleExit(); - - /* Deinitialize gamecard interface. */ - gamecardExit(); - - /* Free NCA crypto buffer. */ - ncaFreeCryptoBuffer(); - - /* Close USB Mass Storage interface. */ - umsExit(); - - /* Close USB interface. */ - usbExit(); - - /* Close initialized services. */ - servicesClose(); - - /* Close logfile. */ - logCloseLogFile(); - - g_resourcesInit = false; - - mutexUnlock(&g_resourcesMutex); } bool utilsCreateThread(Thread *out_thread, ThreadFunc func, void *arg, int cpu_id) @@ -336,14 +373,6 @@ void utilsJoinThread(Thread *thread) memset(thread, 0, sizeof(Thread)); } -bool utilsIsDevelopmentUnit(void) -{ - mutexLock(&g_resourcesMutex); - bool ret = (g_resourcesInit && g_isDevUnit); - mutexUnlock(&g_resourcesMutex); - return ret; -} - __attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...) { if (!dst || !dst_size || (!*dst && *dst_size) || (*dst && !*dst_size) || !fmt || !*fmt) @@ -510,18 +539,6 @@ bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_fr return true; } -FsFileSystem *utilsGetSdCardFileSystemObject(void) -{ - return g_sdCardFileSystem; -} - -bool utilsCommitSdCardFileSystemChanges(void) -{ - if (!g_sdCardFileSystem) return false; - Result rc = fsFsCommit(g_sdCardFileSystem); - return R_SUCCEEDED(rc); -} - bool utilsCheckIfFileExists(const char *path) { if (!path || !*path) return false; @@ -610,46 +627,18 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex return path; } -bool utilsAppletModeCheck(void) +static void _utilsGetLaunchPath(int program_argc, const char **program_argv) { - return (g_programAppletType != AppletType_Application && g_programAppletType != AppletType_SystemApplication); -} - -void utilsChangeHomeButtonBlockStatus(bool block) -{ - mutexLock(&g_homeButtonMutex); + if (program_argc <= 0 || !program_argv) return; - /* Only change HOME button blocking status if we're running as a regular application or a system application, and if it's current blocking status is different than the requested one. */ - if (!utilsAppletModeCheck() && block != g_homeButtonBlocked) + for(int i = 0; i < program_argc; i++) { - if (block) + if (program_argv[i] && !strncmp(program_argv[i], "sdmc:/", 6)) { - appletBeginBlockingHomeButtonShortAndLongPressed(0); - } else { - appletEndBlockingHomeButtonShortAndLongPressed(); + g_appLaunchPath = program_argv[i]; + break; } - - g_homeButtonBlocked = block; } - - mutexUnlock(&g_homeButtonMutex); -} - -u8 utilsGetCustomFirmwareType(void) -{ - return g_customFirmwareType; -} - -FsStorage *utilsGetEmmcBisSystemPartitionStorage(void) -{ - return &g_emmcBisSystemPartitionStorage; -} - -void utilsOverclockSystem(bool overclock) -{ - u32 cpu_rate = ((overclock ? CPU_CLKRT_OVERCLOCKED : CPU_CLKRT_NORMAL) * 1000000); - u32 mem_rate = ((overclock ? MEM_CLKRT_OVERCLOCKED : MEM_CLKRT_NORMAL) * 1000000); - servicesChangeHardwareClockRates(cpu_rate, mem_rate); } static void _utilsGetCustomFirmwareType(void) @@ -676,6 +665,11 @@ static bool _utilsIsDevelopmentUnit(void) return R_SUCCEEDED(rc); } +static bool _utilsAppletModeCheck(void) +{ + return (g_programAppletType > AppletType_Application && g_programAppletType < AppletType_SystemApplication); +} + static bool utilsMountEmmcBisSystemPartitionStorage(void) { Result rc = 0; diff --git a/source/core/services.c b/source/core/services.c index daaec8c..c05cbdf 100644 --- a/source/core/services.c +++ b/source/core/services.c @@ -39,7 +39,7 @@ typedef struct { /* Function prototypes. */ -static bool _servicesCheckInitializedServiceByName(const char *name, bool lock); +static bool _servicesCheckInitializedServiceByName(const char *name); static Result servicesAtmosphereHasService(bool *out, SmServiceName name); static Result servicesGetExosphereApiVersion(u32 *out); @@ -82,134 +82,123 @@ static const u32 g_atmosphereTipcVersion = MAKEHOSVERSION(0, 19, 0); bool servicesInitialize(void) { - mutexLock(&g_servicesMutex); - - Result rc = 0; bool ret = true; - for(u32 i = 0; i < g_serviceInfoCount; i++) + SCOPED_LOCK(&g_servicesMutex) { - ServiceInfo *service_info = &(g_serviceInfo[i]); - - /* Check if this service has been already initialized. */ - if (service_info->initialized) continue; - - /* Check if this service depends on a condition function. */ - if (service_info->cond_func != NULL) + for(u32 i = 0; i < g_serviceInfoCount; i++) { - /* Run the condition function - it will update the current service member. */ - /* Skip this service if the required conditions aren't met. */ - if (!service_info->cond_func(service_info)) continue; + ServiceInfo *service_info = &(g_serviceInfo[i]); + + /* Check if this service has been already initialized. */ + if (service_info->initialized) continue; + + /* Check if this service depends on a condition function. */ + if (service_info->cond_func != NULL) + { + /* Run the condition function - it will update the current service member. */ + /* Skip this service if the required conditions aren't met. */ + if (!service_info->cond_func(service_info)) continue; + } + + /* Check if this service actually has a valid initialization function. */ + if (service_info->init_func == NULL) continue; + + /* Initialize service. */ + Result rc = service_info->init_func(); + if (R_FAILED(rc)) + { + LOG_MSG("Failed to initialize \"%s\" service! (0x%08X).", service_info->name, rc); + ret = false; + break; + } + + /* Update flag. */ + service_info->initialized = true; } - - /* Check if this service actually has a valid initialization function. */ - if (service_info->init_func == NULL) continue; - - /* Initialize service. */ - rc = service_info->init_func(); - if (R_FAILED(rc)) - { - LOG_MSG("Failed to initialize \"%s\" service! (0x%08X).", service_info->name, rc); - ret = false; - break; - } - - /* Update flag. */ - service_info->initialized = true; } - mutexUnlock(&g_servicesMutex); - return ret; } void servicesClose(void) { - mutexLock(&g_servicesMutex); - - for(u32 i = 0; i < g_serviceInfoCount; i++) + SCOPED_LOCK(&g_servicesMutex) { - ServiceInfo *service_info = &(g_serviceInfo[i]); - - /* Check if this service has not been initialized, or if it doesn't have a valid close function. */ - if (!service_info->initialized || service_info->close_func == NULL) continue; - - /* Close service. */ - service_info->close_func(); - - /* Update flag. */ - service_info->initialized = false; + for(u32 i = 0; i < g_serviceInfoCount; i++) + { + ServiceInfo *service_info = &(g_serviceInfo[i]); + + /* Check if this service has not been initialized, or if it doesn't have a valid close function. */ + if (!service_info->initialized || service_info->close_func == NULL) continue; + + /* Close service. */ + service_info->close_func(); + + /* Update flag. */ + service_info->initialized = false; + } } - - mutexUnlock(&g_servicesMutex); } bool servicesCheckInitializedServiceByName(const char *name) { - return _servicesCheckInitializedServiceByName(name, true); + bool ret = false; + SCOPED_LOCK(&g_servicesMutex) ret = _servicesCheckInitializedServiceByName(name); + return ret; } bool servicesCheckRunningServiceByName(const char *name) { - Result rc = 0; - bool out = false; - SmServiceName service_name = {0}; + bool ret = false; - mutexLock(&g_servicesMutex); - - if (!name || !*name || !_servicesCheckInitializedServiceByName("spl:", false)) + SCOPED_LOCK(&g_servicesMutex) { - LOG_MSG("Invalid parameters!"); - goto end; + if (!name || !*name || !_servicesCheckInitializedServiceByName("spl:")) + { + LOG_MSG("Invalid parameters!"); + break; + } + + Result rc = servicesAtmosphereHasService(&ret, smEncodeName(name)); + if (R_FAILED(rc)) LOG_MSG("servicesAtmosphereHasService failed for \"%s\"! (0x%08X).", name, rc); } - service_name = smEncodeName(name); - - rc = servicesAtmosphereHasService(&out, service_name); - if (R_FAILED(rc)) LOG_MSG("servicesAtmosphereHasService failed for \"%s\"! (0x%08X).", name, rc); - -end: - mutexUnlock(&g_servicesMutex); - - return out; + return ret; } void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate) { - Result rc1 = 0, rc2 = 0; - - mutexLock(&g_servicesMutex); - - if ((g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("pcv", false)) || (!g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("clkrst", false))) + SCOPED_LOCK(&g_servicesMutex) { - LOG_MSG("Error: clock service uninitialized."); - goto end; + if ((g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("pcv")) || (!g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("clkrst"))) + { + LOG_MSG("Error: clock service uninitialized."); + break; + } + + Result rc1 = 0, rc2 = 0; + + if (g_clkSvcUsePcv) + { + rc1 = pcvSetClockRate(PcvModule_CpuBus, cpu_rate); + rc2 = pcvSetClockRate(PcvModule_EMC, mem_rate); + } else { + rc1 = clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate); + rc2 = clkrstSetClockRate(&g_clkrstMemSession, mem_rate); + } + + if (R_FAILED(rc1)) LOG_MSG("%sSetClockRate failed! (0x%08X) (CPU).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc1); + if (R_FAILED(rc2)) LOG_MSG("%sSetClockRate failed! (0x%08X) (MEM).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc2); } - - if (g_clkSvcUsePcv) - { - rc1 = pcvSetClockRate(PcvModule_CpuBus, cpu_rate); - rc2 = pcvSetClockRate(PcvModule_EMC, mem_rate); - } else { - rc1 = clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate); - rc2 = clkrstSetClockRate(&g_clkrstMemSession, mem_rate); - } - - if (R_FAILED(rc1)) LOG_MSG("%sSetClockRate failed! (0x%08X) (CPU).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc1); - if (R_FAILED(rc2)) LOG_MSG("%sSetClockRate failed! (0x%08X) (MEM).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc2); - -end: - mutexUnlock(&g_servicesMutex); } -static bool _servicesCheckInitializedServiceByName(const char *name, bool lock) +static bool _servicesCheckInitializedServiceByName(const char *name) { + if (!name || !*name) return false; + bool ret = false; - if (lock) mutexLock(&g_servicesMutex); - - if (!name || !*name) goto end; - for(u32 i = 0; i < g_serviceInfoCount; i++) { ServiceInfo *service_info = &(g_serviceInfo[i]); @@ -221,9 +210,6 @@ static bool _servicesCheckInitializedServiceByName(const char *name, bool lock) } } -end: - if (lock) mutexUnlock(&g_servicesMutex); - return ret; } diff --git a/source/core/title.c b/source/core/title.c index b1dbe8f..454b869 100644 --- a/source/core/title.c +++ b/source/core/title.c @@ -428,7 +428,7 @@ static bool titleRefreshGameCardTitleInfo(void); static void titleRemoveGameCardTitleInfoEntries(void); static bool titleIsUserApplicationContentAvailable(u64 app_id); -static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id, bool lock); +static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id); static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b); static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b); @@ -436,114 +436,114 @@ static int titleOrphanTitleInfoSortFunction(const void *a, const void *b); bool titleInitialize(void) { - mutexLock(&g_titleMutex); + bool ret = false; - bool ret = g_titleInterfaceInit; - if (ret) goto end; - - /* Allocate memory for the ns application control data. */ - /* This will be used each time we need to retrieve the metadata from an application. */ - g_nsAppControlData = calloc(1, sizeof(NsApplicationControlData)); - if (!g_nsAppControlData) + SCOPED_LOCK(&g_titleMutex) { - LOG_MSG("Failed to allocate memory for the ns application control data!"); - goto end; + ret = g_titleInterfaceInit; + if (ret) break; + + /* Allocate memory for the ns application control data. */ + /* This will be used each time we need to retrieve the metadata from an application. */ + g_nsAppControlData = calloc(1, sizeof(NsApplicationControlData)); + if (!g_nsAppControlData) + { + LOG_MSG("Failed to allocate memory for the ns application control data!"); + break; + } + + /* Generate application metadata entries from hardcoded system titles, since we can't retrieve their names via ns. */ + if (!titleGenerateMetadataEntriesFromSystemTitles()) + { + LOG_MSG("Failed to generate application metadata from hardcoded system titles!"); + break; + } + + /* Generate application metadata entries from ns records. */ + /* Theoretically speaking, we should only need to do this once. */ + /* However, if any new gamecard is inserted while the application is running, we *will* have to retrieve the metadata from its application(s). */ + if (!titleGenerateMetadataEntriesFromNsRecords()) + { + LOG_MSG("Failed to generate application metadata from ns records!"); + break; + } + + /* Open eMMC System, eMMC User and SD card ncm databases. */ + if (!titleOpenNcmDatabases()) + { + LOG_MSG("Failed to open ncm databases!"); + break; + } + + /* Open eMMC System, eMMC User and SD card ncm storages. */ + if (!titleOpenNcmStorages()) + { + LOG_MSG("Failed to open ncm storages!"); + break; + } + + /* Load title info by retrieving content meta keys from available eMMC System, eMMC User and SD card titles. */ + if (!titleLoadPersistentStorageTitleInfo()) + { + LOG_MSG("Failed to load persistent storage title info!"); + break; + } + + /* Create user-mode exit event. */ + ueventCreate(&g_titleGameCardInfoThreadExitEvent, true); + + /* Retrieve gamecard status change user event. */ + g_titleGameCardStatusChangeUserEvent = gamecardGetStatusChangeUserEvent(); + if (!g_titleGameCardStatusChangeUserEvent) + { + LOG_MSG("Failed to retrieve gamecard status change user event!"); + break; + } + + /* Create user-mode gamecard update info event. */ + ueventCreate(&g_titleGameCardUpdateInfoUserEvent, true); + + /* Create gamecard title info thread. */ + if (!(g_titleGameCardInfoThreadCreated = titleCreateGameCardInfoThread())) break; + + /* Update flags. */ + ret = g_titleInterfaceInit = true; } - /* Generate application metadata entries from hardcoded system titles, since we can't retrieve their names via ns. */ - if (!titleGenerateMetadataEntriesFromSystemTitles()) - { - LOG_MSG("Failed to generate application metadata from hardcoded system titles!"); - goto end; - } - - /* Generate application metadata entries from ns records. */ - /* Theoretically speaking, we should only need to do this once. */ - /* However, if any new gamecard is inserted while the application is running, we *will* have to retrieve the metadata from its application(s). */ - if (!titleGenerateMetadataEntriesFromNsRecords()) - { - LOG_MSG("Failed to generate application metadata from ns records!"); - goto end; - } - - /* Open eMMC System, eMMC User and SD card ncm databases. */ - if (!titleOpenNcmDatabases()) - { - LOG_MSG("Failed to open ncm databases!"); - goto end; - } - - /* Open eMMC System, eMMC User and SD card ncm storages. */ - if (!titleOpenNcmStorages()) - { - LOG_MSG("Failed to open ncm storages!"); - goto end; - } - - /* Load title info by retrieving content meta keys from available eMMC System, eMMC User and SD card titles. */ - if (!titleLoadPersistentStorageTitleInfo()) - { - LOG_MSG("Failed to load persistent storage title info!"); - goto end; - } - - /* Create user-mode exit event. */ - ueventCreate(&g_titleGameCardInfoThreadExitEvent, true); - - /* Retrieve gamecard status change user event. */ - g_titleGameCardStatusChangeUserEvent = gamecardGetStatusChangeUserEvent(); - if (!g_titleGameCardStatusChangeUserEvent) - { - LOG_MSG("Failed to retrieve gamecard status change user event!"); - goto end; - } - - /* Create user-mode gamecard update info event. */ - ueventCreate(&g_titleGameCardUpdateInfoUserEvent, true); - - /* Create gamecard title info thread. */ - if (!(g_titleGameCardInfoThreadCreated = titleCreateGameCardInfoThread())) goto end; - - ret = g_titleInterfaceInit = true; - -end: - mutexUnlock(&g_titleMutex); - return ret; } void titleExit(void) { - mutexLock(&g_titleMutex); - - /* Destroy gamecard detection thread. */ - if (g_titleGameCardInfoThreadCreated) + SCOPED_LOCK(&g_titleMutex) { - titleDestroyGameCardInfoThread(); - g_titleGameCardInfoThreadCreated = false; + /* Destroy gamecard detection thread. */ + if (g_titleGameCardInfoThreadCreated) + { + titleDestroyGameCardInfoThread(); + g_titleGameCardInfoThreadCreated = false; + } + + /* Free title info. */ + titleFreeTitleInfo(); + + /* Close gamecard ncm database and storage (if needed). */ + titleCloseNcmDatabaseAndStorageFromGameCard(); + + /* Close eMMC System, eMMC User and SD card ncm storages. */ + titleCloseNcmStorages(); + + /* Close eMMC System, eMMC User and SD card ncm databases. */ + titleCloseNcmDatabases(); + + /* Free application metadata. */ + titleFreeApplicationMetadata(); + + /* Free ns application control data. */ + if (g_nsAppControlData) free(g_nsAppControlData); + + g_titleInterfaceInit = false; } - - /* Free title info. */ - titleFreeTitleInfo(); - - /* Close gamecard ncm database and storage (if needed). */ - titleCloseNcmDatabaseAndStorageFromGameCard(); - - /* Close eMMC System, eMMC User and SD card ncm storages. */ - titleCloseNcmStorages(); - - /* Close eMMC System, eMMC User and SD card ncm databases. */ - titleCloseNcmDatabases(); - - /* Free application metadata. */ - titleFreeApplicationMetadata(); - - /* Free ns application control data. */ - if (g_nsAppControlData) free(g_nsAppControlData); - - g_titleInterfaceInit = false; - - mutexUnlock(&g_titleMutex); } NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id) @@ -598,296 +598,291 @@ NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id) TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count) { - mutexLock(&g_titleMutex); - - u32 start_idx = (is_system ? 0 : g_systemTitlesCount); - u32 max_val = (is_system ? g_systemTitlesCount : g_appMetadataCount); - u32 app_count = 0; TitleApplicationMetadata **app_metadata = NULL, **tmp_app_metadata = NULL; - if (!g_titleInterfaceInit || !g_appMetadata || !*g_appMetadata || (is_system && g_appMetadataCount < g_systemTitlesCount) || (!is_system && g_appMetadataCount == g_systemTitlesCount) || !out_count) + SCOPED_LOCK(&g_titleMutex) { - LOG_MSG("Invalid parameters!"); - goto end; - } - - for(u32 i = start_idx; i < max_val; i++) - { - TitleApplicationMetadata *cur_app_metadata = g_appMetadata[i]; - if (!cur_app_metadata) continue; - - /* Skip current metadata entry if content data for this title isn't available. */ - if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id, false)) || \ - (!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue; - - /* Reallocate application metadata pointer array. */ - tmp_app_metadata = realloc(app_metadata, (app_count + 1) * sizeof(TitleApplicationMetadata*)); - if (!tmp_app_metadata) + if (!g_titleInterfaceInit || !g_appMetadata || !*g_appMetadata || (is_system && g_appMetadataCount < g_systemTitlesCount) || (!is_system && g_appMetadataCount == g_systemTitlesCount) || \ + !out_count) { - LOG_MSG("Failed to reallocate application metadata pointer array!"); - if (app_metadata) free(app_metadata); - app_metadata = NULL; - goto end; + LOG_MSG("Invalid parameters!"); + break; } - app_metadata = tmp_app_metadata; - tmp_app_metadata = NULL; + u32 start_idx = (is_system ? 0 : g_systemTitlesCount), max_val = (is_system ? g_systemTitlesCount : g_appMetadataCount); + bool error = false; - /* Set current pointer and increase counter. */ - app_metadata[app_count++] = cur_app_metadata; + for(u32 i = start_idx; i < max_val; i++) + { + TitleApplicationMetadata *cur_app_metadata = g_appMetadata[i]; + if (!cur_app_metadata) continue; + + /* Skip current metadata entry if content data for this title isn't available. */ + if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id)) || \ + (!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue; + + /* Reallocate application metadata pointer array. */ + tmp_app_metadata = realloc(app_metadata, (app_count + 1) * sizeof(TitleApplicationMetadata*)); + if (!tmp_app_metadata) + { + LOG_MSG("Failed to reallocate application metadata pointer array!"); + if (app_metadata) free(app_metadata); + app_metadata = NULL; + error = true; + break; + } + + app_metadata = tmp_app_metadata; + tmp_app_metadata = NULL; + + /* Set current pointer and increase counter. */ + app_metadata[app_count++] = cur_app_metadata; + } + + if (error) break; + + /* Update output counter. */ + *out_count = app_count; + + if (!app_metadata || !app_count) LOG_MSG("No content data found for %s!", is_system ? "system titles" : "user applications"); } - /* Update output counter. */ - *out_count = app_count; - - if (!app_metadata || !app_count) LOG_MSG("No content data found for %s!", is_system ? "system titles" : "user applications"); - -end: - mutexUnlock(&g_titleMutex); - return app_metadata; } TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id) { - return _titleGetInfoFromStorageByTitleId(storage_id, title_id, true); + TitleInfo *ret = NULL; + SCOPED_LOCK(&g_titleMutex) ret = _titleGetInfoFromStorageByTitleId(storage_id, title_id); + return ret; } bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out) { - mutexLock(&g_titleMutex); + bool ret = false; - bool success = false; - - if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !app_id || !out) + SCOPED_LOCK(&g_titleMutex) { - LOG_MSG("Invalid parameters!"); - goto end; - } - - /* Clear output. */ - memset(out, 0, sizeof(TitleUserApplicationData)); - - /* Get first user application title info. */ - out->app_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id, false); - - /* Get first patch title info. */ - out->patch_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, titleGetPatchIdByApplicationId(app_id), false); - - /* Get first add-on content title info. */ - for(u32 i = g_titleInfoBuiltInUserStartIndex; i < g_titleInfoCount; i++) - { - TitleInfo *title_info = g_titleInfo[i]; - if (title_info && title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id)) + if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !app_id || !out) { - out->aoc_info = title_info; + LOG_MSG("Invalid parameters!"); break; } + + /* Clear output. */ + memset(out, 0, sizeof(TitleUserApplicationData)); + + /* Get info for the first user application title. */ + out->app_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, app_id); + + /* Get info for the first patch title. */ + out->patch_info = _titleGetInfoFromStorageByTitleId(NcmStorageId_Any, titleGetPatchIdByApplicationId(app_id)); + + /* Get info for the first add-on content title. */ + for(u32 i = g_titleInfoBuiltInUserStartIndex; i < g_titleInfoCount; i++) + { + TitleInfo *title_info = g_titleInfo[i]; + if (title_info && title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id)) + { + out->aoc_info = title_info; + break; + } + } + + /* Check retrieved title info. */ + ret = (out->app_info || out->patch_info || out->aoc_info); + if (!ret) LOG_MSG("Failed to retrieve user application data for ID \"%016lX\"!", app_id); } - /* Check retrieved title info. */ - success = (out->app_info || out->patch_info || out->aoc_info); - if (!success) - { - LOG_MSG("Failed to retrieve user application data for ID \"%016lX\"!", app_id); - goto end; - } - -end: - mutexUnlock(&g_titleMutex); - - return success; + return ret; } bool titleAreOrphanTitlesAvailable(void) { - mutexLock(&g_titleMutex); - bool ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0); - mutexUnlock(&g_titleMutex); + bool ret = false; + SCOPED_LOCK(&g_titleMutex) ret = (g_titleInterfaceInit && g_orphanTitleInfo && *g_orphanTitleInfo && g_orphanTitleInfoCount > 0); return ret; } TitleInfo **titleGetInfoFromOrphanTitles(u32 *out_count) { - mutexLock(&g_titleMutex); - TitleInfo **orphan_info = NULL; - if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_orphanTitleInfo || !*g_orphanTitleInfo || !g_orphanTitleInfoCount || !out_count) + SCOPED_LOCK(&g_titleMutex) { - LOG_MSG("Invalid parameters!"); - goto end; + if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_orphanTitleInfo || !*g_orphanTitleInfo || !g_orphanTitleInfoCount || !out_count) + { + LOG_MSG("Invalid parameters!"); + break; + } + + /* Allocate orphan title info pointer array. */ + orphan_info = calloc(g_orphanTitleInfoCount, sizeof(TitleInfo*)); + if (!orphan_info) + { + LOG_MSG("Failed to allocate memory for orphan title info pointer array!"); + break; + } + + /* Get pointers to orphan title info entries. */ + for(u32 i = 0; i < g_orphanTitleInfoCount; i++) orphan_info[i] = g_orphanTitleInfo[i]; + + /* Update output counter. */ + *out_count = g_orphanTitleInfoCount; } - /* Allocate orphan title info pointer array. */ - orphan_info = calloc(g_orphanTitleInfoCount, sizeof(TitleInfo*)); - if (!orphan_info) - { - LOG_MSG("Failed to allocate memory for orphan title info pointer array!"); - goto end; - } - - /* Get pointers to orphan title info entries. */ - for(u32 i = 0; i < g_orphanTitleInfoCount; i++) orphan_info[i] = g_orphanTitleInfo[i]; - - /* Update output counter. */ - *out_count = g_orphanTitleInfoCount; - -end: - mutexUnlock(&g_titleMutex); - return orphan_info; } bool titleIsGameCardInfoUpdated(void) { - mutexLock(&g_titleMutex); + bool ret = false; - /* Check if the gamecard thread detected a gamecard status change. */ - bool ret = (g_titleInterfaceInit && g_titleGameCardInfoThreadCreated && g_titleGameCardInfoUpdated); - if (!ret) goto end; + SCOPED_LOCK(&g_titleMutex) + { + /* Check if the gamecard thread detected a gamecard status change. */ + ret = (g_titleInterfaceInit && g_titleGameCardInfoThreadCreated && g_titleGameCardInfoUpdated); + if (!ret) break; + + /* Signal the gamecard update info user event. */ + ueventSignal(&g_titleGameCardUpdateInfoUserEvent); + + /* Wait for the gamecard thread to wake us up. */ + condvarWait(&g_gameCardCondVar, &g_titleMutex); + + /* Update output value and gamecard info updated flag (if needed). */ + ret = g_titleGameCardInfoUpdated; + if (ret) g_titleGameCardInfoUpdated = false; + } - /* Signal the gamecard update info user event. */ - ueventSignal(&g_titleGameCardUpdateInfoUserEvent); - - /* Wait for the gamecard thread to wake us up. */ - condvarWait(&g_gameCardCondVar, &g_titleMutex); - - /* Update output value and gamecard info updated flag (if needed). */ - ret = g_titleGameCardInfoUpdated; - if (ret) g_titleGameCardInfoUpdated = false; - -end: - mutexUnlock(&g_titleMutex); return ret; } char *titleGenerateFileName(const TitleInfo *title_info, u8 name_convention, u8 illegal_char_replace_type) { - mutexLock(&g_titleMutex); - - char *filename = NULL; - char title_name[0x400] = {0}; - if (!title_info || title_info->meta_key.type < NcmContentMetaType_Application || title_info->meta_key.type > NcmContentMetaType_Delta || name_convention > TitleFileNameConvention_IdAndVersionOnly || \ (name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) { LOG_MSG("Invalid parameters!"); - goto end; + return NULL; } - u8 type = (title_info->meta_key.type - 0x80); + char *filename = NULL; - /* Generate filename for this title. */ - if (name_convention == TitleFileNameConvention_Full) + SCOPED_LOCK(&g_titleMutex) { - if (title_info->app_metadata && *(title_info->app_metadata->lang_entry.name)) + char title_name[0x400] = {0}; + u8 type = (title_info->meta_key.type - 0x80); + + /* Generate filename for this title. */ + if (name_convention == TitleFileNameConvention_Full) { - sprintf(title_name, "%s ", title_info->app_metadata->lang_entry.name); - if (illegal_char_replace_type) utilsReplaceIllegalCharacters(title_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); + if (title_info->app_metadata && *(title_info->app_metadata->lang_entry.name)) + { + sprintf(title_name, "%s ", title_info->app_metadata->lang_entry.name); + if (illegal_char_replace_type) utilsReplaceIllegalCharacters(title_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); + } + + sprintf(title_name + strlen(title_name), "[%016lX][v%u][%s]", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]); + } else + if (name_convention == TitleFileNameConvention_IdAndVersionOnly) + { + sprintf(title_name, "%016lX_v%u_%s", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]); } - sprintf(title_name + strlen(title_name), "[%016lX][v%u][%s]", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]); - } else - if (name_convention == TitleFileNameConvention_IdAndVersionOnly) - { - sprintf(title_name, "%016lX_v%u_%s", title_info->meta_key.id, title_info->meta_key.version, g_filenameTypeStrings[type]); + /* Duplicate generated filename. */ + filename = strdup(title_name); + if (!filename) LOG_MSG("Failed to duplicate generated filename!"); } - /* Duplicate generated filename. */ - filename = strdup(title_name); - if (!filename) LOG_MSG("Failed to duplicate generated filename!"); - -end: - mutexUnlock(&g_titleMutex); - return filename; } char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_type) { - mutexLock(&g_titleMutex); + char *filename = NULL; - size_t cur_filename_len = 0; - char *filename = NULL, *tmp_filename = NULL; - char app_name[0x400] = {0}; - - if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_titleGameCardAvailable || !g_titleInfoGameCardCount || \ - name_convention > TitleFileNameConvention_IdAndVersionOnly || (name_convention == TitleFileNameConvention_Full && \ - illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) + SCOPED_LOCK(&g_titleMutex) { - LOG_MSG("Invalid parameters!"); - goto end; - } - - for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++) - { - TitleInfo *app_info = g_titleInfo[i]; - if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue; - - u32 app_version = app_info->meta_key.version; - - /* Check if the inserted gamecard holds any bundled patches for the current user application. */ - /* If so, we'll use the highest patch version available as part of the filename. */ - for(u32 j = g_titleInfoGameCardStartIndex; j < g_titleInfoCount; j++) + if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_titleGameCardAvailable || !g_titleInfoGameCardCount || \ + name_convention > TitleFileNameConvention_IdAndVersionOnly || (name_convention == TitleFileNameConvention_Full && \ + illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) { - if (j == i) continue; - - TitleInfo *patch_info = g_titleInfo[j]; - if (!patch_info || patch_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, patch_info->meta_key.id) || \ - patch_info->meta_key.version <= app_version) continue; - - app_version = patch_info->meta_key.version; + LOG_MSG("Invalid parameters!"); + break; } - /* Generate current user application name. */ - *app_name = '\0'; + size_t cur_filename_len = 0; + char *tmp_filename = NULL, app_name[0x400] = {0}; + bool error = false; - if (name_convention == TitleFileNameConvention_Full) + for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++) { - if (cur_filename_len) strcat(app_name, " + "); + TitleInfo *app_info = g_titleInfo[i]; + if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue; - if (app_info->app_metadata && *(app_info->app_metadata->lang_entry.name)) + u32 app_version = app_info->meta_key.version; + + /* Check if the inserted gamecard holds any bundled patches for the current user application. */ + /* If so, we'll use the highest patch version available as part of the filename. */ + for(u32 j = g_titleInfoGameCardStartIndex; j < g_titleInfoCount; j++) { - sprintf(app_name + strlen(app_name), "%s ", app_info->app_metadata->lang_entry.name); - if (illegal_char_replace_type) utilsReplaceIllegalCharacters(app_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); + if (j == i) continue; + + TitleInfo *patch_info = g_titleInfo[j]; + if (!patch_info || patch_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, patch_info->meta_key.id) || \ + patch_info->meta_key.version <= app_version) continue; + + app_version = patch_info->meta_key.version; } - sprintf(app_name + strlen(app_name), "[%016lX][v%u]", app_info->meta_key.id, app_version); - } else - if (name_convention == TitleFileNameConvention_IdAndVersionOnly) - { - if (cur_filename_len) strcat(app_name, "+"); - sprintf(app_name + strlen(app_name), "%016lX_v%u", app_info->meta_key.id, app_version); + /* Generate current user application name. */ + *app_name = '\0'; + + if (name_convention == TitleFileNameConvention_Full) + { + if (cur_filename_len) strcat(app_name, " + "); + + if (app_info->app_metadata && *(app_info->app_metadata->lang_entry.name)) + { + sprintf(app_name + strlen(app_name), "%s ", app_info->app_metadata->lang_entry.name); + if (illegal_char_replace_type) utilsReplaceIllegalCharacters(app_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); + } + + sprintf(app_name + strlen(app_name), "[%016lX][v%u]", app_info->meta_key.id, app_version); + } else + if (name_convention == TitleFileNameConvention_IdAndVersionOnly) + { + if (cur_filename_len) strcat(app_name, "+"); + sprintf(app_name + strlen(app_name), "%016lX_v%u", app_info->meta_key.id, app_version); + } + + /* Reallocate output buffer. */ + size_t app_name_len = strlen(app_name); + + tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char)); + if (!tmp_filename) + { + LOG_MSG("Failed to reallocate filename buffer!"); + if (filename) free(filename); + filename = NULL; + error = true; + break; + } + + filename = tmp_filename; + tmp_filename = NULL; + + /* Concatenate current user application name. */ + filename[cur_filename_len] = '\0'; + strcat(filename, app_name); + cur_filename_len += app_name_len; } - /* Reallocate output buffer. */ - size_t app_name_len = strlen(app_name); - - tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char)); - if (!tmp_filename) - { - LOG_MSG("Failed to reallocate filename buffer!"); - if (filename) free(filename); - filename = NULL; - goto end; - } - - filename = tmp_filename; - tmp_filename = NULL; - - /* Concatenate current user application name. */ - filename[cur_filename_len] = '\0'; - strcat(filename, app_name); - cur_filename_len += app_name_len; + if (!filename && !error) LOG_MSG("Error: the inserted gamecard doesn't hold any user applications!"); } - if (!filename) LOG_MSG("Error: the inserted gamecard doesn't hold any user applications!"); - -end: - mutexUnlock(&g_titleMutex); - /* Fallback string if any errors occur. */ /* This function is guaranteed to fail with Kiosk / Quest gamecards, so that's why this is needed. */ if (!filename) filename = strdup("gamecard"); @@ -1838,17 +1833,13 @@ static void titleGameCardInfoThreadFunc(void *arg) if (!first_run) { /* Update gamecard info updated flag. */ - mutexLock(&g_titleMutex); - g_titleGameCardInfoUpdated = true; - mutexUnlock(&g_titleMutex); + SCOPED_LOCK(&g_titleMutex) g_titleGameCardInfoUpdated = true; - /* Wait until another function signals us (titleIsGameCardInfoUpdated() or titleExit()). */ + /* Wait until another function signals us (titleIsGameCardInfoUpdated or titleExit). */ rc = waitMulti(&idx, -1, update_info_waiter, exit_event_waiter); if (R_FAILED(rc)) { - mutexLock(&g_titleMutex); - g_titleGameCardInfoUpdated = false; - mutexUnlock(&g_titleMutex); + SCOPED_LOCK(&g_titleMutex) g_titleGameCardInfoUpdated = false; continue; } @@ -1857,9 +1848,7 @@ static void titleGameCardInfoThreadFunc(void *arg) } /* Update gamecard title info. */ - mutexLock(&g_titleMutex); - g_titleGameCardInfoUpdated = (titleRefreshGameCardTitleInfo() && !first_run); - mutexUnlock(&g_titleMutex); + SCOPED_LOCK(&g_titleMutex) g_titleGameCardInfoUpdated = (titleRefreshGameCardTitleInfo() && !first_run); if (first_run) { @@ -2055,17 +2044,18 @@ static bool titleIsUserApplicationContentAvailable(u64 app_id) return false; } -static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id, bool lock) +static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id) { - if (lock) mutexLock(&g_titleMutex); - TitleInfo *info = NULL; - u32 start_idx = ((storage_id == NcmStorageId_BuiltInSystem || storage_id == NcmStorageId_Any) ? 0 : (storage_id == NcmStorageId_BuiltInUser ? g_titleInfoBuiltInUserStartIndex : \ + u32 start_idx = ((storage_id == NcmStorageId_BuiltInSystem || storage_id == NcmStorageId_Any) ? 0 : \ + (storage_id == NcmStorageId_BuiltInUser ? g_titleInfoBuiltInUserStartIndex : \ (storage_id == NcmStorageId_SdCard ? g_titleInfoSdCardStartIndex : g_titleInfoGameCardStartIndex))); - u32 max_val = (storage_id == NcmStorageId_BuiltInSystem ? g_titleInfoBuiltInSystemCount : (storage_id == NcmStorageId_BuiltInUser ? g_titleInfoBuiltInUserCount : \ - (storage_id == NcmStorageId_SdCard ? g_titleInfoSdCardCount : (storage_id == NcmStorageId_GameCard ? g_titleInfoGameCardCount : g_titleInfoCount)))); + u32 max_val = (storage_id == NcmStorageId_BuiltInSystem ? g_titleInfoBuiltInSystemCount : \ + (storage_id == NcmStorageId_BuiltInUser ? g_titleInfoBuiltInUserCount : \ + (storage_id == NcmStorageId_SdCard ? g_titleInfoSdCardCount : \ + (storage_id == NcmStorageId_GameCard ? g_titleInfoGameCardCount : g_titleInfoCount)))); max_val += start_idx; @@ -2086,11 +2076,9 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id, } } - if (!info && lock) LOG_MSG("Unable to find title info entry with ID \"%016lX\"! (storage ID %u).", title_id, storage_id); + if (!info) LOG_MSG("Unable to find title info entry with ID \"%016lX\"! (storage ID %u).", title_id, storage_id); end: - if (lock) mutexUnlock(&g_titleMutex); - return info; } diff --git a/source/core/ums.c b/source/core/ums.c index c92f249..0891713 100644 --- a/source/core/ums.c +++ b/source/core/ums.c @@ -43,109 +43,104 @@ static void umsFreeDeviceData(void); bool umsInitialize(void) { - mutexLock(&g_umsMutex); + bool ret = false; - Result rc = 0; - - bool ret = g_umsInterfaceInit; - if (ret) goto end; - - /* Initialize USB Mass Storage Host interface. */ - rc = usbHsFsInitialize(0); - if (R_FAILED(rc)) + SCOPED_LOCK(&g_umsMutex) { - LOG_MSG("usbHsFsInitialize failed! (0x%08X).", rc); - goto end; + ret = g_umsInterfaceInit; + if (ret) break; + + /* Initialize USB Mass Storage Host interface. */ + Result rc = usbHsFsInitialize(0); + if (R_FAILED(rc)) + { + LOG_MSG("usbHsFsInitialize failed! (0x%08X).", rc); + break; + } + + /* Get USB Mass Storage status change event. */ + g_umsStatusChangeEvent = usbHsFsGetStatusChangeUserEvent(); + + /* Create user-mode exit event. */ + ueventCreate(&g_umsDetectionThreadExitEvent, true); + + /* Create USB Mass Storage detection thread. */ + if (!(g_umsDetectionThreadCreated = umsCreateDetectionThread())) break; + + /* Update flags. */ + ret = g_umsInterfaceInit = true; } - /* Get USB Mass Storage status change event. */ - g_umsStatusChangeEvent = usbHsFsGetStatusChangeUserEvent(); - - /* Create user-mode exit event. */ - ueventCreate(&g_umsDetectionThreadExitEvent, true); - - /* Create USB Mass Storage detection thread. */ - if (!(g_umsDetectionThreadCreated = umsCreateDetectionThread())) goto end; - - ret = g_umsInterfaceInit = true; - -end: - mutexUnlock(&g_umsMutex); - return ret; } void umsExit(void) { - mutexLock(&g_umsMutex); - - if (!g_umsInterfaceInit) goto end; - - /* Destroy USB Mass Storage detection thread. */ - if (g_umsDetectionThreadCreated) + SCOPED_LOCK(&g_umsMutex) { - umsDestroyDetectionThread(); - g_umsDetectionThreadCreated = false; + /* Destroy USB Mass Storage detection thread. */ + if (g_umsDetectionThreadCreated) + { + umsDestroyDetectionThread(); + g_umsDetectionThreadCreated = false; + } + + /* Close USB Mass Storage Host interface. */ + usbHsFsExit(); + + /* Update flag. */ + g_umsInterfaceInit = false; } - - /* Close USB Mass Storage Host interface. */ - usbHsFsExit(); - - g_umsInterfaceInit = false; - -end: - mutexUnlock(&g_umsMutex); } bool umsIsDeviceInfoUpdated(void) { - mutexLock(&g_umsMutex); - bool ret = false; - if (g_umsInterfaceInit && g_umsDeviceInfoUpdated) + SCOPED_LOCK(&g_umsMutex) { + if (!g_umsInterfaceInit || !g_umsDeviceInfoUpdated) break; ret = true; g_umsDeviceInfoUpdated = false; } - mutexUnlock(&g_umsMutex); - return ret; } UsbHsFsDevice *umsGetDevices(u32 *out_count) { - mutexLock(&g_umsMutex); - UsbHsFsDevice *devices = NULL; - if (!g_umsInterfaceInit || !out_count) - { - LOG_MSG("Invalid parameters!"); - goto end; - } - - if (g_umsDeviceCount && g_umsDevices) + SCOPED_LOCK(&g_umsMutex) { + if (!g_umsInterfaceInit || !out_count) + { + LOG_MSG("Invalid parameters!"); + break; + } + + if (!g_umsDeviceCount || !g_umsDevices) + { + /* Update output device count. */ + *out_count = 0; + break; + } + /* Allocate memory for the output devices. */ devices = calloc(g_umsDeviceCount, sizeof(UsbHsFsDevice)); if (!devices) { LOG_MSG("Failed to allocate memory for %u devices!", g_umsDeviceCount); - goto end; + break; } /* Copy device data. */ memcpy(devices, g_umsDevices, g_umsDeviceCount * sizeof(UsbHsFsDevice)); + + /* Update output device count. */ + *out_count = g_umsDeviceCount; } - /* Update output device count. */ - *out_count = ((g_umsDeviceCount && g_umsDevices) ? g_umsDeviceCount : 0); - -end: - mutexUnlock(&g_umsMutex); - return devices; } @@ -189,53 +184,52 @@ static void umsDetectionThreadFunc(void *arg) /* Exit event triggered. */ if (idx == 1) break; - mutexLock(&g_umsMutex); - - /* Free USB Mass Storage device data. */ - umsFreeDeviceData(); - - /* Get mounted device count. */ - g_umsDeviceCount = usbHsFsGetMountedDeviceCount(); - LOG_MSG("USB Mass Storage status change event triggered! Mounted USB Mass Storage device count: %u.", g_umsDeviceCount); - - if (g_umsDeviceCount) + SCOPED_LOCK(&g_umsMutex) { - bool fail = false; + /* Free USB Mass Storage device data. */ + umsFreeDeviceData(); - /* Allocate mounted devices buffer. */ - g_umsDevices = calloc(g_umsDeviceCount, sizeof(UsbHsFsDevice)); - if (g_umsDevices) + /* Get mounted device count. */ + g_umsDeviceCount = usbHsFsGetMountedDeviceCount(); + LOG_MSG("USB Mass Storage status change event triggered! Mounted USB Mass Storage device count: %u.", g_umsDeviceCount); + + if (g_umsDeviceCount) { - /* List mounted devices. */ - listed_device_count = usbHsFsListMountedDevices(g_umsDevices, g_umsDeviceCount); - if (listed_device_count) + bool fail = false; + + /* Allocate mounted devices buffer. */ + g_umsDevices = calloc(g_umsDeviceCount, sizeof(UsbHsFsDevice)); + if (g_umsDevices) { - /* Check if we got as many devices as we expected. */ - if (listed_device_count == g_umsDeviceCount) + /* List mounted devices. */ + listed_device_count = usbHsFsListMountedDevices(g_umsDevices, g_umsDeviceCount); + if (listed_device_count) { - /* Update USB Mass Storage device info updated flag. */ - g_umsDeviceInfoUpdated = true; + /* Check if we got as many devices as we expected. */ + if (listed_device_count == g_umsDeviceCount) + { + /* Update USB Mass Storage device info updated flag. */ + g_umsDeviceInfoUpdated = true; + } else { + LOG_MSG("USB Mass Storage device count mismatch! (%u != %u).", listed_device_count, g_umsDeviceCount); + fail = true; + } } else { - LOG_MSG("USB Mass Storage device count mismatch! (%u != %u).", listed_device_count, g_umsDeviceCount); + LOG_MSG("Failed to list mounted USB Mass Storage devices!"); fail = true; } } else { - LOG_MSG("Failed to list mounted USB Mass Storage devices!"); + LOG_MSG("Failed to allocate memory for mounted USB Mass Storage devices buffer!"); fail = true; } + + /* Free USB Mass Storage device data if something went wrong. */ + if (fail) umsFreeDeviceData(); } else { - LOG_MSG("Failed to allocate memory for mounted USB Mass Storage devices buffer!"); - fail = true; + /* Update USB Mass Storage device info updated flag. */ + g_umsDeviceInfoUpdated = true; } - - /* Free USB Mass Storage device data if something went wrong. */ - if (fail) umsFreeDeviceData(); - } else { - /* Update USB Mass Storage device info updated flag. */ - g_umsDeviceInfoUpdated = true; } - - mutexUnlock(&g_umsMutex); } /* Free USB Mass Storage device data. */ diff --git a/source/core/usb.c b/source/core/usb.c index 1db207c..41c3716 100644 --- a/source/core/usb.c +++ b/source/core/usb.c @@ -54,13 +54,6 @@ /* Type definitions. */ -typedef struct { - RwLock lock, lock_in, lock_out; - bool initialized; - UsbDsInterface *interface; - UsbDsEndpoint *endpoint_in, *endpoint_out; -} UsbDeviceInterface; - typedef enum { UsbCommandType_StartSession = 0, UsbCommandType_SendFileProperties = 1, @@ -187,9 +180,10 @@ NXDT_ASSERT(struct usb_ss_usb_device_capability_descriptor, 0xA); /* Global variables. */ -static RwLock g_usbDeviceLock = {0}; -static UsbDeviceInterface g_usbDeviceInterface = {0}; -static bool g_usbDeviceInterfaceInit = false; +static Mutex g_usbInterfaceMutex = 0; +static UsbDsInterface *g_usbInterface = NULL; +static UsbDsEndpoint *g_usbEndpointIn = NULL, *g_usbEndpointOut = NULL; +static bool g_usbInterfaceInit = false; static Event *g_usbStateChangeEvent = NULL; static Thread g_usbDetectionThread = {0}; @@ -220,9 +214,6 @@ NX_INLINE void usbFreeTransferBuffer(void); static bool usbInitializeComms(void); static void usbCloseComms(void); -static void usbFreeDeviceInterface(void); - -NX_INLINE bool usbInitializeDeviceInterface(void); static bool usbInitializeDeviceInterface5x(void); static bool usbInitializeDeviceInterface1x(void); @@ -238,45 +229,47 @@ bool usbInitialize(void) { bool ret = false; - rwlockWriteLock(&g_usbDeviceLock); - - /* Allocate USB transfer buffer. */ - if (!usbAllocateTransferBuffer()) + SCOPED_LOCK(&g_usbInterfaceMutex) { - LOG_MSG("Failed to allocate memory for the USB transfer buffer!"); - goto end; + ret = g_usbInterfaceInit; + if (ret) break; + + /* Allocate USB transfer buffer. */ + if (!usbAllocateTransferBuffer()) + { + LOG_MSG("Failed to allocate memory for the USB transfer buffer!"); + break; + } + + /* Initialize USB device interface. */ + if (!usbInitializeComms()) + { + LOG_MSG("Failed to initialize USB device interface!"); + break; + } + + /* Retrieve USB state change kernel event. */ + g_usbStateChangeEvent = usbDsGetStateChangeEvent(); + if (!g_usbStateChangeEvent) + { + LOG_MSG("Failed to retrieve USB state change kernel event!"); + break; + } + + /* Create user-mode exit event. */ + ueventCreate(&g_usbDetectionThreadExitEvent, true); + + /* Create user-mode USB timeout event. */ + ueventCreate(&g_usbTimeoutEvent, true); + + /* Create USB detection thread. */ + atomic_store(&g_usbDetectionThreadCreated, usbCreateDetectionThread()); + if (!atomic_load(&g_usbDetectionThreadCreated)) break; + + /* Update flags. */ + ret = g_usbInterfaceInit = true; } - /* Initialize USB device interface. */ - if (!usbInitializeComms()) - { - LOG_MSG("Failed to initialize USB device interface!"); - goto end; - } - - /* Retrieve USB state change kernel event. */ - g_usbStateChangeEvent = usbDsGetStateChangeEvent(); - if (!g_usbStateChangeEvent) - { - LOG_MSG("Failed to retrieve USB state change kernel event!"); - goto end; - } - - /* Create user-mode exit event. */ - ueventCreate(&g_usbDetectionThreadExitEvent, true); - - /* Create user-mode USB timeout event. */ - ueventCreate(&g_usbTimeoutEvent, true); - - /* Create USB detection thread. */ - atomic_store(&g_usbDetectionThreadCreated, usbCreateDetectionThread()); - if (!atomic_load(&g_usbDetectionThreadCreated)) goto end; - - ret = true; - -end: - rwlockWriteUnlock(&g_usbDeviceLock); - return ret; } @@ -290,18 +283,20 @@ void usbExit(void) } /* Now we can safely lock. */ - rwlockWriteLock(&g_usbDeviceLock); - - /* Clear USB state change kernel event. */ - g_usbStateChangeEvent = NULL; - - /* Close USB device interface. */ - usbCloseComms(); - - /* Free USB transfer buffer. */ - usbFreeTransferBuffer(); - - rwlockWriteUnlock(&g_usbDeviceLock); + SCOPED_LOCK(&g_usbInterfaceMutex) + { + /* Clear USB state change kernel event. */ + g_usbStateChangeEvent = NULL; + + /* Close USB device interface. */ + usbCloseComms(); + + /* Free USB transfer buffer. */ + usbFreeTransferBuffer(); + + /* Update flag. */ + g_usbInterfaceInit = false; + } } void *usbAllocatePageAlignedBuffer(size_t size) @@ -312,209 +307,188 @@ void *usbAllocatePageAlignedBuffer(size_t size) bool usbIsReady(void) { - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - bool ret = (g_usbHostAvailable && g_usbSessionStarted); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); + bool ret = false; + SCOPED_LOCK(&g_usbInterfaceMutex) ret = (g_usbHostAvailable && g_usbSessionStarted); return ret; } bool usbSendFileProperties(u64 file_size, const char *filename, u32 nsp_header_size) { - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - bool ret = false; - UsbCommandSendFileProperties *cmd_block = NULL; - size_t filename_length = 0; - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || !filename || \ - !(filename_length = strlen(filename)) || filename_length >= FS_MAX_PATH || (!g_nspTransferMode && ((file_size && nsp_header_size >= file_size) || g_usbTransferRemainingSize)) || \ - (g_nspTransferMode && nsp_header_size)) + SCOPED_LOCK(&g_usbInterfaceMutex) { - LOG_MSG("Invalid parameters!"); - goto end; - } - - /* Prepare command data. */ - usbPrepareCommandHeader(UsbCommandType_SendFileProperties, (u32)sizeof(UsbCommandSendFileProperties)); - - cmd_block = (UsbCommandSendFileProperties*)(g_usbTransferBuffer + sizeof(UsbCommandHeader)); - memset(cmd_block, 0, sizeof(UsbCommandSendFileProperties)); - - cmd_block->file_size = file_size; - cmd_block->filename_length = (u32)filename_length; - cmd_block->nsp_header_size = nsp_header_size; - snprintf(cmd_block->filename, sizeof(cmd_block->filename), "%s", filename); - - /* Send command. */ - ret = usbSendCommand(); - if (!ret) goto end; - - /* Update variables. */ - g_usbTransferRemainingSize = file_size; - g_usbTransferWrittenSize = 0; - if (!g_nspTransferMode) g_nspTransferMode = (file_size && nsp_header_size); - + size_t filename_length = 0; + + if (!g_usbInterfaceInit || !g_usbTransferBuffer || !g_usbHostAvailable || !g_usbSessionStarted || !filename || !(filename_length = strlen(filename)) || filename_length >= FS_MAX_PATH || \ + (!g_nspTransferMode && ((file_size && nsp_header_size >= file_size) || g_usbTransferRemainingSize)) || (g_nspTransferMode && nsp_header_size)) + { + LOG_MSG("Invalid parameters!"); + goto end; + } + + /* Prepare command data. */ + usbPrepareCommandHeader(UsbCommandType_SendFileProperties, (u32)sizeof(UsbCommandSendFileProperties)); + + UsbCommandSendFileProperties *cmd_block = (UsbCommandSendFileProperties*)(g_usbTransferBuffer + sizeof(UsbCommandHeader)); + memset(cmd_block, 0, sizeof(UsbCommandSendFileProperties)); + + cmd_block->file_size = file_size; + cmd_block->filename_length = (u32)filename_length; + cmd_block->nsp_header_size = nsp_header_size; + snprintf(cmd_block->filename, sizeof(cmd_block->filename), "%s", filename); + + /* Send command. */ + ret = usbSendCommand(); + if (!ret) goto end; + + /* Update variables. */ + g_usbTransferRemainingSize = file_size; + g_usbTransferWrittenSize = 0; + if (!g_nspTransferMode) g_nspTransferMode = (file_size && nsp_header_size); + end: - if (!ret && g_nspTransferMode) g_nspTransferMode = false; - - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); + if (!ret && g_nspTransferMode) g_nspTransferMode = false; + } return ret; } bool usbSendFileData(void *data, u64 data_size) { - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); + bool ret = false; - void *buf = NULL; - UsbStatus *cmd_status = NULL; - bool ret = false, zlt_required = false; - - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || !g_usbTransferRemainingSize || !data || \ - !data_size || data_size > USB_TRANSFER_BUFFER_SIZE || data_size > g_usbTransferRemainingSize) + SCOPED_LOCK(&g_usbInterfaceMutex) { - LOG_MSG("Invalid parameters!"); - goto end; - } - - /* Optimization for buffers that already are page aligned. */ - if (IS_ALIGNED((u64)data, USB_TRANSFER_ALIGNMENT)) - { - buf = data; - } else { - buf = g_usbTransferBuffer; - memcpy(buf, data, data_size); - } - - /* Determine if we'll need to set a Zero Length Termination (ZLT) packet. */ - /* This is automatically handled by usbDsEndpoint_PostBufferAsync(), depending on the ZLT setting from the input (write) endpoint. */ - /* First, check if this is the last data chunk for this file. */ - if ((g_usbTransferRemainingSize - data_size) == 0) - { - /* Enable ZLT if the last chunk size is aligned to the USB endpoint max packet size. */ - if (IS_ALIGNED(data_size, g_usbEndpointMaxPacketSize)) + void *buf = NULL; + bool zlt_required = false; + + if (!g_usbTransferBuffer || !g_usbInterfaceInit || !g_usbHostAvailable || !g_usbSessionStarted || !g_usbTransferRemainingSize || !data || !data_size || data_size > USB_TRANSFER_BUFFER_SIZE || \ + data_size > g_usbTransferRemainingSize) { - zlt_required = true; - usbSetZltPacket(true); - //LOG_MSG("ZLT enabled. Last chunk size: 0x%lX bytes.", data_size); - } - } else { - /* Disable ZLT if this is the first of multiple data chunks. */ - if (!g_usbTransferWrittenSize) - { - usbSetZltPacket(false); - //LOG_MSG("ZLT disabled (first chunk)."); - } - } - - /* Send data chunk. */ - if (!usbWrite(buf, data_size)) - { - LOG_MSG("Failed to write 0x%lX bytes long file data chunk from offset 0x%lX! (total size: 0x%lX).", data_size, g_usbTransferWrittenSize, g_usbTransferRemainingSize + g_usbTransferWrittenSize); - goto end; - } - - ret = true; - g_usbTransferRemainingSize -= data_size; - g_usbTransferWrittenSize += data_size; - - /* Check if this is the last chunk. */ - if (!g_usbTransferRemainingSize) - { - /* Check response from host device. */ - if (!usbRead(g_usbTransferBuffer, sizeof(UsbStatus))) - { - LOG_MSG("Failed to read 0x%lX bytes long status block!", sizeof(UsbStatus)); - ret = false; + LOG_MSG("Invalid parameters!"); goto end; } - cmd_status = (UsbStatus*)g_usbTransferBuffer; - - if (cmd_status->magic != __builtin_bswap32(USB_CMD_HEADER_MAGIC)) + /* Optimization for buffers that already are page aligned. */ + if (IS_ALIGNED((u64)data, USB_TRANSFER_ALIGNMENT)) { - LOG_MSG("Invalid status block magic word!"); - ret = false; + buf = data; + } else { + buf = g_usbTransferBuffer; + memcpy(buf, data, data_size); + } + + /* Determine if we'll need to set a Zero Length Termination (ZLT) packet. */ + /* This is automatically handled by usbDsEndpoint_PostBufferAsync(), depending on the ZLT setting from the input (write) endpoint. */ + /* First, check if this is the last data chunk for this file. */ + if ((g_usbTransferRemainingSize - data_size) == 0) + { + /* Enable ZLT if the last chunk size is aligned to the USB endpoint max packet size. */ + if (IS_ALIGNED(data_size, g_usbEndpointMaxPacketSize)) + { + zlt_required = true; + usbSetZltPacket(true); + //LOG_MSG("ZLT enabled. Last chunk size: 0x%lX bytes.", data_size); + } + } else { + /* Disable ZLT if this is the first of multiple data chunks. */ + if (!g_usbTransferWrittenSize) + { + usbSetZltPacket(false); + //LOG_MSG("ZLT disabled (first chunk)."); + } + } + + /* Send data chunk. */ + if (!(ret = usbWrite(buf, data_size))) + { + LOG_MSG("Failed to write 0x%lX bytes long file data chunk from offset 0x%lX! (total size: 0x%lX).", data_size, g_usbTransferWrittenSize, \ + g_usbTransferRemainingSize + g_usbTransferWrittenSize); goto end; } - ret = (cmd_status->status == UsbStatusType_Success); - if (!ret) usbLogStatusDetail(cmd_status->status); - } - + g_usbTransferRemainingSize -= data_size; + g_usbTransferWrittenSize += data_size; + + /* Check if this is the last chunk. */ + if (!g_usbTransferRemainingSize) + { + /* Check response from host device. */ + if (!(ret = usbRead(g_usbTransferBuffer, sizeof(UsbStatus)))) + { + LOG_MSG("Failed to read 0x%lX bytes long status block!", sizeof(UsbStatus)); + goto end; + } + + UsbStatus *cmd_status = (UsbStatus*)g_usbTransferBuffer; + + if (!(ret = (cmd_status->magic == __builtin_bswap32(USB_CMD_HEADER_MAGIC)))) + { + LOG_MSG("Invalid status block magic word!"); + goto end; + } + + if (!(ret = (cmd_status->status == UsbStatusType_Success))) usbLogStatusDetail(cmd_status->status); + } + end: - /* Disable ZLT if it was previously enabled. */ - if (zlt_required) usbSetZltPacket(false); - - /* Reset variables in case of errors. */ - if (!ret) - { - g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; - g_nspTransferMode = false; + /* Disable ZLT if it was previously enabled. */ + if (zlt_required) usbSetZltPacket(false); + + /* Reset variables in case of errors. */ + if (!ret) + { + g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; + g_nspTransferMode = false; + } } - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); - return ret; } void usbCancelFileTransfer(void) { - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || (!g_usbTransferRemainingSize && \ - !g_nspTransferMode)) goto end; - - /* Reset variables right away. */ - g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; - g_nspTransferMode = false; - - /* Prepare command data. */ - usbPrepareCommandHeader(UsbCommandType_CancelFileTransfer, 0); - - /* Send command. We don't care about the result here. */ - usbSendCommand(); - -end: - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); + SCOPED_LOCK(&g_usbInterfaceMutex) + { + if (!g_usbInterfaceInit || !g_usbTransferBuffer || !g_usbHostAvailable || !g_usbSessionStarted || (!g_usbTransferRemainingSize && !g_nspTransferMode)) break; + + /* Reset variables right away. */ + g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; + g_nspTransferMode = false; + + /* Prepare command data. */ + usbPrepareCommandHeader(UsbCommandType_CancelFileTransfer, 0); + + /* Send command. We don't care about the result here. */ + usbSendCommand(); + } } bool usbSendNspHeader(void *nsp_header, u32 nsp_header_size) { - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - bool ret = false; - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || g_usbTransferRemainingSize || \ - !g_nspTransferMode || !nsp_header || !nsp_header_size || nsp_header_size > (USB_TRANSFER_BUFFER_SIZE - sizeof(UsbCommandHeader))) + SCOPED_LOCK(&g_usbInterfaceMutex) { - LOG_MSG("Invalid parameters!"); - goto end; + if (!g_usbInterfaceInit || !g_usbTransferBuffer || !g_usbHostAvailable || !g_usbSessionStarted || g_usbTransferRemainingSize || !g_nspTransferMode || !nsp_header || !nsp_header_size || \ + nsp_header_size > (USB_TRANSFER_BUFFER_SIZE - sizeof(UsbCommandHeader))) + { + LOG_MSG("Invalid parameters!"); + break; + } + + /* Disable NSP transfer mode right away. */ + g_nspTransferMode = false; + + /* Prepare command data. */ + usbPrepareCommandHeader(UsbCommandType_SendNspHeader, nsp_header_size); + memcpy(g_usbTransferBuffer + sizeof(UsbCommandHeader), nsp_header, nsp_header_size); + + /* Send command. */ + ret = usbSendCommand(); } - /* Disable NSP transfer mode right away. */ - g_nspTransferMode = false; - - /* Prepare command data. */ - usbPrepareCommandHeader(UsbCommandType_SendNspHeader, nsp_header_size); - memcpy(g_usbTransferBuffer + sizeof(UsbCommandHeader), nsp_header, nsp_header_size); - - /* Send command. */ - ret = usbSendCommand(); - -end: - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); - return ret; } @@ -549,55 +523,57 @@ static void usbDetectionThreadFunc(void *arg) Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent); Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent); + bool exit_flag = false; + while(true) { /* Wait until an event is triggered. */ rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter); if (R_FAILED(rc)) continue; - rwlockWriteLock(&g_usbDeviceLock); - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - /* Exit event triggered. */ if (idx == 2) break; - /* Retrieve current USB connection status. */ - /* Only proceed if we're dealing with a status change. */ - g_usbHostAvailable = usbIsHostAvailable(); - g_usbSessionStarted = false; - g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; - g_usbEndpointMaxPacketSize = 0; - - /* Start an USB session if we're connected to a host device. */ - /* This will essentially hang this thread and all other threads that call USB-related functions until: */ - /* a) A session is successfully established. */ - /* b) The console is disconnected from the USB host. */ - /* c) The thread exit event is triggered. */ - if (g_usbHostAvailable) + SCOPED_LOCK(&g_usbInterfaceMutex) { - /* Wait until a session is established. */ - g_usbSessionStarted = usbStartSession(); - if (g_usbSessionStarted) + /* Retrieve current USB connection status. */ + /* Only proceed if we're dealing with a status change. */ + g_usbHostAvailable = usbIsHostAvailable(); + g_usbSessionStarted = false; + g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; + g_usbEndpointMaxPacketSize = 0; + + /* Start a USB session if we're connected to a host device. */ + /* This will essentially hang this thread and all other threads that call USB-related functions until: */ + /* a) A session is successfully established. */ + /* b) The console is disconnected from the USB host. */ + /* c) The thread exit event is triggered. */ + if (g_usbHostAvailable) { - LOG_MSG("USB session successfully established. Endpoint max packet size: 0x%04X.", g_usbEndpointMaxPacketSize); - } else { - /* Check if the exit event was triggered while waiting for a session to be established. */ - if (g_usbDetectionThreadExitFlag) break; + /* Wait until a session is established. */ + g_usbSessionStarted = usbStartSession(); + if (g_usbSessionStarted) + { + LOG_MSG("USB session successfully established. Endpoint max packet size: 0x%04X.", g_usbEndpointMaxPacketSize); + } else { + /* Update exit flag. */ + exit_flag = g_usbDetectionThreadExitFlag; + } } } - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); + /* Check if the exit event was triggered while waiting for a session to be established. */ + if (exit_flag) break; } - /* Close USB session if needed. */ - if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession(); - g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false; - g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; - g_usbEndpointMaxPacketSize = 0; - - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - rwlockWriteUnlock(&g_usbDeviceLock); + SCOPED_LOCK(&g_usbInterfaceMutex) + { + /* Close USB session if needed. */ + if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession(); + g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false; + g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0; + g_usbEndpointMaxPacketSize = 0; + } threadExit(); } @@ -607,7 +583,7 @@ static bool usbStartSession(void) UsbCommandStartSession *cmd_block = NULL; bool ret = false; - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized) + if (!g_usbInterfaceInit || !g_usbTransferBuffer) { LOG_MSG("Invalid parameters!"); goto end; @@ -630,8 +606,7 @@ static bool usbStartSession(void) /* Get the endpoint max packet size from the response sent by the USB host. */ /* This is done to accurately know when and where to enable Zero Length Termination (ZLT) packets during bulk transfers. */ /* As much as I'd like to avoid this, usb:ds doesn't disclose information such as the exact device descriptor and/or speed used by the USB host. */ - UsbStatus *cmd_status = (UsbStatus*)g_usbTransferBuffer; - g_usbEndpointMaxPacketSize = cmd_status->max_packet_size; + g_usbEndpointMaxPacketSize = ((UsbStatus*)g_usbTransferBuffer)->max_packet_size; if (g_usbEndpointMaxPacketSize != USB_FS_EP_MAX_PACKET_SIZE && g_usbEndpointMaxPacketSize != USB_HS_EP_MAX_PACKET_SIZE && g_usbEndpointMaxPacketSize != USB_SS_EP_MAX_PACKET_SIZE) { LOG_MSG("Invalid endpoint max packet size value received from USB host: 0x%04X.", g_usbEndpointMaxPacketSize); @@ -648,7 +623,7 @@ end: static void usbEndSession(void) { - if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInit || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted) + if (!g_usbInterfaceInit || !g_usbTransferBuffer || !g_usbHostAvailable || !g_usbSessionStarted) { LOG_MSG("Invalid parameters!"); return; @@ -792,6 +767,7 @@ NX_INLINE void usbFreeTransferBuffer(void) static bool usbInitializeComms(void) { Result rc = 0; + bool ret = false, init_dev_if = false; /* Used on HOS >= 5.0.0. */ struct usb_device_descriptor device_descriptor = { @@ -849,9 +825,6 @@ static bool usbInitializeComms(void) .SerialNumber = APP_VERSION }; - bool ret = (g_usbDeviceInterfaceInit && g_usbDeviceInterface.initialized); - if (ret) goto end; - rc = usbDsInitialize(); if (R_FAILED(rc)) { @@ -928,17 +901,8 @@ static bool usbInitializeComms(void) if (R_FAILED(rc)) goto end; /* Initialize USB device interface. */ - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - rwlockWriteLock(&(g_usbDeviceInterface.lock_in)); - rwlockWriteLock(&(g_usbDeviceInterface.lock_out)); - - bool dev_iface_init = usbInitializeDeviceInterface(); - - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_out)); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in)); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - - if (!dev_iface_init) + init_dev_if = (hosversionAtLeast(5, 0, 0) ? usbInitializeDeviceInterface5x() : usbInitializeDeviceInterface1x()); + if (!init_dev_if) { LOG_MSG("Failed to initialize USB device interface!"); goto end; @@ -954,7 +918,7 @@ static bool usbInitializeComms(void) } } - ret = g_usbDeviceInterfaceInit = true; + ret = true; end: if (!ret) usbCloseComms(); @@ -965,37 +929,10 @@ end: static void usbCloseComms(void) { usbDsExit(); - g_usbDeviceInterfaceInit = false; - usbFreeDeviceInterface(); -} - -static void usbFreeDeviceInterface(void) -{ - rwlockWriteLock(&(g_usbDeviceInterface.lock)); - if (!g_usbDeviceInterface.initialized) { - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); - return; - } - - rwlockWriteLock(&(g_usbDeviceInterface.lock_in)); - rwlockWriteLock(&(g_usbDeviceInterface.lock_out)); - - g_usbDeviceInterface.initialized = false; - - g_usbDeviceInterface.interface = NULL; - g_usbDeviceInterface.endpoint_in = NULL; - g_usbDeviceInterface.endpoint_out = NULL; - - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_out)); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in)); - - rwlockWriteUnlock(&(g_usbDeviceInterface.lock)); -} - -NX_INLINE bool usbInitializeDeviceInterface(void) -{ - return (hosversionAtLeast(5, 0, 0) ? usbInitializeDeviceInterface5x() : usbInitializeDeviceInterface1x()); + g_usbInterface = NULL; + g_usbEndpointIn = NULL; + g_usbEndpointOut = NULL; } static bool usbInitializeDeviceInterface5x(void) @@ -1040,37 +977,34 @@ static bool usbInitializeDeviceInterface5x(void) .wBytesPerInterval = 0 }; - /* Enable device interface. */ - g_usbDeviceInterface.initialized = true; - /* Setup interface. */ - rc = usbDsRegisterInterface(&(g_usbDeviceInterface.interface)); + rc = usbDsRegisterInterface(&g_usbInterface); if (R_FAILED(rc)) { LOG_MSG("usbDsRegisterInterface failed! (0x%08X).", rc); return false; } - interface_descriptor.bInterfaceNumber = g_usbDeviceInterface.interface->interface_index; + interface_descriptor.bInterfaceNumber = g_usbInterface->interface_index; endpoint_descriptor_in.bEndpointAddress += (interface_descriptor.bInterfaceNumber + 1); endpoint_descriptor_out.bEndpointAddress += (interface_descriptor.bInterfaceNumber + 1); /* Full Speed config (USB 1.1). */ - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (interface).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (in endpoint).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 1.1) (out endpoint).", rc); @@ -1081,21 +1015,21 @@ static bool usbInitializeDeviceInterface5x(void) endpoint_descriptor_in.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE; endpoint_descriptor_out.wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE; - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (interface).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (in endpoint).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 2.0) (out endpoint).", rc); @@ -1106,35 +1040,35 @@ static bool usbInitializeDeviceInterface5x(void) endpoint_descriptor_in.wMaxPacketSize = USB_SS_EP_MAX_PACKET_SIZE; endpoint_descriptor_out.wMaxPacketSize = USB_SS_EP_MAX_PACKET_SIZE; - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (interface).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (in endpoint companion).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint).", rc); return false; } - rc = usbDsInterface_AppendConfigurationData(g_usbDeviceInterface.interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); + rc = usbDsInterface_AppendConfigurationData(g_usbInterface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_AppendConfigurationData failed! (0x%08X) (USB 3.0) (out endpoint companion).", rc); @@ -1142,21 +1076,21 @@ static bool usbInitializeDeviceInterface5x(void) } /* Setup endpoints. */ - rc = usbDsInterface_RegisterEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_in), endpoint_descriptor_in.bEndpointAddress); + rc = usbDsInterface_RegisterEndpoint(g_usbInterface, &g_usbEndpointIn, endpoint_descriptor_in.bEndpointAddress); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_RegisterEndpoint failed! (0x%08X) (in endpoint).", rc); return false; } - rc = usbDsInterface_RegisterEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_out), endpoint_descriptor_out.bEndpointAddress); + rc = usbDsInterface_RegisterEndpoint(g_usbInterface, &g_usbEndpointOut, endpoint_descriptor_out.bEndpointAddress); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_RegisterEndpoint failed! (0x%08X) (out endpoint).", rc); return false; } - rc = usbDsInterface_EnableInterface(g_usbDeviceInterface.interface); + rc = usbDsInterface_EnableInterface(g_usbInterface); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_EnableInterface failed! (0x%08X).", rc); @@ -1199,11 +1133,8 @@ static bool usbInitializeDeviceInterface1x(void) .bInterval = 0 }; - /* Enable device interface. */ - g_usbDeviceInterface.initialized = true; - /* Setup interface. */ - rc = usbDsGetDsInterface(&(g_usbDeviceInterface.interface), &interface_descriptor, "usb"); + rc = usbDsGetDsInterface(&g_usbInterface, &interface_descriptor, "usb"); if (R_FAILED(rc)) { LOG_MSG("usbDsGetDsInterface failed! (0x%08X).", rc); @@ -1211,21 +1142,21 @@ static bool usbInitializeDeviceInterface1x(void) } /* Setup endpoints. */ - rc = usbDsInterface_GetDsEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_in), &endpoint_descriptor_in); + rc = usbDsInterface_GetDsEndpoint(g_usbInterface, &g_usbEndpointIn, &endpoint_descriptor_in); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_GetDsEndpoint failed! (0x%08X) (in endpoint).", rc); return false; } - rc = usbDsInterface_GetDsEndpoint(g_usbDeviceInterface.interface, &(g_usbDeviceInterface.endpoint_out), &endpoint_descriptor_out); + rc = usbDsInterface_GetDsEndpoint(g_usbInterface, &g_usbEndpointOut, &endpoint_descriptor_out); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_GetDsEndpoint failed! (0x%08X) (out endpoint).", rc); return false; } - rc = usbDsInterface_EnableInterface(g_usbDeviceInterface.interface); + rc = usbDsInterface_EnableInterface(g_usbInterface); if (R_FAILED(rc)) { LOG_MSG("usbDsInterface_EnableInterface failed! (0x%08X).", rc); @@ -1244,25 +1175,17 @@ NX_INLINE bool usbIsHostAvailable(void) NX_INLINE void usbSetZltPacket(bool enable) { - rwlockWriteLock(&(g_usbDeviceInterface.lock_in)); - usbDsEndpoint_SetZlt(g_usbDeviceInterface.endpoint_in, enable); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in)); + usbDsEndpoint_SetZlt(g_usbEndpointIn, enable); } NX_INLINE bool usbRead(void *buf, u64 size) { - rwlockWriteLock(&(g_usbDeviceInterface.lock_out)); - bool ret = usbTransferData(buf, size, g_usbDeviceInterface.endpoint_out); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_out)); - return ret; + return usbTransferData(buf, size, g_usbEndpointOut); } NX_INLINE bool usbWrite(void *buf, u64 size) { - rwlockWriteLock(&(g_usbDeviceInterface.lock_in)); - bool ret = usbTransferData(buf, size, g_usbDeviceInterface.endpoint_in); - rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in)); - return ret; + return usbTransferData(buf, size, g_usbEndpointIn); } static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint) @@ -1284,7 +1207,7 @@ static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint) u32 urb_id = 0, transferred_size = 0; bool thread_exit = false; - /* Start an USB transfer using the provided endpoint. */ + /* Start a USB transfer using the provided endpoint. */ rc = usbDsEndpoint_PostBufferAsync(endpoint, buf, size, &urb_id); if (R_FAILED(rc)) { @@ -1295,10 +1218,10 @@ static bool usbTransferData(void *buf, u64 size, UsbDsEndpoint *endpoint) /* Wait for the transfer to finish. */ if (g_usbSessionStarted) { - /* If the USB transfer session has already been started, then use a regular timeout value. */ + /* If the USB session has already been established, then use a regular timeout value. */ rc = eventWait(&(endpoint->CompletionEvent), USB_TRANSFER_TIMEOUT * (u64)1000000000); } else { - /* If we're starting an USB transfer session, wait indefinitely inside a loop to let the user start the companion app. */ + /* If we're starting a USB session, wait indefinitely inside a loop to let the user start the companion app. */ int idx = 0; Waiter completion_event_waiter = waiterForEvent(&(endpoint->CompletionEvent)); Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent); diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c index 9197636..9e56bd8 100644 --- a/source/fatfs/diskio.c +++ b/source/fatfs/diskio.c @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------*/ -/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */ +/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ @@ -23,12 +23,9 @@ DSTATUS disk_status ( ) { (void)pdrv; - return 0; } - - /*-----------------------------------------------------------------------*/ /* Inidialize a Drive */ /*-----------------------------------------------------------------------*/ @@ -38,12 +35,9 @@ DSTATUS disk_initialize ( ) { (void)pdrv; - return 0; } - - /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ @@ -66,8 +60,6 @@ DRESULT disk_read ( return (R_SUCCEEDED(rc) ? RES_OK : RES_ERROR); } - - /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ @@ -85,13 +77,11 @@ DRESULT disk_write ( (void)buff; (void)sector; (void)count; - return RES_OK; } #endif - /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ @@ -105,6 +95,5 @@ DRESULT disk_ioctl ( (void)pdrv; (void)cmd; (void)buff; - return RES_OK; } diff --git a/source/fatfs/ff.c b/source/fatfs/ff.c index 271e733..d209605 100644 --- a/source/fatfs/ff.c +++ b/source/fatfs/ff.c @@ -1,8 +1,8 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem Module R0.14 / +/ FatFs - Generic FAT Filesystem Module R0.14b / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2021, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -19,6 +19,7 @@ /----------------------------------------------------------------------------*/ +#include #include "ff.h" /* Declarations of FatFs API */ #include "diskio.h" /* Declarations of device I/O functions */ @@ -29,7 +30,7 @@ ---------------------------------------------------------------------------*/ -#if FF_DEFINED != 86606 /* Revision ID */ +#if FF_DEFINED != 86631 /* Revision ID */ #error Wrong include file (ff.h). #endif @@ -47,6 +48,8 @@ #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') #define IsLower(c) ((c) >= 'a' && (c) <= 'z') #define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSeparator(c) ((c) == '/' || (c) == '\\') +#define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!')) #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) @@ -61,7 +64,8 @@ /* 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 */ +#define AM_MASK 0x3F /* Mask of defined bits in FAT */ +#define AM_MASKX 0x37 /* Mask of defined bits in exFAT */ /* Name status flags in fn[11] */ @@ -233,7 +237,7 @@ /* Re-entrancy related */ #if FF_FS_REENTRANT #if FF_USE_LFN == 1 -#error Static LFN work area cannot be used at thread-safe configuration +#error Static LFN work area cannot be used in thread-safe configuration #endif #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } #else @@ -244,10 +248,10 @@ /* Definitions of logical drive - physical location conversion */ #if FF_MULTI_PARTITION #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ -#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition number (0:auto search, 1..:forced partition number) */ #else #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */ -#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#define LD2PT(vol) 0 /* Auto partition search */ #endif @@ -556,7 +560,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ /* Code conversion tables */ /*--------------------------------*/ -#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ #define CODEPAGE CodePage static WORD CodePage; /* Current code page */ static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ @@ -680,53 +684,6 @@ static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-en /* String functions */ /*-----------------------------------------------------------------------*/ -/* Copy memory to memory */ -static void mem_cpy (void* dst, const void* src, UINT cnt) -{ - BYTE *d = (BYTE*)dst; - const BYTE *s = (const BYTE*)src; - - if (cnt != 0) { - do { - *d++ = *s++; - } while (--cnt); - } -} - - -/* Fill memory block */ -static void mem_set (void* dst, int val, UINT cnt) -{ - BYTE *d = (BYTE*)dst; - - do { - *d++ = (BYTE)val; - } while (--cnt); -} - - -/* Compare memory block */ -static 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; - - 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) /* NZ:contained, ZR:not contained */ -{ - while (*str && *str != chr) str++; - return *str; -} - - /* Test if the byte is DBC 1st byte */ static int dbc_1st (BYTE c) { @@ -795,18 +752,14 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on uc = (BYTE)*p++; /* Get an encoding unit */ if (uc & 0x80) { /* Multiple byte code? */ - if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ uc &= 0x1F; nf = 1; - } else { - if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ - uc &= 0x0F; nf = 2; - } else { - if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ - uc &= 0x07; nf = 3; - } else { /* Wrong sequence */ - return 0xFFFFFFFF; - } - } + } else if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; } do { /* Get trailing bytes */ b = (BYTE)*p++; @@ -844,8 +797,8 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on } -/* Output a TCHAR string in defined API encoding */ -static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ +/* Store a Unicode char in defined API encoding */ +static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ TCHAR* buf, /* Output buffer */ UINT szb /* Size of the buffer */ @@ -1011,7 +964,7 @@ static UINT inc_lock ( /* Increment object open counter and returns its index (0 && Files[i].ofs == dp->dptr) break; } - if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ + if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */ Files[i].fs = dp->obj.fs; @@ -1038,13 +991,13 @@ static FRESULT dec_lock ( /* Decrement object open counter */ if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ n = Files[i].ctr; - if (n == 0x100) n = 0; /* If write mode open, delete the entry */ - if (n > 0) n--; /* Decrement read mode open count */ + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ Files[i].ctr = n; 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 */ + res = FR_INT_ERR; /* Invalid index nunber */ } return res; } @@ -1133,14 +1086,13 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ if (res == FR_OK) { if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ /* Create FSInfo structure */ - mem_set(fs->win, 0, sizeof fs->win); - 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; + memset(fs->win, 0, sizeof fs->win); + st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */ + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */ + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */ + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */ + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */ + fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */ disk_write(fs->pdrv, fs->win, fs->winsect, 1); fs->fsi_flag = 0; } @@ -1173,7 +1125,7 @@ static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ /*-----------------------------------------------------------------------*/ -/* FAT access - Read value of a FAT entry */ +/* FAT access - Read value of an FAT entry */ /*-----------------------------------------------------------------------*/ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ @@ -1235,7 +1187,8 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF break; } } - /* go to default */ + val = 1; /* Internal error */ + break; #endif default: val = 1; /* Internal error */ @@ -1250,7 +1203,7 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF #if !FF_FS_READONLY /*-----------------------------------------------------------------------*/ -/* FAT access - Change value of a FAT entry */ +/* FAT access - Change value of an FAT entry */ /*-----------------------------------------------------------------------*/ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ @@ -1266,12 +1219,12 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { - case FS_FAT12 : + case FS_FAT12: bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; p = fs->win + bc++ % SS(fs); - *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; @@ -1280,16 +1233,16 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ fs->wflag = 1; break; - case FS_FAT16 : + case FS_FAT16: res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ fs->wflag = 1; break; - case FS_FAT32 : + case FS_FAT32: #if FF_FS_EXFAT - case FS_EXFAT : + case FS_EXFAT: #endif res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; @@ -1684,12 +1637,12 @@ static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ sect = clst2sect(fs, clst); /* Top of the cluster */ fs->winsect = sect; /* Set window to top of the cluster */ - mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ + memset(fs->win, 0, sizeof fs->win); /* Clear window buffer */ #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ /* Allocate a temporary buffer */ for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ; if (szb > SS(fs)) { /* Buffer allocated? */ - mem_set(ibuf, 0, szb); + memset(ibuf, 0, szb); szb /= SS(fs); /* Bytes -> Sectors */ for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ ff_memfree(ibuf); @@ -1821,7 +1774,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ - UINT nent /* Number of contiguous entries to allocate */ + UINT n_ent /* Number of contiguous entries to allocate */ ) { FRESULT res; @@ -1836,16 +1789,16 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ res = move_window(fs, dp->sect); if (res != FR_OK) break; #if FF_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)) { + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */ #else - if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */ #endif - if (++n == nent) break; /* A block of contiguous free entries is found */ + if (++n == n_ent) break; /* Is a block of contiguous free entries found? */ } else { - n = 0; /* Not a blank entry. Restart to search */ + n = 0; /* Not a free entry, restart to search */ } - res = dir_next(dp, 1); - } while (res == FR_OK); /* Next entry with table stretch enabled */ + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); } if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ @@ -1993,7 +1946,7 @@ static void put_lfn ( do { 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 following items */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ } while (++s < 13); if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ @@ -2011,7 +1964,7 @@ static void put_lfn ( static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ - const BYTE* src, /* Pointer to SFN */ + const BYTE* src, /* Pointer to SFN in directory form */ const WCHAR* lfn, /* Pointer to LFN */ UINT seq /* Sequence number */ ) @@ -2022,7 +1975,7 @@ static void gen_numname ( DWORD sreg; - mem_cpy(dst, src, 11); + memcpy(dst, src, 11); /* Prepare the SFN to be modified */ if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sreg = seq; @@ -2037,24 +1990,23 @@ static void gen_numname ( seq = (UINT)sreg; } - /* itoa (hexdecimal) */ + /* Make suffix (~ + hexdecimal) */ i = 7; do { - c = (BYTE)((seq % 16) + '0'); + c = (BYTE)((seq % 16) + '0'); seq /= 16; if (c > '9') c += 7; ns[i--] = c; - seq /= 16; - } while (seq); + } while (i && seq); ns[i] = '~'; - /* Append the number to the SFN body */ - for (j = 0; j < i && dst[j] != ' '; j++) { - if (dbc_1st(dst[j])) { + /* Append the suffix to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { /* Find the offset to append */ + if (dbc_1st(dst[j])) { /* To avoid DBC break up */ if (j == i - 1) break; j++; } } - do { + do { /* Append the suffix */ dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } @@ -2139,47 +2091,6 @@ static DWORD xsum32 ( /* Returns 32-bit checksum */ #endif -#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 -/*------------------------------------------------------*/ -/* exFAT: Get object information from a directory block */ -/*------------------------------------------------------*/ - -static void get_xfileinfo ( - BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ - FILINFO* fno /* Buffer to store the extracted file information */ -) -{ - WCHAR wc, hs; - UINT di, si, nc; - - /* Get file name from the entry block */ - si = SZDIRE * 2; /* 1st C1 entry */ - nc = 0; hs = 0; di = 0; - while (nc < dirb[XDIR_NumName]) { - if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ - if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ - wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ - if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ - hs = wc; continue; /* Get low surrogate */ - } - wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ - if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ - di += wc; - hs = 0; - } - if (hs != 0) di = 0; /* Broken surrogate pair? */ - if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ - fno->fname[di] = 0; /* Terminate the name */ - fno->altname[0] = 0; /* exFAT does not support 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 /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ - /*-----------------------------------*/ /* exFAT: Get a directry entry block */ @@ -2191,28 +2102,28 @@ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ { FRESULT res; UINT i, sz_ent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + BYTE *dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ - /* Load file-directory entry */ + /* Load file directory entry */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */ - mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); + memcpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; - /* Load stream-extension entry */ + /* Load stream extension entry */ res = dir_next(dp, 0); if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ 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] != ET_STREAM) return FR_INT_ERR; /* Invalid order */ - mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); + memcpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; - /* Load file-name entries */ + /* Load file name entries */ i = 2 * SZDIRE; /* Name offset to load */ do { res = dir_next(dp, 0); @@ -2221,7 +2132,7 @@ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */ - if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + if (i < MAXDIRB(FF_MAX_LFN)) memcpy(dirb + i, dp->dir, SZDIRE); } while ((i += SZDIRE) < sz_ent); /* Sanity check (do it for only accessible object) */ @@ -2289,7 +2200,7 @@ static FRESULT store_xdir ( { FRESULT res; UINT nent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + 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)); @@ -2300,7 +2211,7 @@ static FRESULT store_xdir ( while (res == FR_OK) { res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) break; - mem_cpy(dp->dir, dirb, SZDIRE); + memcpy(dp->dir, dirb, SZDIRE); dp->obj.fs->wflag = 1; if (--nent == 0) break; dirb += SZDIRE; @@ -2326,7 +2237,7 @@ static void create_xdir ( /* Create file-directory and stream-extension entry */ - mem_set(dirb, 0, 2 * SZDIRE); + memset(dirb, 0, 2 * SZDIRE); dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; @@ -2337,7 +2248,7 @@ static void create_xdir ( dirb[i++] = ET_FILENAME; dirb[i++] = 0; do { /* Fill name field */ if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ - st_word(dirb + i, wc); /* Store it */ + st_word(dirb + i, wc); /* Store it */ i += 2; } while (i % SZDIRE != 0); nc1++; @@ -2402,17 +2313,17 @@ static FRESULT dir_read ( if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ ord = 0xFF; } else { - if (attr == AM_LFN) { /* An LFN entry is found */ - if (b & LLEF) { /* Is it start of an LFN sequence? */ + if (attr == AM_LFN) { /* An LFN entry is found */ + if (b & LLEF) { /* Is it start of an LFN sequence? */ sum = dp->dir[LDIR_Chksum]; b &= (BYTE)~LLEF; ord = b; dp->blk_ofs = dp->dptr; } /* Check LFN validity and capture it */ ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; - } else { /* An SFN entry is found */ + } else { /* An SFN entry is found */ if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ - dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ } break; } @@ -2460,7 +2371,7 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */ #if FF_MAX_LFN < 255 - if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ + if (fs->dirbuf[XDIR_NumName] > FF_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 */ @@ -2498,13 +2409,13 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ } } else { /* An SFN entry is found */ if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ - if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !memcmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } #else /* Non LFN configuration */ 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? */ + if (!(dp->dir[DIR_Attr] & AM_VOL) && !memcmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ #endif res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); @@ -2527,19 +2438,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too FRESULT res; FATFS *fs = dp->obj.fs; #if FF_USE_LFN /* LFN configuration */ - UINT n, nlen, nent; + UINT n, len, n_ent; 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 */ + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ - res = dir_alloc(dp, nent); /* Allocate directory entries */ + n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, n_ent); /* Allocate directory entries */ if (res != FR_OK) return res; - dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */ if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */ dp->obj.stat &= ~4; @@ -2552,10 +2463,10 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too res = load_obj_xdir(&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 */ + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); - fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */ + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */ res = store_xdir(&dj); /* Store the object status */ if (res != FR_OK) return res; } @@ -2566,7 +2477,7 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too } #endif /* On the FAT/FAT32 volume */ - mem_cpy(sn, dp->fn, 12); + memcpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { @@ -2580,19 +2491,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too } /* 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->dptr - nent * SZDIRE); + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, n_ent); /* Allocate entries */ + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); if (res == FR_OK) { sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ res = move_window(fs, dp->sect); if (res != FR_OK) break; - put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK && --nent); + } while (res == FR_OK && --n_ent); } } @@ -2605,8 +2516,8 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too if (res == FR_OK) { res = move_window(fs, dp->sect); if (res == FR_OK) { - mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ - mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ + memset(dp->dir, 0, SZDIRE); /* Clean the entry */ + memcpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ #if FF_USE_LFN dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ #endif @@ -2642,7 +2553,7 @@ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ if (res != FR_OK) break; if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ - } else { /* On the FAT/FAT32 volume */ + } else { /* On the FAT/FAT32 volume */ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ } fs->wflag = 1; @@ -2682,6 +2593,7 @@ static void get_fileinfo ( BYTE lcf; WCHAR wc, hs; FATFS *fs = dp->obj.fs; + UINT nw; #else TCHAR c; #endif @@ -2692,22 +2604,47 @@ static void get_fileinfo ( #if FF_USE_LFN /* LFN configuration */ #if FF_FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - get_xfileinfo(fs->dirbuf, fno); + if (fs->fs_type == FS_EXFAT) { /* exFAT volume */ + UINT nc = 0; + + si = SZDIRE * 2; di = 0; /* 1st C1 entry in the entry block */ + hs = 0; + while (nc < fs->dirbuf[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + wc = ld_word(fs->dirbuf + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */ + di += nw; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not support SFN */ + + fno->fattrib = fs->dirbuf[XDIR_Attr] & AM_MASKX; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(fs->dirbuf + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(fs->dirbuf + XDIR_ModTime + 2); /* Date */ return; } else #endif - { /* On the FAT/FAT32 volume */ + { /* FAT/FAT32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ - si = di = hs = 0; + si = di = 0; + hs = 0; while (fs->lfnbuf[si] != 0) { wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ hs = wc; continue; /* Get low surrogate */ } - wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ - if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ - di += wc; + nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */ + di += nw; hs = 0; } if (hs != 0) di = 0; /* Broken surrogate pair? */ @@ -2727,9 +2664,9 @@ static void get_fileinfo ( } wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ - wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */ - if (wc == 0) { di = 0; break; } /* Buffer overflow? */ - di += wc; + nw = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow? */ + di += nw; #else /* ANSI/OEM output */ fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ #endif @@ -2760,10 +2697,10 @@ static void get_fileinfo ( if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ fno->fname[di++] = c; } - fno->fname[di] = 0; + fno->fname[di] = 0; /* Terminate the SFN */ #endif - fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fattrib = dp->dir[DIR_Attr] & AM_MASK; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ @@ -2778,7 +2715,10 @@ static void get_fileinfo ( /* Pattern matching */ /*-----------------------------------------------------------------------*/ -static DWORD get_achar ( /* Get a character and advances ptr */ +#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */ + + +static DWORD get_achar ( /* Get a character and advance ptr */ const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ ) { @@ -2809,41 +2749,43 @@ static DWORD get_achar ( /* Get a character and advances ptr */ } -static int pattern_matching ( /* 0:not matched, 1:matched */ +static int pattern_match ( /* 0:mismatched, 1:matched */ const TCHAR* pat, /* Matching pattern */ const TCHAR* nam, /* String to be tested */ - int skip, /* Number of pre-skip chars (number of ?s) */ - int inf /* Infinite search (* specified) */ + UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */ + UINT recur /* Recursion count */ ) { - const TCHAR *pp, *np; - DWORD pc, nc; - int nm, nx; + const TCHAR *pptr, *nptr; + DWORD pchr, nchr; + UINT sk; - while (skip--) { /* Pre-skip name chars */ + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + skip--; } - if (*pat == 0 && inf) return 1; /* (short circuit) */ + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ do { - pp = pat; np = nam; /* Top of pattern and name to match */ + pptr = pat; nptr = nam; /* Top of pattern and name to match */ for (;;) { - if (*pp == '?' || *pp == '*') { /* Wildcard? */ - nm = nx = 0; - do { /* Analyze the wildcard block */ - if (*pp++ == '?') nm++; else nx = 1; - } while (*pp == '?' || *pp == '*'); - if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ - nc = *np; break; /* Branch mismatched */ + if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */ + if (recur == 0) return 0; /* Too many wildcard terms? */ + sk = 0; + do { /* Analyze the wildcard term */ + if (*pptr++ == '?') sk++; else sk |= 0x100; + } while (*pptr == '?' || *pptr == '*'); + if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */ + nchr = *nptr; break; /* Branch mismatched */ } - pc = get_achar(&pp); /* Get a pattern char */ - nc = get_achar(&np); /* Get a name char */ - if (pc != nc) break; /* Branch mismatched? */ - if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + pchr = get_achar(&pptr); /* Get a pattern char */ + nchr = get_achar(&nptr); /* Get a name char */ + if (pchr != nchr) break; /* Branch mismatched? */ + if (pchr == 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 */ + } while (skip && nchr); /* Retry until end of name if infinite search is specified */ return 0; } @@ -2876,16 +2818,17 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ wc = (WCHAR)uc; - if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ - if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (wc < ' ' || IsSeparator(wc)) break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && strchr("*:<>|\"\?\x7F", (int)wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ - lfn[di++] = wc; /* Store the Unicode character */ + lfn[di++] = wc; /* Store the Unicode character */ } - if (wc < ' ') { /* End of path? */ - cf = NS_LAST; /* Set last segment flag */ - } else { - cf = 0; /* Next segment follows */ - while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + if (wc < ' ') { /* Stopped at end of the path? */ + cf = NS_LAST; /* Last segment */ + } else { /* Stopped at a separator */ + while (IsSeparator(*p)) p++; /* Skip duplicated separators if exist */ + cf = 0; /* Next segment may follow */ + if (IsTerminator(*p)) cf = NS_LAST; /* Ignore terminating separator */ } *path = p; /* Return pointer to the next segment */ @@ -2893,14 +2836,14 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr 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++) { /* Create dot name for SFN entry */ + 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 */ + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ return FR_OK; } #endif - while (di) { /* Snip off trailing spaces and dots if exist */ + while (di) { /* Snip off trailing spaces and dots if exist */ wc = lfn[di - 1]; if (wc != ' ' && wc != '.') break; di--; @@ -2913,7 +2856,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ - mem_set(dp->fn, ' ', 11); + memset(dp->fn, ' ', 11); i = b = 0; ni = 8; for (;;) { wc = lfn[si++]; /* Get an LFN character */ @@ -2934,20 +2877,20 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr continue; } - if (wc >= 0x80) { /* Is this a non-ASCII character? */ + if (wc >= 0x80) { /* Is this an extended character? */ cf |= NS_LFN; /* LFN entry needs to be created */ #if FF_CODE_PAGE == 0 - if (ExCvt) { /* At SBCS */ + if (ExCvt) { /* In SBCS cfg */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ - } else { /* At DBCS */ - wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ + } else { /* In DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */ } -#elif FF_CODE_PAGE < 900 /* SBCS cfg */ +#elif FF_CODE_PAGE < 900 /* In SBCS cfg */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ -#else /* DBCS cfg */ - wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ +#else /* In DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */ #endif } @@ -2958,7 +2901,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr } dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ } else { /* SBC */ - if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + if (wc == 0 || strchr("+,;=[]", (int)wc)) { /* Replace illegal characters for SFN */ wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ } else { if (IsUpper(wc)) { /* ASCII upper case? */ @@ -2993,7 +2936,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr /* Create file name in directory form */ p = *path; sfn = dp->fn; - mem_set(sfn, ' ', 11); + memset(sfn, ' ', 11); si = i = 0; ni = 8; #if FF_FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ @@ -3002,8 +2945,8 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (c != '.' || si >= 3) break; sfn[i++] = c; } - if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; - *path = p + si; /* Return pointer to the next segment */ + if (!IsSeparator(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 the path */ return FR_OK; } @@ -3011,8 +2954,8 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr for (;;) { c = (BYTE)p[si++]; /* Get a byte */ 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 */ + if (IsSeparator(c)) { /* Break if a separator is found */ + while (IsSeparator(p[si])) si++; /* Skip duplicated separator if exist */ break; } if (c == '.' || i >= ni) { /* End of body or field overflow? */ @@ -3035,16 +2978,16 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr sfn[i++] = c; sfn[i++] = d; } else { /* SBC */ - if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (strchr("*+,:;<=>[]|\"\?\x7F", (int)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 */ + *path = &p[si]; /* Return pointer to the next segment */ if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ 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 */ + sfn[NSFLAG] = (c <= ' ' || p[si] <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; #endif /* FF_USE_LFN */ @@ -3068,13 +3011,13 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ #if FF_FS_RPATH != 0 - if (*path != '/' && *path != '\\') { /* Without heading separator */ - dp->obj.sclust = fs->cdir; /* Start from current directory */ + if (!IsSeparator(*path) && (FF_STR_VOLUME_ID != 2 || !IsTerminator(*path))) { /* Without heading separator */ + dp->obj.sclust = fs->cdir; /* Start at the current directory */ } else #endif { /* With heading separator */ - while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ - dp->obj.sclust = 0; /* Start from root directory */ + while (IsSeparator(*path)) path++; /* Strip separators */ + dp->obj.sclust = 0; /* Start from the root directory */ } #if FF_FS_EXFAT dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */ @@ -3115,13 +3058,13 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ } break; } - if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ /* Get into the sub-directory */ - if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } #if FF_FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ dp->obj.c_scl = dp->obj.sclust; dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; dp->obj.c_ofs = dp->blk_ofs; @@ -3150,7 +3093,8 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb { const TCHAR *tp, *tt; TCHAR tc; - int i, vol = -1; + int i; + int vol = -1; #if FF_STR_VOLUME_ID /* Find string volume ID */ const char *sp; char c; @@ -3158,7 +3102,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb tt = tp = *path; if (!tp) return vol; /* Invalid path name? */ - do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */ + do tc = *tt++; while (!IsTerminator(tc) && tc != ':'); /* Find a colon in the path */ if (tc == ':') { /* DOS/Windows style volume ID? */ i = FF_VOLUMES; @@ -3185,21 +3129,22 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb return vol; } #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ - if (*tp == '/') { + if (*tp == '/') { /* Is there a volume ID? */ + while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */ i = 0; do { - sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + tt = tp; sp = VolumeStr[i]; /* Path name and this string volume ID */ do { /* Compare the volume ID with path name */ - c = *sp++; tc = *(++tp); + c = *sp++; tc = *(++tt); if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; } while (c && (TCHAR)c == tc); - } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ + } while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ vol = i; /* Drive number */ - *path = tp; /* Snip the drive prefix off */ - return vol; + *path = tt; /* Snip the drive prefix off */ } + return vol; } #endif /* No drive prefix is found */ @@ -3248,7 +3193,7 @@ static int test_gpt_header ( /* 0:Invalid, 1:Valid */ DWORD bcc; - if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */ + if (memcmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */ for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check header BCC */ bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); } @@ -3290,7 +3235,7 @@ static DWORD make_rand ( /* Check what the sector is */ -static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */ +static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */ FATFS* fs, /* Filesystem object */ LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */ ) @@ -3303,21 +3248,24 @@ static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */ sign = ld_word(fs->win + BS_55AA); #if FF_FS_EXFAT - if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ + if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ #endif b = fs->win[BS_JmpBoot]; if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */ - if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */ - /* FAT volumes formatted with early MS-DOS lack boot signature and FAT string, so that we need to identify the FAT VBR without them. */ + if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) { + return 0; /* It is an FAT32 VBR */ + } + /* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */ w = ld_word(fs->win + BPB_BytsPerSec); - if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) { /* Properness of sector size */ - b = fs->win[BPB_SecPerClus]; - if (b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size */ - && (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == 2) /* Properness of number of FATs */ - && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root entry count */ - && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size */ - return 0; /* Sector can be presumed an FAT VBR */ - } + b = fs->win[BPB_SecPerClus]; + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */ + && b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */ + && ld_word(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */ + && (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */ + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */ + && (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */ + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */ + return 0; /* It can be presumed an FAT VBR */ } } return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */ @@ -3336,8 +3284,8 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ DWORD mbr_pt[4]; - fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD */ - if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */ + fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format */ + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */ /* Sector 0 is not an FAT VBR or forced partition number wants a partition */ @@ -3353,7 +3301,7 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */ if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector */ ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector */ - if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */ + if (!memcmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */ v_ent++; fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status */ if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */ @@ -3421,7 +3369,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ } /* The filesystem object is not valid. */ - /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */ + /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */ fs->fs_type = 0; /* Clear the filesystem object */ fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */ @@ -3441,7 +3389,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ fmt = find_volume(fs, LD2PT(vol)); 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 */ - bsect = fs->winsect; /* Volume location */ + bsect = fs->winsect; /* Volume offset */ /* An FAT volume is found (bsect). Following code initializes the filesystem object */ @@ -3459,8 +3407,8 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ return FR_NO_FILESYSTEM; } - maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ - if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */ + if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed in 32-bit LBA) */ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ @@ -3468,7 +3416,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ 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) */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768 sectors) */ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ @@ -3489,11 +3437,11 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR; so++; } - if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ + if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ i = (i + SZDIRE) % SS(fs); /* Next entry */ } - bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ - if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; + bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */ fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */ for (;;) { /* Check if bitmap is contiguous */ if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR; @@ -3655,9 +3603,9 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ /*-----------------------------------------------------------------------*/ FRESULT f_mount ( - FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/ const TCHAR* path, /* Logical drive number to be mounted/unmounted */ - BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ + BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */ ) { FATFS *cfs; @@ -3705,14 +3653,14 @@ FRESULT f_mount ( FRESULT f_open ( FIL* fp, /* Pointer to the blank file object */ const TCHAR* path, /* Pointer to the file name */ - BYTE mode /* Access mode and file open mode flags */ + BYTE mode /* Access mode and open mode flags */ ) { FRESULT res; DIR dj; FATFS *fs; #if !FF_FS_READONLY - DWORD cl, bcs, clst; + DWORD cl, bcs, clst, tm; LBA_t sc; FSIZE_t ofs; #endif @@ -3765,8 +3713,8 @@ FRESULT f_open ( fp->obj.fs = fs; init_alloc_info(fs, &fp->obj); /* Set directory entry block initial state */ - mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ - mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ + memset(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ + memset(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ fs->dirbuf[XDIR_Attr] = AM_ARC; st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); fs->dirbuf[XDIR_GenFlags] = 1; @@ -3779,8 +3727,10 @@ FRESULT f_open ( #endif { /* Set directory entry initial state */ + tm = GET_FATTIME(); /* Set created time */ + st_dword(dj.dir + DIR_CrtTime, tm); + st_dword(dj.dir + DIR_ModTime, tm); cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ - st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ st_clust(fs, dj.dir, 0); /* Reset file allocation info */ st_dword(dj.dir + DIR_FileSize, 0); @@ -3842,17 +3792,17 @@ FRESULT f_open ( fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); } #if FF_USE_FASTSEEK - fp->cltbl = 0; /* Disable fast seek mode */ + fp->cltbl = 0; /* Disable fast seek mode */ #endif - fp->obj.fs = fs; /* Validate the file object */ + 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 */ + 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 !FF_FS_READONLY #if !FF_FS_TINY - mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ + memset(fp->buf, 0, sizeof fp->buf); /* 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 */ @@ -3875,6 +3825,9 @@ FRESULT f_open ( #endif } } +#if FF_FS_LOCK != 0 + if (res != FR_OK) dec_lock(fp->obj.lockid); /* Decrement file open counter if seek failed */ +#endif } #endif } @@ -3895,10 +3848,10 @@ FRESULT f_open ( /*-----------------------------------------------------------------------*/ FRESULT f_read ( - FIL* fp, /* Pointer to the file object */ - void* buff, /* Pointer to data buffer */ + FIL* fp, /* Open file to be read */ + void* buff, /* Data buffer to store the read data */ UINT btr, /* Number of bytes to read */ - UINT* br /* Pointer to number of bytes read */ + UINT* br /* Number of bytes read */ ) { FRESULT res; @@ -3917,8 +3870,7 @@ FRESULT f_read ( remain = fp->obj.objsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ - for ( ; btr; /* Repeat until btr bytes read */ - btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + for ( ; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */ 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? */ @@ -3950,11 +3902,11 @@ FRESULT f_read ( #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if FF_FS_TINY if (fs->wflag && fs->winsect - sect < cc) { - mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); } #else if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { - mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } #endif #endif @@ -3978,9 +3930,9 @@ FRESULT f_read ( if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #if FF_FS_TINY 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 */ + memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else - mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ + memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } @@ -3996,10 +3948,10 @@ FRESULT f_read ( /*-----------------------------------------------------------------------*/ FRESULT f_write ( - FIL* fp, /* Pointer to the file object */ - const void* buff, /* Pointer to the data to be written */ + FIL* fp, /* Open file to be written */ + const void* buff, /* Data to be written */ UINT btw, /* Number of bytes to write */ - UINT* bw /* Pointer to number of bytes written */ + UINT* bw /* Number of bytes written */ ) { FRESULT res; @@ -4020,8 +3972,7 @@ FRESULT f_write ( btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } - for ( ; btw; /* Repeat until all data written */ - btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + for ( ; btw > 0; btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { /* Repeat until all data written */ 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? */ @@ -4066,12 +4017,12 @@ FRESULT f_write ( #if FF_FS_MINIMIZE <= 2 #if FF_FS_TINY 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)); + memcpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); fs->wflag = 0; } #else 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)); + memcpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); fp->flag &= (BYTE)~FA_DIRTY; } #endif @@ -4097,10 +4048,10 @@ FRESULT f_write ( if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #if FF_FS_TINY 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 */ + memcpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fs->wflag = 1; #else - mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + memcpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fp->flag |= FA_DIRTY; #endif } @@ -4118,7 +4069,7 @@ FRESULT f_write ( /*-----------------------------------------------------------------------*/ FRESULT f_sync ( - FIL* fp /* Pointer to the file object */ + FIL* fp /* Open file to be synced */ ) { FRESULT res; @@ -4199,7 +4150,7 @@ FRESULT f_sync ( /*-----------------------------------------------------------------------*/ FRESULT f_close ( - FIL* fp /* Pointer to the file object to be closed */ + FIL* fp /* Open file to be closed */ ) { FRESULT res; @@ -4300,7 +4251,7 @@ FRESULT f_chdir ( } FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; -#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */ +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed if in Unix style volume ID */ if (res == FR_OK) { for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */ CurrVol = (BYTE)i; @@ -4424,7 +4375,8 @@ FRESULT f_lseek ( LBA_t nsect; FSIZE_t ifptr; #if FF_USE_FASTSEEK - DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; + DWORD cl, pcl, ncl, tcl, tlen, ulen; + DWORD *tbl; LBA_t dsc; #endif @@ -4719,9 +4671,9 @@ 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 (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */ #if FF_USE_LFN && FF_USE_FIND == 2 - if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */ #endif } return res; @@ -4869,9 +4821,11 @@ FRESULT f_getfree ( } while (--clst); } } - *nclst = nfree; /* Return the free clusters */ - fs->free_clst = nfree; /* Now free_clst is valid */ - fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + if (res == FR_OK) { /* Update parameters if succeeded */ + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } } } @@ -4982,12 +4936,12 @@ FRESULT f_unlink ( } if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ #if FF_FS_RPATH != 0 - if (dclst == fs->cdir) { /* Is it the current directory? */ + if (dclst == fs->cdir) { /* Is it the current directory? */ res = FR_DENIED; } else #endif { - sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.fs = fs; /* Open the sub-directory */ sdj.obj.sclust = dclst; #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { @@ -5062,12 +5016,12 @@ FRESULT f_mkdir ( res = dir_clear(fs, dcl); /* Clean up the new table */ if (res == FR_OK) { if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */ - mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ + memset(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ fs->win[DIR_Name] = '.'; fs->win[DIR_Attr] = AM_DIR; st_dword(fs->win + DIR_ModTime, tm); st_clust(fs, fs->win, dcl); - mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ + memcpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; st_clust(fs, fs->win + SZDIRE, pcl); fs->wflag = 1; @@ -5131,21 +5085,21 @@ FRESULT f_rename ( if (res == FR_OK) { djo.obj.fs = fs; INIT_NAMBUF(fs); - res = follow_path(&djo, path_old); /* Check old object */ + 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 FF_FS_LOCK != 0 if (res == FR_OK) { res = chk_lock(&djo, 2); } #endif - if (res == FR_OK) { /* Object to be renamed is found */ + if (res == FR_OK) { /* Object to be renamed is found */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ 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); + memcpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + memcpy(&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; @@ -5155,7 +5109,7 @@ FRESULT f_rename ( 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); /* Restore 85+C0 entry */ + memcpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; st_word(fs->dirbuf + XDIR_NameHash, nh); if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ @@ -5166,8 +5120,8 @@ FRESULT f_rename ( } else #endif { /* At FAT/FAT32 volume */ - mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ - mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + memcpy(&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; @@ -5176,7 +5130,7 @@ FRESULT f_rename ( res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { dir = djn.dir; /* Copy directory entry of the object except name */ - mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + memcpy(dir + 13, buf + 13, SZDIRE - 13); dir[DIR_Attr] = buf[DIR_Attr]; if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ fs->wflag = 1; @@ -5342,15 +5296,16 @@ FRESULT f_getlabel ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { WCHAR hs; + UINT nw; for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ wc = ld_word(dj.dir + XDIR_Label + si * 2); if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ hs = wc; continue; } - wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); - if (wc == 0) { di = 0; break; } - di += wc; + nw = put_utf((DWORD)hs << 16 | wc, &label[di], 4); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Encode error? */ + di += nw; hs = 0; } if (hs != 0) di = 0; /* Broken surrogate pair? */ @@ -5363,10 +5318,9 @@ FRESULT f_getlabel ( wc = dj.dir[si++]; #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ - wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ - if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */ - if (wc == 0) { di = 0; break; } - di += wc; + wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ + if (wc == 0) { di = 0; break; } /* Invalid char in current code page? */ + di += put_utf(wc, &label[di], 4); /* Store it in Unicode */ #else /* ANSI/OEM output */ label[di++] = (TCHAR)wc; #endif @@ -5390,10 +5344,12 @@ FRESULT f_getlabel ( if (res == FR_OK) { switch (fs->fs_type) { case FS_EXFAT: - di = BPB_VolIDEx; break; + di = BPB_VolIDEx; + break; case FS_FAT32: - di = BS_VolID32; break; + di = BS_VolID32; + break; default: di = BS_VolID; @@ -5422,7 +5378,7 @@ FRESULT f_setlabel ( BYTE dirvn[22]; UINT di; WCHAR wc; - static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ + static const char badchr[18] = "+.,;=[]" "/*:<>|\\\"\?\x7F"; /* [0..16] for FAT, [7..16] for exFAT */ #if FF_USE_LFN DWORD dc; #endif @@ -5433,7 +5389,7 @@ FRESULT f_setlabel ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - mem_set(dirvn, 0, 22); + memset(dirvn, 0, 22); di = 0; while ((UINT)*label >= ' ') { /* Create volume label */ dc = tchar2uni(&label); /* Get a Unicode character */ @@ -5444,7 +5400,7 @@ FRESULT f_setlabel ( st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; } } - if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ + if (dc == 0 || strchr(&badchr[7], (int)dc) || di >= 11) { /* Check validity of the volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } st_word(dirvn + di * 2, (WCHAR)dc); di++; @@ -5452,7 +5408,7 @@ FRESULT f_setlabel ( } else #endif { /* On the FAT/FAT32 volume */ - mem_set(dirvn, ' ', 11); + memset(dirvn, ' ', 11); di = 0; while ((UINT)*label >= ' ') { /* Create volume label */ #if FF_USE_LFN @@ -5468,7 +5424,7 @@ FRESULT f_setlabel ( if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ #endif #endif - if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + if (wc == 0 || strchr(&badchr[0], (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); @@ -5486,10 +5442,10 @@ FRESULT f_setlabel ( if (res == FR_OK) { if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { if (di != 0) { - mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + memcpy(dj.dir, dirvn, 11); /* Change the volume label */ } else { dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ } @@ -5502,14 +5458,14 @@ FRESULT f_setlabel ( if (di != 0) { /* Create a volume label entry */ res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { - mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + memset(dj.dir, 0, SZDIRE); /* Clean the entry */ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */ dj.dir[XDIR_NumLabel] = (BYTE)di; - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ - mem_cpy(dj.dir, dirvn, 11); + memcpy(dj.dir, dirvn, 11); } fs->wflag = 1; res = sync_fs(fs); @@ -5646,8 +5602,7 @@ FRESULT f_forward ( 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 goes busy */ - fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + for ( ; btf > 0 && (*func)(0, 0); fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { /* Repeat until all data transferred or stream goes busy */ 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? */ @@ -5691,7 +5646,7 @@ FRESULT f_forward ( #if !FF_FS_READONLY && FF_USE_MKFS /*-----------------------------------------------------------------------*/ -/* Create an FAT/exFAT volume */ +/* Create FAT/exFAT volume (with sub-functions) */ /*-----------------------------------------------------------------------*/ #define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */ @@ -5699,29 +5654,30 @@ FRESULT f_forward ( #define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */ -/* Create partitions on the physical drive */ +/* Create partitions on the physical drive in format of MBR or GPT */ static FRESULT create_partition ( BYTE drv, /* Physical drive number */ const LBA_t plst[], /* Partition list */ - UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */ + BYTE sys, /* System ID (for only MBR, temp setting) */ BYTE* buf /* Working buffer for a sector */ ) { UINT i, cy; LBA_t sz_drv; - DWORD sz_drv32, s_lba32, n_lba32; - BYTE *pte, hd, n_hd, sc, n_sc; + DWORD sz_drv32, nxt_alloc32, sz_part32; + BYTE *pte; + BYTE hd, n_hd, sc, n_sc; - /* Get drive size */ + /* Get physical drive size */ if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR; #if FF_LBA64 - if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT format */ WORD ss; - UINT sz_pt, pi, si, ofs; + UINT sz_ptbl, pi, si, ofs; DWORD bcc, rnd, align; - QWORD s_lba64, n_lba64, sz_pool, s_bpt; + QWORD nxt_alloc, sz_part, sz_pool, top_bpt; static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; #if FF_MAX_SS != FF_MIN_SS @@ -5730,103 +5686,103 @@ static FRESULT create_partition ( #else ss = FF_MAX_SS; #endif - rnd = GET_FATTIME(); /* Random seed */ - align = GPT_ALIGN / ss; /* Partition alignment [sector] */ - sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ - s_bpt = sz_drv - sz_pt - 1; /* Backup PT start sector */ - s_lba64 = 2 + sz_pt; /* First allocatable sector */ - sz_pool = s_bpt - s_lba64; /* Size of allocatable area */ - bcc = 0xFFFFFFFF; n_lba64 = 1; + rnd = (DWORD)sz_drv + GET_FATTIME(); /* Random seed */ + align = GPT_ALIGN / ss; /* Partition alignment for GPT [sector] */ + sz_ptbl = GPT_ITEMS * SZ_GPTE / ss; /* Size of partition table [sector] */ + top_bpt = sz_drv - sz_ptbl - 1; /* Backup partiiton table start sector */ + nxt_alloc = 2 + sz_ptbl; /* First allocatable sector */ + sz_pool = top_bpt - nxt_alloc; /* Size of allocatable area */ + bcc = 0xFFFFFFFF; sz_part = 1; pi = si = 0; /* partition table index, size table index */ do { - if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* Clean the buffer if needed */ - if (n_lba64 != 0) { /* Is the size table not termintated? */ - s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition start */ - n_lba64 = plst[si++]; /* Get a partition size */ - if (n_lba64 <= 100) { /* Is the size in percentage? */ - n_lba64 = sz_pool * n_lba64 / 100; - n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */ + if (pi * SZ_GPTE % ss == 0) memset(buf, 0, ss); /* Clean the buffer if needed */ + if (sz_part != 0) { /* Is the size table not termintated? */ + nxt_alloc = (nxt_alloc + align - 1) & ((QWORD)0 - align); /* Align partition start */ + sz_part = plst[si++]; /* Get a partition size */ + if (sz_part <= 100) { /* Is the size in percentage? */ + sz_part = sz_pool * sz_part / 100; + sz_part = (sz_part + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */ } - if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end of the pool */ - n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; + if (nxt_alloc + sz_part > top_bpt) { /* Clip the size at end of the pool */ + sz_part = (nxt_alloc < top_bpt) ? top_bpt - nxt_alloc : 0; } } - if (n_lba64 != 0) { /* Add a partition? */ + if (sz_part != 0) { /* Add a partition? */ ofs = pi * SZ_GPTE % ss; - mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Partition GUID (Microsoft Basic Data) */ - rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Unique partition GUID */ - st_qword(buf + ofs + GPTE_FstLba, s_lba64); /* Partition start LBA */ - st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 - 1); /* Partition end LBA */ - s_lba64 += n_lba64; /* Next partition LBA */ + memcpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Set partition GUID (Microsoft Basic Data) */ + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Set unique partition GUID */ + st_qword(buf + ofs + GPTE_FstLba, nxt_alloc); /* Set partition start sector */ + st_qword(buf + ofs + GPTE_LstLba, nxt_alloc + sz_part - 1); /* Set partition end sector */ + nxt_alloc += sz_part; /* Next allocatable sector */ } if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the buffer if it is filled up */ for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */ - if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Primary table */ - if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to primary table */ + if (disk_write(drv, buf, top_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to secondary table */ } } while (++pi < GPT_ITEMS); /* Create primary GPT header */ - mem_set(buf, 0, ss); - mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */ - st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */ - st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */ - st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of another header */ - st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA of first allocatable sector */ - st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA of last allocatable sector */ - st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */ - st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */ - st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */ + memset(buf, 0, ss); + memcpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */ + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */ + st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of secondary header */ + st_qword(buf + GPTH_FstLba, 2 + sz_ptbl); /* LBA of first allocatable sector */ + st_qword(buf + GPTH_LstLba, top_bpt - 1); /* LBA of last allocatable sector */ + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */ + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */ + st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */ rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ - st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; /* Create secondary GPT header */ - st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */ - st_qword(buf + GPTH_BakLba, 1); /* LBA of another header */ - st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of this table */ + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, 1); /* LBA of primary header */ + st_qword(buf + GPTH_PtOfs, top_bpt); /* LBA of this table */ st_dword(buf + GPTH_Bcc, 0); for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ - st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR; /* Create protective MBR */ - mem_set(buf, 0, ss); - mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */ + memset(buf, 0, ss); + memcpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */ st_word(buf + BS_55AA, 0xAA55); if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; } else #endif - { /* Create partitions in MBR */ + { /* Create partitions in MBR format */ sz_drv32 = (DWORD)sz_drv; - n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ + n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ; - if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ - mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ + memset(buf, 0, FF_MAX_SS); /* Clear MBR */ pte = buf + MBR_Table; /* Partition table in the MBR */ - for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { - n_lba32 = (DWORD)plst[i]; /* Get partition size */ - if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ - if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ - if (n_lba32 == 0) break; /* End of table or no sector to allocate? */ + for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) { + sz_part32 = (DWORD)plst[i]; /* Get partition size */ + if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */ + if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */ + if (sz_part32 == 0) break; /* End of table or no sector to allocate? */ - st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ - st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */ - pte[PTE_System] = (BYTE)sys; /* System type */ + st_dword(pte + PTE_StLba, nxt_alloc32); /* Start LBA */ + st_dword(pte + PTE_SizLba, sz_part32); /* Number of sectors */ + pte[PTE_System] = sys; /* System type */ - cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */ - hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ - sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ + cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Start cylinder */ + hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Start head */ + sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Start sector */ pte[PTE_StHead] = hd; pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_StCyl] = (BYTE)cy; - cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* End cylinder */ - hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* End head */ - sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* End sector */ + cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* End cylinder */ + hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* End head */ + sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* End sector */ pte[PTE_EdHead] = hd; pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_EdCyl] = (BYTE)cy; @@ -5855,7 +5811,7 @@ FRESULT f_mkfs ( static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */ BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; WORD ss; /* Sector size */ - DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n, vsn; LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */ LBA_t sect, lba[2]; DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */ @@ -5921,7 +5877,7 @@ FRESULT f_mkfs ( ofs = i = 0; while (n_ent) { /* Find MS Basic partition with order of ipart */ if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ - if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ + if (!memcmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ b_vol = ld_qword(buf + ofs + GPTE_FstLba); sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - b_vol + 1; break; @@ -5957,7 +5913,7 @@ FRESULT f_mkfs ( } if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ - /* Now start to create a FAT volume at b_vol and sz_vol */ + /* Now start to create an FAT volume at b_vol and sz_vol */ do { /* Pre-determine the FAT type */ if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */ @@ -5978,12 +5934,13 @@ FRESULT f_mkfs ( fsty = FS_FAT16; } while (0); + vsn = (DWORD)sz_vol + GET_FATTIME(); /* VSN generated from current time and partitiion size */ + #if FF_FS_EXFAT if (fsty == FS_EXFAT) { /* Create an exFAT volume */ - DWORD szb_bit, szb_case, sum, nb, cl, tbl[3]; + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; WCHAR ch, si; UINT j, st; - BYTE b; if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */ #if FF_USE_TRIM @@ -6004,12 +5961,12 @@ FRESULT f_mkfs ( if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ - szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ - tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */ + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */ /* Create a compressed up-case table */ - sect = b_data + sz_au * tbl[0]; /* Table start sector */ - sum = 0; /* Table checksum to be stored in the 82 entry */ + sect = b_data + sz_au * clen[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ st = 0; si = 0; i = 0; j = 0; szb_case = 0; do { switch (st) { @@ -6020,10 +5977,10 @@ FRESULT f_mkfs ( } 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 */ + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */ } st = 1; /* Do not compress short run */ - /* go to next case */ + /* FALLTHROUGH */ case 1: ch = si++; /* Fill the short run */ if (--j == 0) st = 0; @@ -6033,7 +5990,7 @@ FRESULT f_mkfs ( ch = (WCHAR)j; si += (WCHAR)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 + 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 == 0 || i == sz_buf * ss) { /* Write buffered data when buffer full or end of process */ @@ -6042,16 +5999,15 @@ FRESULT f_mkfs ( sect += n; i = 0; } } while (si); - tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */ - tbl[2] = 1; /* Number of root dir clusters */ + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */ + clen[2] = 1; /* Number of root dir clusters */ /* 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 */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */ + nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */ do { - mem_set(buf, 0, sz_buf * ss); - for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ; - for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ; + memset(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */ + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; @@ -6059,40 +6015,40 @@ FRESULT f_mkfs ( /* Initialize the FAT */ sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ - j = nb = cl = 0; + j = nbit = clu = 0; do { - mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write index */ - if (cl == 0) { /* Set FAT [0] and FAT[1] */ - st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; - st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + memset(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */ + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; } do { /* Create chains of bitmap, up-case and root dir */ - while (nb != 0 && i < sz_buf * ss) { /* Create a chain */ - st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); - i += 4; cl++; nb--; + while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */ + st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF); + i += 4; clu++; nbit--; } - if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */ - } while (nb != 0 && i < sz_buf * ss); + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */ + } while (nbit != 0 && i < sz_buf * ss); n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; } while (nsect); /* Initialize the root directory */ - mem_set(buf, 0, sz_buf * ss); - buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */ - buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ - st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ - st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ - buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ - st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ - st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */ - st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ - sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au; /* Start of the root directory and number of sectors */ + memset(buf, 0, sz_buf * ss); + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */ + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ + st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */ + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_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) LEAVE_MKFS(FR_DISK_ERR); - mem_set(buf, 0, ss); + memset(buf, 0, ss); /* Rest of entries are filled with zero */ sect += n; nsect -= n; } while (nsect); @@ -6100,16 +6056,16 @@ FRESULT f_mkfs ( 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 */ + memset(buf, 0, ss); + memcpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */ st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_DataOfsEx, (DWORD)(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_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, vsn); /* VSN */ st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem 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 = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ @@ -6122,14 +6078,14 @@ FRESULT f_mkfs ( } if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Extended bootstrap record (+1..+8) */ - mem_set(buf, 0, ss); + memset(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) LEAVE_MKFS(FR_DISK_ERR); } /* OEM/Reserved record (+9..+10) */ - mem_set(buf, 0, ss); + memset(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) LEAVE_MKFS(FR_DISK_ERR); @@ -6197,7 +6153,7 @@ FRESULT f_mkfs ( if (fsty == FS_FAT16) { if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ if (sz_au == 0 && (pau * 2) <= 64) { - sz_au = pau * 2; continue; /* Adjust cluster size and retry */ + sz_au = pau * 2; continue; /* Adjust cluster size and retry */ } if ((fsopt & FM_FAT32)) { fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */ @@ -6221,8 +6177,8 @@ FRESULT f_mkfs ( disk_ioctl(pdrv, CTRL_TRIM, lba); #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 */ + memset(buf, 0, ss); + memcpy(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 */ @@ -6238,20 +6194,20 @@ FRESULT f_mkfs ( st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */ if (fsty == FS_FAT32) { - st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID32, vsn); /* 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 */ + memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ } else { - st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID, vsn); /* 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 */ + memcpy(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) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ @@ -6259,7 +6215,7 @@ FRESULT f_mkfs ( /* Create FSINFO record if needed */ if (fsty == FS_FAT32) { disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ - mem_set(buf, 0, ss); + memset(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 */ @@ -6270,7 +6226,7 @@ FRESULT f_mkfs ( } /* Initialize FAT area */ - mem_set(buf, 0, sz_buf * ss); + memset(buf, 0, sz_buf * ss); sect = b_fat; /* FAT start sector */ for (i = 0; i < n_fat; i++) { /* Initialize FATs each */ if (fsty == FS_FAT32) { @@ -6284,7 +6240,7 @@ FRESULT f_mkfs ( do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); - mem_set(buf, 0, ss); /* Rest of FAT all are cleared */ + memset(buf, 0, ss); /* Rest of FAT all are cleared */ sect += n; nsect -= n; } while (nsect); } @@ -6324,8 +6280,8 @@ FRESULT f_mkfs ( if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ } } else { /* Volume as a new single partition */ - if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ - lba[0] = sz_vol, lba[1] = 0; + if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ + lba[0] = sz_vol; lba[1] = 0; fr = create_partition(pdrv, lba, sys, buf); if (fr != FR_OK) LEAVE_MKFS(fr); } @@ -6408,12 +6364,12 @@ TCHAR* f_gets ( if (rc != 1) break; /* EOF? */ wc = s[0]; if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ - f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ + f_read(fp, s, 1, &rc); /* Get 2nd byte */ if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong code? */ wc = wc << 8 | s[0]; } - dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ - if (dc == 0) continue; + dc = ff_oem2uni(wc, CODEPAGE); /* Convert ANSI/OEM into Unicode */ + if (dc == 0) continue; /* Conversion error? */ #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ f_read(fp, s, 2, &rc); /* Get a code unit */ if (rc != 2) break; /* EOF? */ @@ -6436,7 +6392,7 @@ TCHAR* f_gets ( if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte sequence? */ if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte sequence? */ if (ct == 0) continue; - f_read(fp, s, ct, &rc); /* Get trailing bytes */ + f_read(fp, s, ct, &rc); /* Get trailing bytes */ if (rc != ct) break; rc = 0; do { /* Merge the byte sequence */ @@ -6505,11 +6461,14 @@ TCHAR* f_gets ( #if !FF_FS_READONLY #include +#define SZ_PUTC_BUF 64 +#define SZ_NUM_BUF 32 + /*-----------------------------------------------------------------------*/ -/* Put a Character to the File (sub-functions) */ +/* Put a Character to the File (with sub-functions) */ /*-----------------------------------------------------------------------*/ -/* Putchar output buffer and work area */ +/* Output buffer and work area */ typedef struct { FIL *fp; /* Ptr to the writing file */ @@ -6520,11 +6479,11 @@ typedef struct { BYTE bs[4]; UINT wi, ct; #endif - BYTE buf[64]; /* Write buffer */ + BYTE buf[SZ_PUTC_BUF]; /* Write buffer */ } putbuff; -/* Buffered write with code conversion */ +/* Buffered file write with code conversion */ static void putc_bfd (putbuff* pb, TCHAR c) { @@ -6543,7 +6502,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) } i = pb->idx; /* Write index of pb->buf[] */ - if (i < 0) return; + if (i < 0) return; /* In write error? */ nc = pb->nchr; /* Write unit counter */ #if FF_USE_LFN && FF_LFN_UNICODE @@ -6642,7 +6601,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) pb->buf[i++] = (BYTE)wc; #endif -#else /* ANSI/OEM input (without re-encoding) */ +#else /* ANSI/OEM input (without re-encoding) */ pb->buf[i++] = (BYTE)c; #endif @@ -6664,7 +6623,7 @@ static int putc_flush (putbuff* pb) 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 -1; } @@ -6672,7 +6631,7 @@ static int putc_flush (putbuff* pb) static void putc_init (putbuff* pb, FIL* fp) { - mem_set(pb, 0, sizeof (putbuff)); + memset(pb, 0, sizeof (putbuff)); pb->fp = fp; } @@ -6715,8 +6674,129 @@ int f_puts ( /*-----------------------------------------------------------------------*/ -/* Put a Formatted String to the File */ +/* Put a Formatted String to the File (with sub-functions) */ /*-----------------------------------------------------------------------*/ +#if FF_PRINT_FLOAT && FF_INTDEF == 2 +#include + +static int ilog10 (double n) /* Calculate log10(n) in integer output */ +{ + int rv = 0; + + while (n >= 10) { /* Decimate digit in right shift */ + if (n >= 100000) { + n /= 100000; rv += 5; + } else { + n /= 10; rv++; + } + } + while (n < 1) { /* Decimate digit in left shift */ + if (n < 0.00001) { + n *= 100000; rv -= 5; + } else { + n *= 10; rv--; + } + } + return rv; +} + + +static double i10x (int n) /* Calculate 10^n in integer input */ +{ + double rv = 1; + + while (n > 0) { /* Left shift */ + if (n >= 5) { + rv *= 100000; n -= 5; + } else { + rv *= 10; n--; + } + } + while (n < 0) { /* Right shift */ + if (n <= -5) { + rv /= 100000; n += 5; + } else { + rv /= 10; n++; + } + } + return rv; +} + + +static void ftoa ( + char* buf, /* Buffer to output the floating point string */ + double val, /* Value to output */ + int prec, /* Number of fractional digits */ + TCHAR fmt /* Notation */ +) +{ + int d; + int e = 0, m = 0; + char sign = 0; + double w; + const char *er = 0; + const char ds = FF_PRINT_FLOAT == 2 ? ',' : '.'; + + + if (isnan(val)) { /* Not a number? */ + er = "NaN"; + } else { + if (prec < 0) prec = 6; /* Default precision? (6 fractional digits) */ + if (val < 0) { /* Nagative? */ + val = 0 - val; sign = '-'; + } else { + sign = '+'; + } + if (isinf(val)) { /* Infinite? */ + er = "INF"; + } else { + if (fmt == 'f') { /* Decimal notation? */ + val += i10x(0 - prec) / 2; /* Round (nearest) */ + m = ilog10(val); + if (m < 0) m = 0; + if (m + prec + 3 >= SZ_NUM_BUF) er = "OV"; /* Buffer overflow? */ + } else { /* E notation */ + if (val != 0) { /* Not a true zero? */ + val += i10x(ilog10(val) - prec) / 2; /* Round (nearest) */ + e = ilog10(val); + if (e > 99 || prec + 7 >= SZ_NUM_BUF) { /* Buffer overflow or E > +99? */ + er = "OV"; + } else { + if (e < -99) e = -99; + val /= i10x(e); /* Normalize */ + } + } + } + } + if (!er) { /* Not error condition */ + if (sign == '-') *buf++ = sign; /* Add a - if negative value */ + do { /* Put decimal number */ + if (m == -1) *buf++ = ds; /* Insert a decimal separator when get into fractional part */ + w = i10x(m); /* Snip the highest digit d */ + d = (int)(val / w); val -= d * w; + *buf++ = (char)('0' + d); /* Put the digit */ + } while (--m >= -prec); /* Output all digits specified by prec */ + if (fmt != 'f') { /* Put exponent if needed */ + *buf++ = (char)fmt; + if (e < 0) { + e = 0 - e; *buf++ = '-'; + } else { + *buf++ = '+'; + } + *buf++ = (char)('0' + e / 10); + *buf++ = (char)('0' + e % 10); + } + } + } + if (er) { /* Error condition */ + if (sign) *buf++ = sign; /* Add sign if needed */ + do *buf++ = *er++; while (*er); /* Put error symbol */ + } + *buf = 0; /* Term */ +} +#endif /* FF_PRINT_FLOAT && FF_INTDEF == 2 */ + + int f_printf ( FIL* fp, /* Pointer to the file object */ @@ -6726,10 +6806,16 @@ int f_printf ( { va_list arp; putbuff pb; - BYTE f, r; - UINT i, j, w; + UINT i, j, w, f, r; + int prec; +#if FF_PRINT_LLI && FF_INTDEF == 2 + QWORD v; +#else DWORD v; - TCHAR c, d, str[32], *p; +#endif + TCHAR tc, pad, *tp; + TCHAR nul = 0; + char d, str[SZ_NUM_BUF]; putc_init(&pb, fp); @@ -6737,88 +6823,122 @@ int f_printf ( va_start(arp, fmt); for (;;) { - c = *fmt++; - if (c == 0) break; /* End of string */ - if (c != '%') { /* Non escape character */ - putc_bfd(&pb, c); + tc = *fmt++; + if (tc == 0) break; /* End of format string */ + if (tc != '%') { /* Not an escape character (pass-through) */ + putc_bfd(&pb, tc); continue; } - w = f = 0; - c = *fmt++; - if (c == '0') { /* Flag: '0' padding */ - f = 1; c = *fmt++; - } else { - if (c == '-') { /* Flag: left justified */ - f = 2; c = *fmt++; - } + f = w = 0; pad = ' '; prec = -1; /* Initialize parms */ + tc = *fmt++; + if (tc == '0') { /* Flag: '0' padded */ + pad = '0'; tc = *fmt++; + } else if (tc == '-') { /* Flag: Left aligned */ + f = 2; tc = *fmt++; } - if (c == '*') { /* Minimum width by argument */ + if (tc == '*') { /* Minimum width from an argument */ w = va_arg(arp, int); - c = *fmt++; + tc = *fmt++; } else { - while (IsDigit(c)) { /* Minimum width */ - w = w * 10 + c - '0'; - c = *fmt++; + while (IsDigit(tc)) { /* Minimum width */ + w = w * 10 + tc - '0'; + tc = *fmt++; } } - if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ - f |= 4; c = *fmt++; - } - if (c == 0) break; - d = c; - if (IsLower(d)) d -= 0x20; - switch (d) { /* Atgument type is... */ - case 'S' : /* String */ - p = va_arg(arp, TCHAR*); - for (j = 0; p[j]; j++) ; - if (!(f & 2)) { /* Right padded */ - while (j++ < w) putc_bfd(&pb, ' ') ; + if (tc == '.') { /* Precision */ + tc = *fmt++; + if (tc == '*') { /* Precision from an argument */ + prec = va_arg(arp, int); + tc = *fmt++; + } else { + prec = 0; + while (IsDigit(tc)) { /* Precision */ + prec = prec * 10 + tc - '0'; + tc = *fmt++; + } } - while (*p) putc_bfd(&pb, *p++) ; /* String body */ - while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ - continue; - - case 'C' : /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; - - case 'B' : /* Unsigned binary */ + } + if (tc == 'l') { /* Size: long int */ + f |= 4; tc = *fmt++; +#if FF_PRINT_LLI && FF_INTDEF == 2 + if (tc == 'l') { /* Size: long long int */ + f |= 8; tc = *fmt++; + } +#endif + } + if (tc == 0) break; /* End of format string */ + switch (tc) { /* Atgument type is... */ + case 'b': /* Unsigned binary */ r = 2; break; - - case 'O' : /* Unsigned octal */ + case 'o': /* Unsigned octal */ r = 8; break; - - case 'D' : /* Signed decimal */ - case 'U' : /* Unsigned decimal */ + case 'd': /* Signed decimal */ + case 'u': /* Unsigned decimal */ r = 10; break; - - case 'X' : /* Unsigned hexdecimal */ + case 'x': /* Unsigned hexdecimal (lower case) */ + case 'X': /* Unsigned hexdecimal (upper case) */ r = 16; break; - + case 'c': /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); + continue; + case 's': /* String */ + tp = va_arg(arp, TCHAR*); /* Get a pointer argument */ + if (!tp) tp = &nul; /* Null ptr generates a null string */ + for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */ + if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */ + for ( ; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + while (*tp && prec--) putc_bfd(&pb, *tp++); /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ + continue; +#if FF_PRINT_FLOAT && FF_INTDEF == 2 + case 'f': /* Floating point (decimal) */ + case 'e': /* Floating point (e) */ + case 'E': /* Floating point (E) */ + ftoa(str, va_arg(arp, double), prec, tc); /* Make a flaoting point string */ + for (j = strlen(str); !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + for (i = 0; str[i]; putc_bfd(&pb, str[i++])) ; /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ + continue; +#endif default: /* Unknown type (pass-through) */ - putc_bfd(&pb, c); continue; + putc_bfd(&pb, tc); continue; } - /* Get an argument and put it in numeral */ - v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); - if (d == 'D' && (v & 0x80000000)) { - v = 0 - v; - f |= 8; + /* Get an integer argument and put it in numeral */ +#if FF_PRINT_LLI && FF_INTDEF == 2 + if (f & 8) { /* long long argument? */ + v = (QWORD)va_arg(arp, LONGLONG); + } else { + if (f & 4) { /* long argument? */ + v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, long) : (QWORD)va_arg(arp, unsigned long); + } else { /* int/short/char argument */ + v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, int) : (QWORD)va_arg(arp, unsigned int); + } } + if (tc == 'd' && (v & 0x8000000000000000)) { /* Negative value? */ + v = 0 - v; f |= 1; + } +#else + if (f & 4) { /* long argument? */ + v = (DWORD)va_arg(arp, long); + } else { /* int/short/char argument */ + v = (tc == 'd') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int); + } + if (tc == 'd' && (v & 0x80000000)) { /* Negative value? */ + v = 0 - v; f |= 1; + } +#endif i = 0; - do { - d = (TCHAR)(v % r); v /= r; - if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + do { /* Make an integer number string */ + d = (char)(v % r); v /= r; + if (d > 9) d += (tc == 'x') ? 0x27 : 0x07; str[i++] = d + '0'; - } while (v && i < sizeof str / sizeof *str); - if (f & 8) str[i++] = '-'; - j = i; d = (f & 1) ? '0' : ' '; - if (!(f & 2)) { - while (j++ < w) putc_bfd(&pb, d); /* Right pad */ - } - do { - putc_bfd(&pb, str[--i]); /* Number body */ - } while (i); - while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } while (v && i < SZ_NUM_BUF); + if (f & 1) str[i++] = '-'; /* Sign */ + /* Write it */ + for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ } va_end(arp); @@ -6840,8 +6960,8 @@ FRESULT f_setcp ( WORD cp /* Value to be set as active code page */ ) { - static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; - static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; + static const WORD validcp[22] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const BYTE* const tables[22] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct855, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; UINT i; diff --git a/source/fatfs/ffsystem.c b/source/fatfs/ffsystem.c index 1a9b33c..746cbc9 100644 --- a/source/fatfs/ffsystem.c +++ b/source/fatfs/ffsystem.c @@ -1,6 +1,6 @@ /*------------------------------------------------------------------------*/ /* Sample Code of OS Dependent Functions for FatFs */ -/* (C)ChaN, 2018 */ +/* (C)ChaN, 2018 */ /*------------------------------------------------------------------------*/ #include @@ -19,7 +19,6 @@ void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if no return malloc(msize); /* Allocate a new memory block with POSIX API */ } - /*------------------------------------------------------------------------*/ /* Free a memory block */ /*------------------------------------------------------------------------*/ @@ -32,3 +31,136 @@ void ff_memfree ( } #endif + +#if FF_FS_REENTRANT /* Mutal exclusion */ + +/*------------------------------------------------------------------------*/ +/* Create a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to create a new +/ synchronization object for the volume, such as semaphore and mutex. +/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR. +*/ + +//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ + + +int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ + BYTE vol, /* Corresponding volume (logical drive number) */ + FF_SYNC_t* sobj /* Pointer to return the created sync object */ +) +{ + /* Win32 */ + *sobj = CreateMutex(NULL, FALSE, NULL); + return (int)(*sobj != INVALID_HANDLE_VALUE); + + /* uITRON */ +// T_CSEM csem = {TA_TPRI,1,1}; +// *sobj = acre_sem(&csem); +// return (int)(*sobj > 0); + + /* uC/OS-II */ +// OS_ERR err; +// *sobj = OSMutexCreate(0, &err); +// return (int)(err == OS_NO_ERR); + + /* FreeRTOS */ +// *sobj = xSemaphoreCreateMutex(); +// return (int)(*sobj != NULL); + + /* CMSIS-RTOS */ +// *sobj = osMutexCreate(&Mutex[vol]); +// return (int)(*sobj != NULL); +} + + +/*------------------------------------------------------------------------*/ +/* Delete a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to delete a synchronization +/ object that created with ff_cre_syncobj() function. When a 0 is returned, +/ the f_mount() function fails with FR_INT_ERR. +*/ + +int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ + FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ +) +{ + /* Win32 */ + return (int)CloseHandle(sobj); + + /* uITRON */ +// return (int)(del_sem(sobj) == E_OK); + + /* uC/OS-II */ +// OS_ERR err; +// OSMutexDel(sobj, OS_DEL_ALWAYS, &err); +// return (int)(err == OS_NO_ERR); + + /* FreeRTOS */ +// vSemaphoreDelete(sobj); +// return 1; + + /* CMSIS-RTOS */ +// return (int)(osMutexDelete(sobj) == osOK); +} + + +/*------------------------------------------------------------------------*/ +/* Request Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on entering file functions to lock the volume. +/ When a 0 is returned, the file function fails with FR_TIMEOUT. +*/ + +int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ + FF_SYNC_t sobj /* Sync object to wait */ +) +{ + /* Win32 */ + return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); + + /* uITRON */ +// return (int)(wai_sem(sobj) == E_OK); + + /* uC/OS-II */ +// OS_ERR err; +// OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); +// return (int)(err == OS_NO_ERR); + + /* FreeRTOS */ +// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); + + /* CMSIS-RTOS */ +// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); +} + + +/*------------------------------------------------------------------------*/ +/* Release Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on leaving file functions to unlock the volume. +*/ + +void ff_rel_grant ( + FF_SYNC_t sobj /* Sync object to be signaled */ +) +{ + /* Win32 */ + ReleaseMutex(sobj); + + /* uITRON */ +// sig_sem(sobj); + + /* uC/OS-II */ +// OSMutexPost(sobj); + + /* FreeRTOS */ +// xSemaphoreGive(sobj); + + /* CMSIS-RTOS */ +// osMutexRelease(sobj); +} + +#endif + diff --git a/source/main.cpp b/source/main.cpp index ed29e1f..2f8e733 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -28,10 +28,6 @@ #include "sample_installer_page.hpp" #include "sample_loading_page.hpp" -int g_argc = 0; -char **g_argv = NULL; -const char *g_appLaunchPath = NULL; - namespace i18n = brls::i18n; // for loadTranslations() and getStr() using namespace i18n::literals; // for _i18n @@ -48,10 +44,7 @@ std::vector NOTIFICATIONS = { int main(int argc, char* argv[]) { - g_argc = argc; - g_argv = argv; - - if (!utilsInitializeResources()) + if (!utilsInitializeResources(argc, (const char**)argv)) { utilsCloseResources(); return EXIT_FAILURE; diff --git a/todo.txt b/todo.txt index cad9df0..46cfe17 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,33 @@ +todo: + + log: verbosity levels + log: nxlink output for advanced users + + nca: signature verification + nca: support for compressed fs sections? + nca: support for sparse sections? + + usb: improve usbIsReady + + title: orphan system titles + title: come up with a better way to handle gamecard titles + title: more functions for title lookup? (filters, patches / aoc, etc.) + title: more functions for content lookup? (based on id) + title: parse the update partition from gamecards (if available) to generate ncmcontentinfo data for all update titles + + gamecard: functions to display filelist + + pfs0: functions to display filelist + + romfs: functions to display filelist + + bktr: functions to display filelist (wrappers for romfs functions tbh) + + others: config load/save using json + others: dump verification via nswdb / no-intro + others: update application feature + others: fatfs browser for emmc partitions + reminder: list of top level functions designed to alter nca data in order of (possible) usage: @@ -48,34 +78,3 @@ minor steps to take into account: * check if rights_id_available == true and titlekey_retrieved == false (preload handling) * actually, just inform the user about it - this is being handled - - - - - - - - - -todo: - - nca: support for compressed fs sections? - nca: support for sparse sections? - - gamecard: functions to display filelist - - pfs0: functions to display filelist - - romfs: functions to display filelist - - bktr: functions to display filelist (wrappers for romfs functions tbh) - - title: more functions for title lookup? (filters, patches / aoc, etc.) - title: more functions for content lookup? (based on id) - title: parse the update partition from gamecards (if available) to generate ncmcontentinfo data for all update titles - - others: config load/save using json - others: dump verification via nswdb / no-intro - others: update application feature - others: fatfs browser for emmc partitions - \ No newline at end of file