mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-22 16:42:11 +00:00
Implement JSON configuration handler.
* Thread-safe. * Provides getter/setter functions for the data types used by nxdumptool's configuration. * Each setter function writes the modified JSON configuration back to the SD card. * Configuration is validated on interface initialization. If validation fails, a default JSON template is loaded from the application's RomFS and written back to the SD card. Other changes: * Implement directory creation. * Moved more preprocessor definitions to defines.h. * Replaced strtok() calls throughout the code with strtok_r() to guarantee thread-safety.
This commit is contained in:
parent
a2a401d36d
commit
64ab1705bc
15 changed files with 600 additions and 60 deletions
5
Makefile
5
Makefile
|
@ -84,15 +84,14 @@ CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" -DGIT_R
|
|||
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 json-c --cflags`
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++20 -Wno-volatile -Wno-unused-parameter
|
||||
|
||||
ASFLAGS := -g -gdwarf-4 $(ARCH)
|
||||
LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs -g -gdwarf-4 $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfs -lntfs-3g -llwext4 -lnx
|
||||
#LIBS += -ljson-c
|
||||
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -ljson-c -lz -lusbhsfs -lntfs-3g -llwext4 -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
|
|
63
include/core/config.h
Normal file
63
include/core/config.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* config.h
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ConfigDumpDestination_SdCard = 0,
|
||||
ConfigDumpDestination_UsbHost = 1,
|
||||
ConfigDumpDestination_Count = 2
|
||||
} ConfigDumpDestination;
|
||||
|
||||
typedef enum {
|
||||
ConfigChecksumLookupMethod_None = 0,
|
||||
ConfigChecksumLookupMethod_NSWDB = 1,
|
||||
ConfigChecksumLookupMethod_NoIntro = 2,
|
||||
ConfigChecksumLookupMethod_Count = 3
|
||||
} ConfigChecksumLookupMethod;
|
||||
|
||||
/// Initializes the configuration interface.
|
||||
bool configInitialize(void);
|
||||
|
||||
/// Closes the configuration interface.
|
||||
void configExit(void);
|
||||
|
||||
/// Getters and setters for various data types.
|
||||
/// Path elements must be separated using forward slashes.
|
||||
|
||||
bool configGetBoolean(const char *path);
|
||||
void configSetBoolean(const char *path, bool value);
|
||||
|
||||
int configGetInteger(const char *path);
|
||||
void configSetInteger(const char *path, int value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CONFIG_H__ */
|
|
@ -56,6 +56,9 @@
|
|||
/* File-based logger. */
|
||||
#include "nxdt_log.h"
|
||||
|
||||
/* Configuration handler. */
|
||||
#include "config.h"
|
||||
|
||||
/* USB Mass Storage support. */
|
||||
#include "ums.h"
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ 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);
|
||||
|
||||
/// Creates output directories in the specified device.
|
||||
/// If 'device' is NULL, output directories will be created on the SD card.
|
||||
void utilsCreateOutputDirectories(const char *device);
|
||||
|
||||
/// Returns true if a file exists.
|
||||
bool utilsCheckIfFileExists(const char *path);
|
||||
|
||||
|
|
|
@ -65,9 +65,36 @@
|
|||
|
||||
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */
|
||||
|
||||
/* Other defines. */
|
||||
#define UTF8_BOM "\xEF\xBB\xBF"
|
||||
#define CRLF "\r\n"
|
||||
|
||||
#define HBMENU_BASE_PATH "/switch/"
|
||||
#define APP_BASE_PATH HBMENU_BASE_PATH APP_TITLE "/"
|
||||
|
||||
#define GAMECARD_PATH APP_BASE_PATH "Gamecard/"
|
||||
#define CERT_PATH APP_BASE_PATH "Certificate/"
|
||||
#define HFS_PATH APP_BASE_PATH "HFS/"
|
||||
#define NSP_PATH APP_BASE_PATH "NSP/"
|
||||
#define TICKET_PATH APP_BASE_PATH "Ticket/"
|
||||
#define NCA_PATH APP_BASE_PATH "NCA/"
|
||||
#define NCA_FS_PATH APP_BASE_PATH "NCA FS/"
|
||||
|
||||
#define CONFIG_PATH "sdmc:" APP_BASE_PATH "config.json"
|
||||
#define DEFAULT_CONFIG_PATH "romfs:/default_config.json"
|
||||
|
||||
#define NRO_NAME APP_TITLE ".nro"
|
||||
#define NRO_PATH APP_BASE_PATH NRO_NAME
|
||||
|
||||
#define NSWDB_XML_PATH APP_BASE_PATH "NSWreleases.xml"
|
||||
|
||||
#define KEYS_FILE_PATH "sdmc:" HBMENU_BASE_PATH "prod.keys" /* Location used by Lockpick_RCM. */
|
||||
|
||||
#define LOG_FILE_NAME APP_TITLE ".log"
|
||||
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
|
||||
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
|
||||
|
||||
#define NXLINK_TIMEOUT 2000
|
||||
|
||||
#define APP_BASE_PATH "sdmc:/switch/" APP_TITLE "/"
|
||||
#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:"
|
||||
|
||||
#define GITHUB_REPOSITORY_URL "https://github.com/DarkMatterCore/nxdumptool"
|
||||
|
@ -80,4 +107,6 @@
|
|||
|
||||
#define DISCORD_SERVER_URL "https://discord.gg/SCbbcQx"
|
||||
|
||||
#define LOCKPICK_RCM_URL "https://github.com/shchmue/Lockpick_RCM"
|
||||
|
||||
#endif /* __DEFINES_H__ */
|
||||
|
|
29
romfs/default_config.json
Normal file
29
romfs/default_config.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"overclock": true,
|
||||
"name_convention": 0,
|
||||
"dump_destination": 0,
|
||||
"gamecard": {
|
||||
"append_key_area": false,
|
||||
"keep_certificate": false,
|
||||
"trim_dump": false,
|
||||
"calculate_checksum": true,
|
||||
"checksum_lookup_method": 1
|
||||
},
|
||||
"nsp": {
|
||||
"set_download_distribution": false,
|
||||
"remove_console_data": true,
|
||||
"remove_titlekey_crypto": false,
|
||||
"replace_acid_key_sig": false,
|
||||
"disable_linked_account_requirement": false,
|
||||
"enable_screenshots": false,
|
||||
"enable_video_capture": false,
|
||||
"disable_hdcp": false,
|
||||
"lookup_checksum": true
|
||||
},
|
||||
"ticket": {
|
||||
"remove_console_data": true
|
||||
},
|
||||
"nca_fs": {
|
||||
"use_layeredfs_dir": false
|
||||
}
|
||||
}
|
|
@ -300,7 +300,7 @@ static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst
|
|||
}
|
||||
|
||||
u32 i = 0;
|
||||
char issuer_copy[0x40] = {0};
|
||||
char issuer_copy[0x40] = {0}, *pch = NULL, *state = NULL;
|
||||
bool success = true;
|
||||
|
||||
dst->count = certGetCertificateCountInSignatureIssuer(issuer);
|
||||
|
@ -317,11 +317,11 @@ static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Copy string to avoid problems with strtok(). */
|
||||
/* Copy string to avoid problems with strtok_r(). */
|
||||
/* The "Root-" parent from the issuer string is skipped. */
|
||||
snprintf(issuer_copy, 0x40, "%s", issuer + 5);
|
||||
snprintf(issuer_copy, sizeof(issuer_copy), "%s", issuer + 5);
|
||||
|
||||
char *pch = strtok(issuer_copy, "-");
|
||||
pch = strtok_r(issuer_copy, "-", &state);
|
||||
while(pch != NULL)
|
||||
{
|
||||
if (!_certRetrieveCertificateByName(&(dst->certs[i]), pch))
|
||||
|
@ -332,7 +332,7 @@ static bool _certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst
|
|||
}
|
||||
|
||||
i++;
|
||||
pch = strtok(NULL, "-");
|
||||
pch = strtok_r(NULL, "-", &state);
|
||||
}
|
||||
|
||||
if (!success) certFreeCertificateChain(dst);
|
||||
|
@ -345,17 +345,17 @@ static u32 certGetCertificateCountInSignatureIssuer(const char *issuer)
|
|||
if (!issuer || !*issuer) return 0;
|
||||
|
||||
u32 count = 0;
|
||||
char issuer_copy[0x40] = {0};
|
||||
char issuer_copy[0x40] = {0}, *pch = NULL, *state = NULL;
|
||||
|
||||
/* Copy string to avoid problems with strtok(). */
|
||||
/* Copy string to avoid problems with strtok_r(). */
|
||||
/* The "Root-" parent from the issuer string is skipped. */
|
||||
snprintf(issuer_copy, 0x40, issuer + 5);
|
||||
snprintf(issuer_copy, sizeof(issuer_copy), "%s", issuer + 5);
|
||||
|
||||
char *pch = strtok(issuer_copy, "-");
|
||||
pch = strtok_r(issuer_copy, "-", &state);
|
||||
while(pch != NULL)
|
||||
{
|
||||
count++;
|
||||
pch = strtok(NULL, "-");
|
||||
pch = strtok_r(NULL, "-", &state);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
|
372
source/core/config.c
Normal file
372
source/core/config.c
Normal file
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* config.c
|
||||
*
|
||||
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
||||
*
|
||||
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
||||
*
|
||||
* nxdumptool is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* nxdumptool is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nxdt_utils.h"
|
||||
#include "config.h"
|
||||
#include "title.h"
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
#define JSON_VALIDATE_FIELD(type, name, ...) \
|
||||
if (!strcmp(key, #name)) { \
|
||||
if (name##_found || !configValidateJson##type(val, ##__VA_ARGS__)) goto end; \
|
||||
name##_found = true; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define JSON_VALIDATE_OBJECT(type, name) \
|
||||
if (!strcmp(key, #name)) { \
|
||||
if (name##_found || !configValidateJson##type##Object(val)) goto end; \
|
||||
name##_found = true; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define JSON_GETTER(functype, vartype, jsontype, ...) \
|
||||
vartype configGet##functype(const char *path) { \
|
||||
vartype ret = (vartype)0; \
|
||||
SCOPED_LOCK(&g_configMutex) { \
|
||||
struct json_object *obj = configGetJsonObjectByPath(g_configJson, path); \
|
||||
if (!obj || !configValidateJson##functype(obj, ##__VA_ARGS__)) break; \
|
||||
ret = (vartype)json_object_get_##jsontype(obj); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define JSON_SETTER(functype, vartype, jsontype, ...) \
|
||||
void configSet##functype(const char *path, vartype value) { \
|
||||
SCOPED_LOCK(&g_configMutex) { \
|
||||
struct json_object *obj = configGetJsonObjectByPath(g_configJson, path); \
|
||||
if (!obj || !configValidateJson##functype(obj, ##__VA_ARGS__)) break; \
|
||||
if (json_object_set_##jsontype(obj, value)) { \
|
||||
configWriteConfigJson(); \
|
||||
} else { \
|
||||
LOG_MSG("Failed to update \"%s\"!", path); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static Mutex g_configMutex = 0;
|
||||
static bool g_configInterfaceInit = false;
|
||||
|
||||
static struct json_object *g_configJson = NULL;
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static bool configParseConfigJson(void);
|
||||
static void configWriteConfigJson(void);
|
||||
static void configFreeConfigJson(void);
|
||||
|
||||
static struct json_object *configGetJsonObjectByPath(const struct json_object *obj, const char *path);
|
||||
|
||||
static bool configValidateJsonRootObject(const struct json_object *obj);
|
||||
static bool configValidateJsonGameCardObject(const struct json_object *obj);
|
||||
static bool configValidateJsonNspObject(const struct json_object *obj);
|
||||
static bool configValidateJsonTicketObject(const struct json_object *obj);
|
||||
static bool configValidateJsonNcaFsObject(const struct json_object *obj);
|
||||
|
||||
NX_INLINE bool configValidateJsonBoolean(const struct json_object *obj);
|
||||
NX_INLINE bool configValidateJsonInteger(const struct json_object *obj, int lower_boundary, int upper_boundary);
|
||||
NX_INLINE bool configValidateJsonObject(const struct json_object *obj);
|
||||
|
||||
static void configLogJsonError(void);
|
||||
|
||||
bool configInitialize(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
SCOPED_LOCK(&g_configMutex)
|
||||
{
|
||||
ret = g_configInterfaceInit;
|
||||
if (ret) break;
|
||||
|
||||
/* Parse JSON config. */
|
||||
if (!configParseConfigJson())
|
||||
{
|
||||
LOG_MSG("Failed to parse JSON configuration!");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update flags. */
|
||||
ret = g_configInterfaceInit = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void configExit(void)
|
||||
{
|
||||
SCOPED_LOCK(&g_configMutex)
|
||||
{
|
||||
/* Free JSON object. */
|
||||
/* We don't need to write it back to the SD card - setter functions do that on their own. */
|
||||
configFreeConfigJson();
|
||||
|
||||
/* Update flag. */
|
||||
g_configInterfaceInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_GETTER(Boolean, bool, boolean);
|
||||
JSON_SETTER(Boolean, bool, boolean);
|
||||
|
||||
JSON_GETTER(Integer, int, int, INT32_MIN, INT32_MAX);
|
||||
JSON_SETTER(Integer, int, int, INT32_MIN, INT32_MAX);
|
||||
|
||||
static bool configParseConfigJson(void)
|
||||
{
|
||||
bool use_default_config = true, ret = false;
|
||||
|
||||
/* Read config JSON. */
|
||||
g_configJson = json_object_from_file(CONFIG_PATH);
|
||||
if (!g_configJson)
|
||||
{
|
||||
configLogJsonError();
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Validate configuration. */
|
||||
ret = configValidateJsonRootObject(g_configJson);
|
||||
use_default_config = !ret;
|
||||
|
||||
end:
|
||||
if (use_default_config)
|
||||
{
|
||||
/* Free config JSON. */
|
||||
configFreeConfigJson();
|
||||
|
||||
/* Read default config JSON. */
|
||||
g_configJson = json_object_from_file(DEFAULT_CONFIG_PATH);
|
||||
if (g_configJson)
|
||||
{
|
||||
configWriteConfigJson();
|
||||
ret = true;
|
||||
} else {
|
||||
configLogJsonError();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void configWriteConfigJson(void)
|
||||
{
|
||||
if (!g_configJson) return;
|
||||
if (json_object_to_file_ext(CONFIG_PATH, g_configJson, JSON_C_TO_STRING_PRETTY) != 0) configLogJsonError();
|
||||
}
|
||||
|
||||
static void configFreeConfigJson(void)
|
||||
{
|
||||
if (!g_configJson) return;
|
||||
json_object_put(g_configJson);
|
||||
g_configJson = NULL;
|
||||
}
|
||||
|
||||
static struct json_object *configGetJsonObjectByPath(const struct json_object *obj, const char *path)
|
||||
{
|
||||
const struct json_object *parent_obj = obj;
|
||||
struct json_object *child_obj = NULL;
|
||||
char *path_dup = NULL, *pch = NULL, *state = NULL;
|
||||
|
||||
if (!configValidateJsonObject(obj) || !path || !*path)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Duplicate path to avoid problems with strtok_r(). */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pch = strtok_r(path_dup, "/", &state);
|
||||
if (!pch)
|
||||
{
|
||||
LOG_MSG("Failed to tokenize input path! (\"%s\").", path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
while(pch)
|
||||
{
|
||||
if (!json_object_object_get_ex(parent_obj, pch, &child_obj))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve JSON object by key for \"%s\"! (\"%s\").", pch, path);
|
||||
break;
|
||||
}
|
||||
|
||||
pch = strtok_r(NULL, "/", &state);
|
||||
if (pch)
|
||||
{
|
||||
parent_obj = child_obj;
|
||||
child_obj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (path_dup) free(path_dup);
|
||||
|
||||
return child_obj;
|
||||
}
|
||||
|
||||
static bool configValidateJsonRootObject(const struct json_object *obj)
|
||||
{
|
||||
bool ret = false, overclock_found = false, name_convention_found = false, dump_destination_found = false, gamecard_found = false;
|
||||
bool nsp_found = false, ticket_found = false, nca_fs_found = false;
|
||||
|
||||
if (!configValidateJsonObject(obj)) goto end;
|
||||
|
||||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, overclock);
|
||||
JSON_VALIDATE_FIELD(Integer, name_convention, TitleFileNameConvention_Full, TitleFileNameConvention_IdAndVersionOnly);
|
||||
JSON_VALIDATE_FIELD(Integer, dump_destination, ConfigDumpDestination_SdCard, ConfigDumpDestination_UsbHost);
|
||||
JSON_VALIDATE_OBJECT(GameCard, gamecard);
|
||||
JSON_VALIDATE_OBJECT(Nsp, nsp);
|
||||
JSON_VALIDATE_OBJECT(Ticket, ticket);
|
||||
JSON_VALIDATE_OBJECT(NcaFs, nca_fs);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = (overclock_found && name_convention_found && dump_destination_found && gamecard_found && nsp_found && ticket_found && nca_fs_found);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool configValidateJsonGameCardObject(const struct json_object *obj)
|
||||
{
|
||||
bool ret = false, append_key_area_found = false, keep_certificate_found = false, trim_dump_found = false, calculate_checksum_found = false, checksum_lookup_method_found = false;
|
||||
|
||||
if (!configValidateJsonObject(obj)) goto end;
|
||||
|
||||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, append_key_area);
|
||||
JSON_VALIDATE_FIELD(Boolean, keep_certificate);
|
||||
JSON_VALIDATE_FIELD(Boolean, trim_dump);
|
||||
JSON_VALIDATE_FIELD(Boolean, calculate_checksum);
|
||||
JSON_VALIDATE_FIELD(Integer, checksum_lookup_method, ConfigChecksumLookupMethod_None, ConfigChecksumLookupMethod_NoIntro);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = (append_key_area_found && keep_certificate_found && trim_dump_found && calculate_checksum_found && checksum_lookup_method_found);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool configValidateJsonNspObject(const struct json_object *obj)
|
||||
{
|
||||
bool ret = false, set_download_distribution_found = false, remove_console_data_found = false, remove_titlekey_crypto_found = false, replace_acid_key_sig_found = false;
|
||||
bool disable_linked_account_requirement_found = false, enable_screenshots_found = false, enable_video_capture_found = false, disable_hdcp_found = false, lookup_checksum_found = false;
|
||||
|
||||
if (!configValidateJsonObject(obj)) goto end;
|
||||
|
||||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, set_download_distribution);
|
||||
JSON_VALIDATE_FIELD(Boolean, remove_console_data);
|
||||
JSON_VALIDATE_FIELD(Boolean, remove_titlekey_crypto);
|
||||
JSON_VALIDATE_FIELD(Boolean, replace_acid_key_sig);
|
||||
JSON_VALIDATE_FIELD(Boolean, disable_linked_account_requirement);
|
||||
JSON_VALIDATE_FIELD(Boolean, enable_screenshots);
|
||||
JSON_VALIDATE_FIELD(Boolean, enable_video_capture);
|
||||
JSON_VALIDATE_FIELD(Boolean, disable_hdcp);
|
||||
JSON_VALIDATE_FIELD(Boolean, lookup_checksum);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = (set_download_distribution_found && remove_console_data_found && remove_titlekey_crypto_found && replace_acid_key_sig_found && disable_linked_account_requirement_found && \
|
||||
enable_screenshots_found && enable_video_capture_found && disable_hdcp_found && lookup_checksum_found);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool configValidateJsonTicketObject(const struct json_object *obj)
|
||||
{
|
||||
bool ret = false, remove_console_data_found = false;
|
||||
|
||||
if (!configValidateJsonObject(obj)) goto end;
|
||||
|
||||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, remove_console_data);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = remove_console_data_found;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool configValidateJsonNcaFsObject(const struct json_object *obj)
|
||||
{
|
||||
bool ret = false, use_layeredfs_dir_found = false;
|
||||
|
||||
if (!configValidateJsonObject(obj)) goto end;
|
||||
|
||||
json_object_object_foreach(obj, key, val)
|
||||
{
|
||||
JSON_VALIDATE_FIELD(Boolean, use_layeredfs_dir);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = use_layeredfs_dir_found;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
NX_INLINE bool configValidateJsonBoolean(const struct json_object *obj)
|
||||
{
|
||||
return (obj != NULL && json_object_is_type(obj, json_type_boolean));
|
||||
}
|
||||
|
||||
NX_INLINE bool configValidateJsonInteger(const struct json_object *obj, int lower_boundary, int upper_boundary)
|
||||
{
|
||||
if (!obj || !json_object_is_type(obj, json_type_int) || lower_boundary > upper_boundary) return false;
|
||||
int val = json_object_get_int(obj);
|
||||
return (val >= lower_boundary && val <= upper_boundary);
|
||||
}
|
||||
|
||||
NX_INLINE bool configValidateJsonObject(const struct json_object *obj)
|
||||
{
|
||||
return (obj != NULL && json_object_is_type(obj, json_type_object) && json_object_object_length(obj) > 0);
|
||||
}
|
||||
|
||||
static void configLogJsonError(void)
|
||||
{
|
||||
size_t str_len = 0;
|
||||
char *str = (char*)json_util_get_last_err(); /* Drop the const. */
|
||||
if (!str || !(str_len = strlen(str))) return;
|
||||
|
||||
/* Remove line breaks. */
|
||||
if (str[str_len - 1] == '\n') str[--str_len] = '\0';
|
||||
if (str[str_len - 1] == '\r') str[--str_len] = '\0';
|
||||
|
||||
/* Log error message. */
|
||||
LOG_MSG("%s", str);
|
||||
}
|
|
@ -27,8 +27,6 @@
|
|||
#include "nca.h"
|
||||
#include "rsa.h"
|
||||
|
||||
#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM. */
|
||||
|
||||
#define ETICKET_RSA_DEVICE_KEY_PUBLIC_EXPONENT 0x10001
|
||||
|
||||
/* Type definitions. */
|
||||
|
|
|
@ -21,10 +21,6 @@
|
|||
|
||||
#include "nxdt_utils.h"
|
||||
|
||||
#define LOG_FILE_NAME APP_TITLE ".log"
|
||||
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
|
||||
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static Mutex g_logMutex = 0;
|
||||
|
@ -37,9 +33,7 @@ static s64 g_logFileOffset = 0;
|
|||
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.%09lu] %s -> ";
|
||||
static const char *g_logLineBreak = "\r\n";
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
|
@ -121,7 +115,7 @@ __attribute__((format(printf, 4, 5))) void logWriteFormattedStringToBuffer(char
|
|||
/* Generate formatted string. */
|
||||
sprintf(dst_ptr + dst_str_len, 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);
|
||||
vsprintf(dst_ptr + dst_str_len + (size_t)str1_len, fmt, args);
|
||||
strcat(dst_ptr, g_logLineBreak);
|
||||
strcat(dst_ptr, CRLF);
|
||||
|
||||
end:
|
||||
va_end(args);
|
||||
|
@ -141,7 +135,7 @@ __attribute__((format(printf, 4, 5))) void logWriteBinaryDataToLogFile(const voi
|
|||
|
||||
/* Generate hex string representation. */
|
||||
utilsGenerateHexStringFromData(data_str, data_str_size, data, data_size, true);
|
||||
strcat(data_str, g_logLineBreak);
|
||||
strcat(data_str, CRLF);
|
||||
|
||||
SCOPED_LOCK(&g_logMutex)
|
||||
{
|
||||
|
@ -322,7 +316,7 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name,
|
|||
/* Nice and easy string formatting using the log buffer. */
|
||||
sprintf(g_logBuffer + g_logBufferLength, 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);
|
||||
vsprintf(g_logBuffer + g_logBufferLength + (size_t)str1_len, fmt, args);
|
||||
strcat(g_logBuffer, g_logLineBreak);
|
||||
strcat(g_logBuffer, CRLF);
|
||||
g_logBufferLength += log_str_len;
|
||||
} else {
|
||||
/* Flush log buffer. */
|
||||
|
@ -336,7 +330,7 @@ static void _logWriteFormattedStringToLogFile(bool save, const char *func_name,
|
|||
/* 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);
|
||||
vsprintf(tmp_str + (size_t)str1_len, fmt, args);
|
||||
strcat(tmp_str, g_logLineBreak);
|
||||
strcat(tmp_str, CRLF);
|
||||
|
||||
/* Write formatted string data until it no longer exceeds the log buffer size. */
|
||||
while(log_str_len >= LOG_BUF_SIZE)
|
||||
|
@ -432,8 +426,8 @@ static bool logOpenLogFile(void)
|
|||
/* Write UTF-8 BOM right away (if needed). */
|
||||
if (!g_logFileOffset)
|
||||
{
|
||||
size_t utf8_bom_len = strlen(g_utf8Bom);
|
||||
fsFileWrite(&g_logFile, g_logFileOffset, g_utf8Bom, utf8_bom_len, FsWriteOption_Flush);
|
||||
size_t utf8_bom_len = strlen(UTF8_BOM);
|
||||
fsFileWrite(&g_logFile, g_logFileOffset, UTF8_BOM, utf8_bom_len, FsWriteOption_Flush);
|
||||
g_logFileOffset += (s64)utf8_bom_len;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -65,6 +65,20 @@ static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes);
|
|||
static const char g_illegalFileSystemChars[] = "\\/:*?\"<>|";
|
||||
static const size_t g_illegalFileSystemCharsLength = (MAX_ELEMENTS(g_illegalFileSystemChars) - 1);
|
||||
|
||||
static const char *g_outputDirs[] = {
|
||||
HBMENU_BASE_PATH,
|
||||
APP_BASE_PATH,
|
||||
GAMECARD_PATH,
|
||||
CERT_PATH,
|
||||
HFS_PATH,
|
||||
NSP_PATH,
|
||||
TICKET_PATH,
|
||||
NCA_PATH,
|
||||
NCA_FS_PATH
|
||||
};
|
||||
|
||||
static const size_t g_outputDirsCount = MAX_ELEMENTS(g_outputDirs);
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static void _utilsGetLaunchPath(int program_argc, const char **program_argv);
|
||||
|
@ -87,7 +101,7 @@ static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_
|
|||
bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
||||
{
|
||||
Result rc = 0;
|
||||
bool ret = false, flag = false;
|
||||
bool ret = false;
|
||||
|
||||
SCOPED_LOCK(&g_resourcesMutex)
|
||||
{
|
||||
|
@ -129,6 +143,9 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
g_programAppletType = appletGetAppletType();
|
||||
LOG_MSG("Running under %s mode.", _utilsAppletModeCheck() ? "applet" : "title override");
|
||||
|
||||
/* Create output directories (SD card only). */
|
||||
utilsCreateOutputDirectories(NULL);
|
||||
|
||||
/* Initialize USB interface. */
|
||||
if (!usbInitialize()) break;
|
||||
|
||||
|
@ -160,22 +177,34 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
|
|||
/* Mount eMMC BIS System partition. */
|
||||
if (!utilsMountEmmcBisSystemPartitionStorage()) break;
|
||||
|
||||
/* Enable video recording. */
|
||||
rc = appletIsGamePlayRecordingSupported(&flag);
|
||||
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
|
||||
/* Mount application RomFS. */
|
||||
rc = romfsInit();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
LOG_MSG("Failed to mount RomFS container!");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable screen dimming and auto sleep. */
|
||||
/* TODO: only use this function right before starting a dump procedure - make sure to handle power button presses as well. */
|
||||
appletSetMediaPlaybackState(true);
|
||||
/* Load configuration. */
|
||||
if (!configInitialize()) break;
|
||||
|
||||
/* Overclock system. */
|
||||
utilsOverclockSystem(true);
|
||||
utilsOverclockSystem(configGetBoolean("overclock"));
|
||||
|
||||
/* 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();
|
||||
/* Enable video recording if we're running under title override mode. */
|
||||
if (!_utilsAppletModeCheck())
|
||||
{
|
||||
bool flag = false;
|
||||
rc = appletIsGamePlayRecordingSupported(&flag);
|
||||
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
|
||||
}
|
||||
|
||||
/* Disable screen dimming and auto sleep. */
|
||||
/* TODO: only use this function while dealing with a dump process - make sure to handle power button presses as well. */
|
||||
appletSetMediaPlaybackState(true);
|
||||
|
||||
/* Redirect stdout and stderr over network to nxlink. */
|
||||
rc = socketInitializeDefault();
|
||||
|
@ -203,8 +232,12 @@ void utilsCloseResources(void)
|
|||
|
||||
socketExit();
|
||||
|
||||
/* Unmount application RomFS. */
|
||||
romfsExit();
|
||||
/* Enable screen dimming and auto sleep. */
|
||||
/* TODO: only use this function while dealing with a dump process - make sure to handle power button presses as well. */
|
||||
appletSetMediaPlaybackState(false);
|
||||
|
||||
/* Unblock HOME button presses. */
|
||||
utilsChangeHomeButtonBlockStatus(false);
|
||||
|
||||
/* Unset our overclock applet hook. */
|
||||
appletUnhook(&g_systemOverclockCookie);
|
||||
|
@ -212,11 +245,11 @@ void utilsCloseResources(void)
|
|||
/* Restore hardware clocks. */
|
||||
utilsOverclockSystem(false);
|
||||
|
||||
/* Enable screen dimming and auto sleep. */
|
||||
appletSetMediaPlaybackState(false);
|
||||
/* Close configuration interface. */
|
||||
configExit();
|
||||
|
||||
/* Unblock HOME button presses. */
|
||||
utilsChangeHomeButtonBlockStatus(false);
|
||||
/* Unmount application RomFS. */
|
||||
romfsExit();
|
||||
|
||||
/* Unmount eMMC BIS System partition. */
|
||||
utilsUnmountEmmcBisSystemPartitionStorage();
|
||||
|
@ -574,6 +607,24 @@ bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_fr
|
|||
return true;
|
||||
}
|
||||
|
||||
void utilsCreateOutputDirectories(const char *device)
|
||||
{
|
||||
size_t device_len = 0;
|
||||
char path[FS_MAX_PATH] = {0};
|
||||
|
||||
if (device && (!(device_len = strlen(device)) || device[device_len - 1] != ':'))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < g_outputDirsCount; i++)
|
||||
{
|
||||
sprintf(path, "%s%s", (device ? device : "sdmc:"), g_outputDirs[i]);
|
||||
mkdir(path, 0744);
|
||||
}
|
||||
}
|
||||
|
||||
bool utilsCheckIfFileExists(const char *path)
|
||||
{
|
||||
if (!path || !*path) return false;
|
||||
|
@ -844,8 +895,7 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param)
|
|||
|
||||
if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return;
|
||||
|
||||
/* TODO: read config here to actually know the value to use with utilsOverclockSystem. */
|
||||
utilsOverclockSystem(true);
|
||||
utilsOverclockSystem(configGetBoolean("overclock"));
|
||||
}
|
||||
|
||||
static void utilsPrintConsoleError(void)
|
||||
|
|
|
@ -276,7 +276,7 @@ bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectory
|
|||
RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
char *path_dup = NULL, *pch = NULL;
|
||||
char *path_dup = NULL, *pch = NULL, *state = NULL;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !path || *path != '/' || !(path_len = strlen(path)) || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0)))
|
||||
|
@ -288,14 +288,14 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
/* Check if the root directory was requested. */
|
||||
if (path_len == 1) return dir_entry;
|
||||
|
||||
/* Duplicate path to avoid problems with strtok(). */
|
||||
/* Duplicate path to avoid problems with strtok_r(). */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOG_MSG("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pch = strtok(path_dup, "/");
|
||||
pch = strtok_r(path_dup, "/", &state);
|
||||
if (!pch)
|
||||
{
|
||||
LOG_MSG("Failed to tokenize input path! (\"%s\").", path);
|
||||
|
@ -311,7 +311,7 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
break;
|
||||
}
|
||||
|
||||
pch = strtok(NULL, "/");
|
||||
pch = strtok_r(NULL, "/", &state);
|
||||
}
|
||||
|
||||
end:
|
||||
|
|
|
@ -1434,7 +1434,7 @@ static bool titleGenerateMetadataEntriesFromSystemTitles(void)
|
|||
/* Fill information. */
|
||||
const TitleSystemEntry *system_title = &(g_systemTitles[extra_app_count]);
|
||||
cur_app_metadata->title_id = system_title->title_id;
|
||||
sprintf(cur_app_metadata->lang_entry.name, system_title->name);
|
||||
sprintf(cur_app_metadata->lang_entry.name, "%s", system_title->name);
|
||||
|
||||
/* Set application metadata entry pointer. */
|
||||
g_systemMetadata[g_systemMetadataCount + extra_app_count] = cur_app_metadata;
|
||||
|
|
|
@ -66,11 +66,7 @@ namespace nxdt::views
|
|||
is_system(is_system)
|
||||
{
|
||||
/* Set sublabel. */
|
||||
if (!this->is_system)
|
||||
{
|
||||
this->setSubLabel(std::string(app_metadata->lang_entry.author));
|
||||
this->setHeight(brls::Application::getStyle()->List.Item.heightWithSubLabel);
|
||||
}
|
||||
if (!this->is_system) this->setSubLabel(std::string(app_metadata->lang_entry.author));
|
||||
|
||||
/* Set thumbnail (if needed). */
|
||||
if (app_metadata->icon && app_metadata->icon_size) this->setThumbnail(app_metadata->icon, app_metadata->icon_size);
|
||||
|
@ -102,10 +98,8 @@ namespace nxdt::views
|
|||
|
||||
void TitlesTab::PopulateList(const nxdt::tasks::TitleApplicationMetadataVector* app_metadata)
|
||||
{
|
||||
if (!app_metadata) return;
|
||||
|
||||
/* Populate variables. */
|
||||
size_t app_metadata_count = app_metadata->size();
|
||||
size_t app_metadata_count = (app_metadata ? app_metadata->size() : 0);
|
||||
bool update_focused_view = this->IsListItemFocused();
|
||||
int focus_stack_index = this->GetFocusStackViewIndex();
|
||||
|
||||
|
|
5
todo.txt
5
todo.txt
|
@ -18,6 +18,11 @@ todo:
|
|||
|
||||
bktr: functions to display filelist (wrappers for romfs functions tbh)
|
||||
|
||||
usb: change buffer size?
|
||||
usb: change chunk size?
|
||||
usb: improve abi (make it rest-like?)
|
||||
usb: improve cancel mechanism
|
||||
|
||||
others: config load/save using json
|
||||
others: dump verification via nswdb / no-intro
|
||||
others: update application feature
|
||||
|
|
Loading…
Reference in a new issue