1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 10:16:39 +00:00

Small code refactor (part 2).

* Rewrote mutex handling throughout the code to use a small, macro-based scoped lock implementation.

* Removed extern variables from common.h - launch path management is now completely handled in utils.c.

* Updated NpdmSystemCallId_Count to reflect changes introduced in 12.0.0.

* Added NcaMainSignatureKeyGeneration enum.

* NCA main signature moduli are now retrieved from FS .rodata at runtime.

* Simplified lock management in usb.c by using a single global mutex with scoped locks instead of three different r/w locks.

* Updated FatFs to R0.14b.

* Enabled 64-bit LBA support in FatFs to potentially support custom eMMC replacements / resized USER partitions in the future.

* Updated LZ4 to v1.9.3.

* Fixed typos.

* USB gamecard dumper PoC now only dumps the Initial Data area.

* Updated to-do list.
This commit is contained in:
Pablo Curiel 2021-05-18 08:32:43 -04:00
parent 85f146f50c
commit f82d7a3db4
36 changed files with 3122 additions and 2845 deletions

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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__ */

View file

@ -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 <stdint.h>
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 <stdint.h>
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")

View file

@ -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.

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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().

View file

@ -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 <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#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 <stdint.h>
@ -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

View file

@ -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. */

View file

@ -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)

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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. */

View file

@ -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);

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*------------------------------------------------------------------------*/
/* Sample Code of OS Dependent Functions for FatFs */
/* (C)ChaN, 2018 */
/* (C)ChaN, 2018 */
/*------------------------------------------------------------------------*/
#include <stdlib.h>
@ -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

View file

@ -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<std::string> 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;

View file

@ -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