Many small changes (sorry, I need to push these).

* Use forward declarations for type definitions wherever suitable (e.g. NcaContext, BucketTreeContext).
* Replace references to "DumpDestination" with "OutputStorage".
* Replace references to "append key area" with "prepend key area".
* Update LZ4 to v1.9.4.
* Update Material Icons font to 2022-08-01.
* RootView: change USB-related icons, reflect how many UMS devices are currently connected to the console, provide a way for child views to retrieve the populated UMS devices vector.
* Tasks: implement GetUmsDevices().
* Update borealis.
* Update libusbhsfs.
* Begin implementation of a DumpOptionsFrame class to display configurable options before starting a new dump process.
* bktr: rename bktrIsValidSubstorage() to bktrIsValidSubStorage(), use bktrIsValidSubStorage() in bktrInitializeCompressedStorageContext(), fixed a bug in bktrReadCompressedStorage() where Sparse substorages wouldn't be allowed.
* GamecardTab: push a DumpOptionsFrame object onto the view stack if the XCI option is clicked.
This commit is contained in:
Pablo Curiel 2022-09-12 20:19:10 +02:00
parent 3cbdb5dc65
commit 7bb708e394
32 changed files with 1085 additions and 343 deletions

View File

@ -161,14 +161,17 @@ typedef enum {
BucketTreeSubStorageType_Count = 5 ///< Total values supported by this enum.
} BucketTreeSubStorageType;
// Forward declaration for BucketTreeSubStorage.
typedef struct _BucketTreeContext BucketTreeContext;
typedef struct {
u8 index; ///< Substorage index.
NcaFsSectionContext *nca_fs_ctx; ///< NCA FS section context. Used to perform operations on the target NCA.
u8 type; ///< BucketTreeSubStorageType.
void *bktr_ctx; ///< BucketTreeContext related to this storage. Only used if type > BucketTreeSubStorageType_Regular.
BucketTreeContext *bktr_ctx; ///< BucketTreeContext related to this storage. Only used if type > BucketTreeSubStorageType_Regular.
} BucketTreeSubStorage;
typedef struct {
struct _BucketTreeContext {
NcaFsSectionContext *nca_fs_ctx; ///< NCA FS section context. Used to perform operations on the target NCA.
u8 storage_type; ///< BucketTreeStorageType.
BucketTreeTable *storage_table; ///< Pointer to the dynamically allocated Bucket Tree Table for this storage.
@ -181,7 +184,7 @@ typedef struct {
u64 start_offset; ///< Virtual storage start offset.
u64 end_offset; ///< Virtual storage end offset.
BucketTreeSubStorage substorages[BKTR_MAX_SUBSTORAGE_COUNT]; ///< Substorages required for this BucketTree storage. May be set after initializing this context.
} BucketTreeContext;
};
/// Initializes a Bucket Tree context using the provided NCA FS section context and a storage type.
/// 'storage_type' may only be BucketTreeStorageType_Indirect, BucketTreeStorageType_AesCtrEx or BucketTreeStorageType_Sparse.
@ -231,11 +234,11 @@ NX_INLINE bool bktrIsBlockWithinStorageRange(BucketTreeContext *ctx, u64 size, u
return (bktrIsValidContext(ctx) && size > 0 && ctx->start_offset <= offset && size <= (ctx->end_offset - offset));
}
NX_INLINE bool bktrIsValidSubstorage(BucketTreeSubStorage *substorage)
NX_INLINE bool bktrIsValidSubStorage(BucketTreeSubStorage *substorage)
{
return (substorage && substorage->index < BKTR_MAX_SUBSTORAGE_COUNT && substorage->nca_fs_ctx && substorage->type < BucketTreeSubStorageType_Count && \
((substorage->type == BucketTreeSubStorageType_Regular && substorage->index == 0 && !substorage->bktr_ctx) || \
(substorage->type > BucketTreeSubStorageType_Regular && substorage->bktr_ctx)));
(substorage->type > BucketTreeSubStorageType_Regular && substorage->bktr_ctx && substorage->bktr_ctx->nca_fs_ctx == substorage->nca_fs_ctx)));
}
#ifdef __cplusplus

View File

@ -31,10 +31,10 @@ extern "C" {
#endif
typedef enum {
ConfigDumpDestination_SdCard = 0,
ConfigDumpDestination_UsbHost = 1,
ConfigDumpDestination_Count = 2
} ConfigDumpDestination;
ConfigOutputStorage_SdCard = 0,
ConfigOutputStorage_UsbHost = 1,
ConfigOutputStorage_Count = 2
} ConfigOutputStorage;
typedef enum {
ConfigChecksumLookupMethod_None = 0,

View File

@ -1,7 +1,7 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-present, Yann Collet.
* Copyright (C) 2011-2020, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@ -97,36 +97,77 @@ extern "C" {
# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
/*! LZ4_FREESTANDING :
* When this macro is set to 1, it enables "freestanding mode" that is
* suitable for typical freestanding environment which doesn't support
* standard C library.
*
* - LZ4_FREESTANDING is a compile-time switch.
* - It requires the following macros to be defined:
* LZ4_memcpy, LZ4_memmove, LZ4_memset.
* - It only enables LZ4/HC functions which don't use heap.
* All LZ4F_* functions are not supported.
* - See tests/freestanding.c to check its basic setup.
*/
#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
# define LZ4_HEAPMODE 0
# define LZ4HC_HEAPMODE 0
# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
# if !defined(LZ4_memcpy)
# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
# endif
# if !defined(LZ4_memset)
# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
# endif
# if !defined(LZ4_memmove)
# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
# endif
#elif ! defined(LZ4_FREESTANDING)
# define LZ4_FREESTANDING 0
#endif
/*------ 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 3 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
/*-************************************
* Tuning parameter
**************************************/
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
* Increasing memory usage improves compression ratio.
* Reduced memory usage may improve speed, thanks to better cache locality.
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )
* Increasing memory usage improves compression ratio, at the cost of speed.
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
#ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE 14
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
#endif
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
# error "LZ4_MEMORY_USAGE is too small !"
#endif
#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
# error "LZ4_MEMORY_USAGE is too large !"
#endif
/*-************************************
* Simple Functions
@ -270,8 +311,25 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcS
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/**
Note about RC_INVOKED
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
- Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
and reports warning "RC4011: identifier truncated".
- To eliminate the warning, we surround long preprocessor symbol with
"#if !defined(RC_INVOKED) ... #endif" block that means
"skip this block when rc.exe is trying to read it".
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_resetStream_fast() : v1.9.0+
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
@ -355,8 +413,12 @@ typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
* creation / destruction of streaming decompression tracking context.
* A tracking context can be re-used multiple times.
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_setStreamDecode() :
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
@ -406,7 +468,10 @@ LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
LZ4LIB_API int
LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
const char* src, char* dst,
int srcSize, int dstCapacity);
/*! LZ4_decompress_*_usingDict() :
@ -417,7 +482,16 @@ LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecod
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
LZ4LIB_API int
LZ4_decompress_safe_usingDict(const char* src, char* dst,
int srcSize, int dstCapacity,
const char* dictStart, int dictSize);
LZ4LIB_API int
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
int compressedSize,
int targetOutputSize, int maxOutputSize,
const char* dictStart, int dictSize);
#endif /* LZ4_H_2983827168210 */
@ -496,13 +570,15 @@ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const c
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the first compression call on the stream.
*/
LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream);
LZ4LIB_STATIC_API void
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! In-place compression and decompression
*
* It's possible to have input and output sharing the same buffer,
* for highly contrained memory environments.
* for highly constrained memory environments.
* In both cases, it requires input to lay at the end of the buffer,
* and decompression to start at beginning of the buffer.
* Buffer size must feature some margin, hence be larger than final size.
@ -592,38 +668,26 @@ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const
typedef unsigned int LZ4_u32;
#endif
/*! LZ4_stream_t :
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_stream_t object.
**/
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 currentOffset;
LZ4_u32 tableType;
LZ4_u32 dictSize;
/* Implicit padding to ensure structure is aligned */
};
typedef struct {
const LZ4_byte* externalDict;
size_t extDictSize;
const LZ4_byte* prefixEnd;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
/*! LZ4_stream_t :
* 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 future versions.
*/
#define LZ4_STREAMSIZE 16416 /* static size, for inter-version compatibility */
#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*))
#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */
union LZ4_stream_u {
void* table[LZ4_STREAMSIZE_VOIDP];
char minStateSize[LZ4_STREAM_MINSIZE];
LZ4_stream_t_internal internal_donotuse;
}; /* previously typedef'd to LZ4_stream_t */
@ -641,21 +705,25 @@ union LZ4_stream_u {
* In which case, the function will @return NULL.
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead
*/
**/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
/*! LZ4_streamDecode_t :
* information structure to track an LZ4 stream during decompression.
* init this structure using LZ4_setStreamDecode() before first use.
* note : only use in association with static linking !
* this definition is not API/ABI safe,
* and may change in a future version !
*/
#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ )
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
**/
typedef struct {
const LZ4_byte* externalDict;
const LZ4_byte* prefixEnd;
size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#define LZ4_STREAMDECODE_MINSIZE 32
union LZ4_streamDecode_u {
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
char minStateSize[LZ4_STREAMDECODE_MINSIZE];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */

View File

@ -358,11 +358,14 @@ typedef enum {
NcaFsSectionType_Invalid = 4
} NcaFsSectionType;
// Forward declaration for NcaFsSectionContext.
typedef struct _NcaContext NcaContext;
/// Unlike NCA contexts, we don't need to keep a hash for the NCA FS section header in NCA FS section contexts.
/// This is because the functions that modify the NCA FS section header also update the NCA FS section header hash stored in the NCA header.
typedef struct {
bool enabled; ///< Set to true if this NCA FS section has passed all validation checks and can be safely used.
void *nca_ctx; ///< NcaContext. Used to perform NCA reads.
NcaContext *nca_ctx; ///< NcaContext. Used to perform NCA reads.
NcaFsHeader header; ///< Plaintext NCA FS section header.
NcaFsHeader encrypted_header; ///< Encrypted NCA FS section header. If the plaintext NCA FS section header is modified, this will hold an encrypted copy of it.
///< Otherwise, this holds the unmodified, encrypted NCA FS section header.
@ -418,7 +421,7 @@ typedef struct {
NXDT_ASSERT(NcaDecryptedKeyArea, NCA_KEY_AREA_USED_SIZE);
typedef struct {
struct _NcaContext {
u8 storage_id; ///< NcmStorageId.
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD.
u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
@ -448,7 +451,7 @@ typedef struct {
void *content_type_ctx; ///< Pointer to a content type context (e.g. ContentMetaContext, ProgramInfoContext, NacpContext, LegalInfoContext). Set to NULL if unused.
bool content_type_ctx_patch; ///< Set to true if a NCA patch generated by the content type context is needed and hasn't been completely written yet.
u32 content_type_ctx_data_idx; ///< Start index for the data generated by the content type context. Used while creating NSPs.
} NcaContext;
};
typedef struct {
bool written; ///< Set to true if this patch has already been written.
@ -478,7 +481,7 @@ void ncaFreeCryptoBuffer(void);
/// Initializes a NCA context.
/// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value.
/// If the NCA holds a populated Rights ID field, ticket data will need to be retrieved.
/// If the 'tik' argument points to a valid Ticket element, it will either be updated (if it's empty) or be used to read ticket data that has already been retrieved.
/// If the 'tik' argument points to a valid Ticket element, it will either be updated (if it's empty) or used to read ticket data that has already been retrieved.
/// If the 'tik' argument is NULL, the function will just retrieve the necessary ticket data on its own.
/// If ticket data can't be retrieved, the context will still be initialized, but anything that involves working with encrypted NCA FS section blocks won't be possible (e.g. ncaReadFsSection()).
bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentInfo *content_info, u32 title_version, Ticket *tik);

View File

@ -157,7 +157,7 @@ NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx,
{
if (!ctx || !ncaStorageIsValidContext(&(ctx->storage_ctx)) || ctx->nca_fs_ctx != ctx->storage_ctx.nca_fs_ctx || \
ctx->storage_ctx.base_storage_type != NcaStorageBaseStorageType_Regular) return;
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
ncaWriteHierarchicalSha256PatchToMemoryBuffer(ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
}
NX_INLINE void pfsFreeEntryPatch(NcaHierarchicalSha256Patch *patch)

View File

@ -281,7 +281,7 @@ NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx,
(!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
(patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
NcaContext *nca_ctx = (NcaContext*)ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;
NcaContext *nca_ctx = ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;
if (patch->use_old_format_patch)
{

View File

@ -121,7 +121,8 @@
#define GITHUB_API_RELEASE_URL GITHUB_API_URL "/repos/" GITHUB_REPOSITORY "/releases/latest"
#define NSWDB_XML_URL "http://nswdb.com/xml.php"
#define NSWDB_XML_PATH APP_BASE_PATH "NSWreleases.xml"
#define NSWDB_XML_NAME "NSWreleases.xml"
#define NSWDB_XML_PATH APP_BASE_PATH NSWDB_XML_NAME
#define BOREALIS_URL "https://github.com/natinusala/borealis"
#define LIBUSBHSFS_URL "https://github.com/DarkMatterCore/libusbhsfs"

View File

@ -0,0 +1,340 @@
/*
* dump_options_frame.hpp
*
* Copyright (c) 2020-2022, 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 __DUMP_OPTIONS_FRAME_HPP__
#define __DUMP_OPTIONS_FRAME_HPP__
#include <borealis.hpp>
using namespace brls::i18n::literals;
namespace nxdt::views
{
class DumpOptionsFrame: public brls::ThumbnailFrame
{
private:
RootView *root_view = nullptr;
nxdt::tasks::UmsEvent::Subscription ums_task_sub;
char *raw_filename = NULL;
const char *extension = NULL;
brls::List *list = nullptr;
brls::InputListItem *filename_input = nullptr;
brls::SelectListItem *output_storage = nullptr;
brls::ToggleListItem *prepend_key_area = nullptr;
brls::ToggleListItem *keep_certificate = nullptr;
brls::ToggleListItem *trim_dump = nullptr;
brls::ToggleListItem *calculate_checksum = nullptr;
brls::SelectListItem *checksum_lookup_method = nullptr;
std::string RegenerateFileName(void)
{
if (!this->raw_filename) return "dummy";
char *raw_filename_dup = strdup(this->raw_filename);
if (!raw_filename_dup) return "dummy";
u8 selected = static_cast<u8>(this->output_storage ? this->output_storage->getSelectedValue() : configGetInteger("output_storage"));
utilsReplaceIllegalCharacters(raw_filename_dup, selected == ConfigOutputStorage_SdCard);
std::string output = std::string(raw_filename_dup);
free(raw_filename_dup);
return output;
}
void UpdateRawFileName(void)
{
if (raw_filename) free(this->raw_filename);
this->raw_filename = strdup(this->filename_input->getValue().c_str());
}
void UpdateStorages(const nxdt::tasks::UmsDeviceVector* ums_devices)
{
if (!this->output_storage) return;
std::vector<std::string> *storages = this->output_storage->getValues();
storages->clear();
storages->push_back("dump_options/output_storage/value_00"_i18n);
storages->push_back("dump_options/output_storage/value_01"_i18n);
for(const UsbHsFsDevice& cur_ums_device : *ums_devices)
{
std::string device_str = (std::string(cur_ums_device.name) + ", ");
if (cur_ums_device.product_name[0]) device_str += (std::string(cur_ums_device.product_name) + ", ");
device_str += fmt::format("LUN {}, FS #{}, {}", cur_ums_device.lun, cur_ums_device.fs_idx, LIBUSBHSFS_FS_TYPE_STR(cur_ums_device.fs_type));
storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_02"_i18n, device_str));
}
if (this->output_storage->getSelectedValue() > ConfigOutputStorage_UsbHost)
{
/* Set the SD card as the current output storage. */
this->output_storage->setSelectedValue(ConfigOutputStorage_SdCard);
/* Regenerate filename. */
this->output_storage->getValueSelectedEvent()->fire(ConfigOutputStorage_SdCard);
}
}
protected:
bool onCancel(void) override
{
/* Pop view. */
brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT);
return true;
}
public:
DumpOptionsFrame(RootView *root_view, std::string title, brls::Image *icon, char *raw_filename, const char *extension) : brls::ThumbnailFrame(), root_view(root_view), raw_filename(raw_filename), extension(extension)
{
/* Set UI properties. */
this->setTitle(title);
this->setIcon(icon);
this->list = new brls::List();
this->list->setSpacing(this->list->getSpacing() / 2);
this->list->setMarginBottom(20);
this->filename_input = new brls::InputListItem("dump_options/filename/label"_i18n, this->RegenerateFileName(), "", "dump_options/filename/description"_i18n, 255);
this->filename_input->getClickEvent()->subscribe([this](brls::View *view) {
this->UpdateRawFileName();
this->filename_input->setValue(this->RegenerateFileName());
});
this->list->addView(this->filename_input);
this->output_storage = new brls::SelectListItem("dump_options/output_storage/label"_i18n, {
"dump_options/output_storage/value_00"_i18n,
"dump_options/output_storage/value_01"_i18n
}, configGetInteger("output_storage"),
brls::i18n::getStr("dump_options/output_storage/description"_i18n, GITHUB_REPOSITORY_URL));
/* Subscribe to SelectListItem's value selected event. */
this->output_storage->getValueSelectedEvent()->subscribe([this](int selected) {
/* Make sure the current value isn't out of bounds. */
if (selected < ConfigOutputStorage_SdCard || selected >= static_cast<int>(this->root_view->GetUmsDevices()->size() + ConfigOutputStorage_Count)) return;
/* Update configuration. */
if (selected == ConfigOutputStorage_SdCard || selected == ConfigOutputStorage_UsbHost) configSetInteger("output_storage", selected);
/* Update output filename. */
this->filename_input->setValue(this->RegenerateFileName());
});
/* Update output storages vector. */
this->UpdateStorages(this->root_view->GetUmsDevices());
this->list->addView(this->output_storage);
/* Subscribe to the UMS device event. */
this->ums_task_sub = this->root_view->RegisterUmsTaskListener([this](const nxdt::tasks::UmsDeviceVector* ums_devices) {
/* Update output storages vector. */
this->UpdateStorages(ums_devices);
});
this->prepend_key_area = new brls::ToggleListItem("dump_options/prepend_key_area/label"_i18n, configGetBoolean("gamecard/prepend_key_area"), \
"dump_options/prepend_key_area/description"_i18n, "generic/value_enabled"_i18n, \
"generic/value_disabled"_i18n);
this->prepend_key_area->getClickEvent()->subscribe([](brls::View* view) {
/* Get current value. */
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
bool value = item->getToggleState();
/* Update configuration. */
configSetBoolean("gamecard/prepend_key_area", value);
LOG_MSG_DEBUG("Prepend Key Area setting changed by user.");
});
this->list->addView(this->prepend_key_area);
this->keep_certificate = new brls::ToggleListItem("dump_options/keep_certificate/label"_i18n, configGetBoolean("gamecard/keep_certificate"), \
"dump_options/keep_certificate/description"_i18n, "generic/value_enabled"_i18n, \
"generic/value_disabled"_i18n);
this->keep_certificate->getClickEvent()->subscribe([](brls::View* view) {
/* Get current value. */
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
bool value = item->getToggleState();
/* Update configuration. */
configSetBoolean("gamecard/keep_certificate", value);
LOG_MSG_DEBUG("Keep certificate setting changed by user.");
});
this->list->addView(this->keep_certificate);
this->trim_dump = new brls::ToggleListItem("dump_options/trim_dump/label"_i18n, configGetBoolean("gamecard/trim_dump"), \
"dump_options/trim_dump/description"_i18n, "generic/value_enabled"_i18n, \
"generic/value_disabled"_i18n);
this->trim_dump->getClickEvent()->subscribe([](brls::View* view) {
/* Get current value. */
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
bool value = item->getToggleState();
/* Update configuration. */
configSetBoolean("gamecard/trim_dump", value);
LOG_MSG_DEBUG("Trim dump setting changed by user.");
});
this->list->addView(this->trim_dump);
this->calculate_checksum = new brls::ToggleListItem("dump_options/calculate_checksum/label"_i18n, configGetBoolean("gamecard/calculate_checksum"), \
"dump_options/calculate_checksum/description"_i18n, "generic/value_enabled"_i18n, \
"generic/value_disabled"_i18n);
this->calculate_checksum->getClickEvent()->subscribe([](brls::View* view) {
/* Get current value. */
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
bool value = item->getToggleState();
/* Update configuration. */
configSetBoolean("gamecard/calculate_checksum", value);
LOG_MSG_DEBUG("Calculate checksum setting changed by user.");
});
this->list->addView(this->calculate_checksum);
this->checksum_lookup_method = new brls::SelectListItem("dump_options/checksum_lookup_method/label"_i18n, {
"dump_options/checksum_lookup_method/value_00"_i18n,
"NSWDB",
"No-Intro"
}, configGetInteger("gamecard/checksum_lookup_method"),
brls::i18n::getStr("dump_options/checksum_lookup_method/description"_i18n,
"dump_options/calculate_checksum/label"_i18n, "NSWDB", NSWDB_XML_NAME, "No-Intro"));
/* Subscribe to SelectListItem's value selected event. */
this->checksum_lookup_method->getValueSelectedEvent()->subscribe([this](int selected) {
/* Make sure the current value isn't out of bounds. */
if (selected < ConfigChecksumLookupMethod_None || selected >= ConfigChecksumLookupMethod_Count) return;
/* Update configuration. */
configSetInteger("gamecard/checksum_lookup_method", selected);
});
this->list->addView(this->checksum_lookup_method);
brls::Button *button = this->getSidebar()->getButton();
button->setLabel("dump_options/start_dump"_i18n);
button->getClickEvent()->subscribe([](brls::View *view) {
brls::Application::notify("test");
});
this->setContentView(this->list);
}
~DumpOptionsFrame(void)
{
/* Unregister task listener. */
this->root_view->UnregisterUmsTaskListener(this->ums_task_sub);
if (this->raw_filename) free(this->raw_filename);
}
};
}
#endif /* __DUMP_OPTIONS_FRAME_HPP__ */

View File

@ -41,7 +41,8 @@ namespace nxdt::views
brls::Label *time_lbl = nullptr;
brls::Label *battery_icon = nullptr, *battery_percentage = nullptr;
brls::Label *connection_icon = nullptr, *connection_status_lbl = nullptr;
brls::Label *usb_icon = nullptr, *usb_host_speed_lbl = nullptr;
brls::Label *usb_icon = nullptr, *ums_counter_lbl = nullptr;
brls::Label *cable_icon = nullptr, *usb_host_speed_lbl = nullptr;
nxdt::tasks::StatusInfoTask *status_info_task = nullptr;
nxdt::tasks::GameCardTask *gc_status_task = nullptr;
@ -50,6 +51,7 @@ namespace nxdt::views
nxdt::tasks::UsbHostTask *usb_host_task = nullptr;
nxdt::tasks::StatusInfoEvent::Subscription status_info_task_sub;
nxdt::tasks::UmsEvent::Subscription ums_task_sub;
nxdt::tasks::UsbHostEvent::Subscription usb_host_task_sub;
protected:
@ -75,6 +77,11 @@ namespace nxdt::views
return this->title_task->GetApplicationMetadata(is_system);
}
ALWAYS_INLINE const nxdt::tasks::UmsDeviceVector* GetUmsDevices(void)
{
return this->ums_task->GetUmsDevices();
}
EVENT_SUBSCRIPTION(StatusInfoTask, StatusInfoEvent, status_info_task);
EVENT_SUBSCRIPTION(GameCardTask, GameCardStatusEvent, gc_status_task);
EVENT_SUBSCRIPTION(TitleTask, TitleEvent, title_task);

View File

@ -120,7 +120,7 @@ namespace nxdt::tasks
TitleTask(void);
~TitleTask(void);
/* Intentionally left here to let system titles views retrieve metadata. */
/* Intentionally left here to let views retrieve title metadata. */
const TitleApplicationMetadataVector* GetApplicationMetadata(bool is_system);
EVENT_SUBSCRIPTION(TitleEvent, title_event);
@ -143,6 +143,9 @@ namespace nxdt::tasks
UmsTask(void);
~UmsTask(void);
/* Intentionally left here to let views retrieve UMS device info. */
const UmsDeviceVector* GetUmsDevices(void);
EVENT_SUBSCRIPTION(UmsEvent, ums_event);
};

@ -1 +1 @@
Subproject commit 642c599c33f8632c42b0931aaa7676293576a191
Subproject commit f9f4aa9637f84aa89025d68a8ec15e5ff34d1537

@ -1 +1 @@
Subproject commit 315c98ecd631656b0e5839ca5e6fc293908d06f8
Subproject commit c1f63a931df0c6d07f8f2c91ef6352657acbdb2d

View File

@ -1,9 +1,9 @@
{
"overclock": true,
"naming_convention": 0,
"dump_destination": 0,
"output_storage": 0,
"gamecard": {
"append_key_area": false,
"prepend_key_area": false,
"keep_certificate": false,
"trim_dump": false,
"calculate_checksum": true,

View File

@ -0,0 +1,42 @@
{
"filename": {
"label": "Filename",
"description": "Filename used for the output dump.\nIllegal filesystem characters will be automatically replaced with underscores (\"_\"). If the inserted SD card is used as the output storage, only ASCII characters will be kept — this is a limitation on Nintendo's FS driver.\nThe file extension cannot be modified, and it is excluded on purpose."
},
"output_storage": {
"label": "Output storage",
"description": "Storage where the dumped data will be written to. Changing it will automatically update the output filename to better suit the output filesystem limitations.\nUsing a connected USB host requires a libusb-based driver, as well as the Python host script. For more information, please visit \"{}\".",
"value_00": "SD card",
"value_01": "USB host",
"value_02": "USB Mass Storage ({})"
},
"prepend_key_area": {
"label": "Prepend KeyArea data",
"description": "Prepends the full, 4 KiB long KeyArea block to the output XCI dump, which includes the InitialData area. XCI dumps with KeyArea data are also known as \"Full XCIs\". Disabled by default."
},
"keep_certificate": {
"label": "Keep certificate",
"description": "Preserves the gamecard certificate in the output XCI dump, which is used to unequivocally identify each individual gamecard. Disabled by default."
},
"trim_dump": {
"label": "Trim dump",
"description": "Trims the output XCI dump by removing padding data beyond the end of the last HFS partition. Disabled by default."
},
"calculate_checksum": {
"label": "Calculate checksum",
"description": "Calculates one or more CRC32 checksums over the dumped data, depending on the selected configuration. Checksums are useful to verify data integrity. Enabled by default."
},
"checksum_lookup_method": {
"label": "Checksum lookup method",
"description": "If \"{0}\" is enabled, this option determines which lookup method is used to validate the calculated CRC32 checksum at the end of the dump process.\nIf \"{1}\" is selected, the calculated checksum will be looked up in \"{2}\", which must have been previously downloaded.\nIf \"{3}\" is selected, the calculated checksum will be looked up using an Internet connection and a public HTTP endpoint.",
"value_00": "None"
},
"start_dump": "Start dump"
}

View File

@ -15,5 +15,8 @@
"unknown_exception": "unknown",
"libnx_abort": "Fatal error triggered in libnx!\nError code: 0x{:08X}.",
"exception_triggered": "Fatal exception triggered!\nReason: {} (0x{:X})."
"exception_triggered": "Fatal exception triggered!\nReason: {} (0x{:X}).",
"value_enabled": "Yes",
"value_disabled": "No"
}

View File

@ -3,9 +3,7 @@
"overclock": {
"label": "Overclock",
"description": "Overclocks both CPU and MEM to 1785 MHz and 1600 MHz, respectively, in order to speed up dump operations. This is considered a relatively safe action.\n\nIf the application is running under title override mode, and sys-clk is active, and a clock profile has been created for the overridden title, this setting has no effect at all.",
"value_enabled": "Yes",
"value_disabled": "No"
"description": "Overclocks both CPU and MEM to 1785 MHz and 1600 MHz, respectively, in order to speed up dump operations. This is considered a relatively safe action.\n\nIf the application is running under title override mode, and sys-clk is active, and a clock profile has been created for the overridden title, this setting has no effect at all."
},
"naming_convention": {

View File

@ -1,8 +1,13 @@
{
"applet_mode": "\uE8B2 Applet Mode \uE8B2",
"not_connected": "Not connected",
"ums_counter": "{} UMS device(s)",
"usb_host_speed": "USB {}.0 host",
"usb_host_not_connected": "No USB host connected",
"tabs": {
"gamecard": "Gamecard",
"user_titles": "User titles",

View File

@ -137,10 +137,9 @@ static bool bktrVisitorMovePrevious(BucketTreeVisitor *visitor);
bool bktrInitializeContext(BucketTreeContext *out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type)
{
NcaContext *nca_ctx = NULL;
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type >= NcaFsSectionType_Invalid || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || \
(nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved) || storage_type == BucketTreeStorageType_Compressed || storage_type >= BucketTreeStorageType_Count)
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type >= NcaFsSectionType_Invalid || !nca_fs_ctx->nca_ctx || \
(nca_fs_ctx->nca_ctx->rights_id_available && !nca_fs_ctx->nca_ctx->titlekey_retrieved) || storage_type == BucketTreeStorageType_Compressed || \
storage_type >= BucketTreeStorageType_Count)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -166,20 +165,17 @@ bool bktrInitializeContext(BucketTreeContext *out, NcaFsSectionContext *nca_fs_c
}
if (!success) LOG_MSG_ERROR("Failed to initialize Bucket Tree %s storage for FS section #%u in \"%s\".", bktrGetStorageTypeName(storage_type), nca_fs_ctx->section_idx, \
nca_ctx->content_id_str);
nca_fs_ctx->nca_ctx->content_id_str);
return success;
}
bool bktrInitializeCompressedStorageContext(BucketTreeContext *out, BucketTreeSubStorage *substorage)
{
NcaFsSectionContext *nca_fs_ctx = NULL;
NcaContext *nca_ctx = NULL;
if (!out || !substorage || substorage->index != 0 || !(nca_fs_ctx = substorage->nca_fs_ctx) || !nca_fs_ctx->enabled || !nca_fs_ctx->has_compression_layer || \
nca_fs_ctx->section_type >= NcaFsSectionType_Invalid || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved) || \
substorage->type == BucketTreeSubStorageType_AesCtrEx || substorage->type == BucketTreeSubStorageType_Compressed || substorage->type >= BucketTreeSubStorageType_Count || \
(substorage->type == BucketTreeSubStorageType_Regular && substorage->bktr_ctx) || (substorage->type != BucketTreeSubStorageType_Regular && !substorage->bktr_ctx))
if (!out || !bktrIsValidSubStorage(substorage) || substorage->index != 0 || !substorage->nca_fs_ctx->enabled || !substorage->nca_fs_ctx->has_compression_layer || \
substorage->nca_fs_ctx->section_type >= NcaFsSectionType_Invalid || !substorage->nca_fs_ctx->nca_ctx || \
(substorage->nca_fs_ctx->nca_ctx->rights_id_available && !substorage->nca_fs_ctx->nca_ctx->titlekey_retrieved) || \
substorage->type == BucketTreeSubStorageType_AesCtrEx || substorage->type == BucketTreeSubStorageType_Compressed || substorage->type >= BucketTreeSubStorageType_Count)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -188,6 +184,7 @@ bool bktrInitializeCompressedStorageContext(BucketTreeContext *out, BucketTreeSu
/* Free output context beforehand. */
bktrFreeContext(out);
NcaFsSectionContext *nca_fs_ctx = substorage->nca_fs_ctx;
NcaBucketInfo *compressed_bucket = &(nca_fs_ctx->header.compression_info.bucket);
BucketTreeTable *compressed_table = NULL;
u64 node_storage_size = 0, entry_storage_size = 0;
@ -253,10 +250,8 @@ end:
bool bktrSetRegularSubStorage(BucketTreeContext *ctx, NcaFsSectionContext *nca_fs_ctx)
{
NcaContext *nca_ctx = NULL;
if (!bktrIsValidContext(ctx) || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type >= NcaFsSectionType_Invalid || \
!(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved) || \
!nca_fs_ctx->nca_ctx || (nca_fs_ctx->nca_ctx->rights_id_available && !nca_fs_ctx->nca_ctx->titlekey_retrieved) || \
ctx->storage_type == BucketTreeStorageType_Compressed || ctx->storage_type >= BucketTreeStorageType_Count || \
(ctx->storage_type == BucketTreeStorageType_Indirect && ctx->nca_fs_ctx == nca_fs_ctx) || \
((ctx->storage_type == BucketTreeStorageType_AesCtrEx || ctx->storage_type == BucketTreeStorageType_Sparse) && ctx->nca_fs_ctx != nca_fs_ctx))
@ -364,7 +359,7 @@ bool bktrIsBlockWithinIndirectStorageRange(BucketTreeContext *ctx, u64 offset, u
/* Check if we're dealing with a Compressed storage. */
if (ctx->storage_type == BucketTreeStorageType_Compressed)
{
BucketTreeContext *indirect_storage = (BucketTreeContext*)ctx->substorages[0].bktr_ctx;
BucketTreeContext *indirect_storage = ctx->substorages[0].bktr_ctx;
const u64 compressed_storage_base_offset = ctx->nca_fs_ctx->hash_region.size;
BucketTreeCompressedStorageEntry *start_entry = NULL, *end_entry = NULL;
@ -509,7 +504,7 @@ static bool bktrInitializeIndirectStorageContext(BucketTreeContext *out, NcaFsSe
return false;
}
NcaContext *nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx;
NcaContext *nca_ctx = nca_fs_ctx->nca_ctx;
NcaBucketInfo *indirect_bucket = (is_sparse ? &(nca_fs_ctx->header.sparse_info.bucket) : &(nca_fs_ctx->header.patch_info.indirect_bucket));
BucketTreeTable *indirect_table = NULL;
u64 node_storage_size = 0, entry_storage_size = 0;
@ -532,7 +527,7 @@ static bool bktrInitializeIndirectStorageContext(BucketTreeContext *out, NcaFsSe
/* Read indirect storage table data. */
if ((!is_sparse && !ncaReadFsSection(nca_fs_ctx, indirect_table, indirect_bucket->size, indirect_bucket->offset)) || \
(is_sparse && !ncaReadContentFile((NcaContext*)nca_fs_ctx->nca_ctx, indirect_table, indirect_bucket->size, nca_fs_ctx->sparse_table_offset)))
(is_sparse && !ncaReadContentFile(nca_ctx, indirect_table, indirect_bucket->size, nca_fs_ctx->sparse_table_offset)))
{
LOG_MSG_ERROR("Failed to read Indirect Storage Table data! (%s).", is_sparse ? "sparse" : "patch");
goto end;
@ -595,10 +590,10 @@ static bool bktrReadIndirectStorage(BucketTreeVisitor *visitor, void *out, u64 r
{
BucketTreeContext *ctx = visitor->bktr_ctx;
bool is_sparse = (ctx->storage_type == BucketTreeStorageType_Sparse);
bool missing_original_storage = !bktrIsValidSubstorage(&(ctx->substorages[0]));
bool missing_original_storage = !bktrIsValidSubStorage(&(ctx->substorages[0]));
if (!out || (is_sparse && (missing_original_storage || ctx->substorages[0].type != BucketTreeSubStorageType_Regular)) || \
(!is_sparse && (!bktrIsValidSubstorage(&(ctx->substorages[1])) || ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx || \
(!is_sparse && (!bktrIsValidSubStorage(&(ctx->substorages[1])) || ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx || \
(!missing_original_storage && (ctx->substorages[0].type == BucketTreeSubStorageType_Indirect || ctx->substorages[0].type == BucketTreeSubStorageType_AesCtrEx || \
ctx->substorages[0].type >= BucketTreeSubStorageType_Count)))) || (offset + read_size) > ctx->end_offset)
{
@ -771,7 +766,7 @@ static bool bktrReadAesCtrExStorage(BucketTreeVisitor *visitor, void *out, u64 r
{
BucketTreeContext *ctx = visitor->bktr_ctx;
if (!out || !bktrIsValidSubstorage(&(ctx->substorages[0])) || ctx->substorages[0].type != BucketTreeSubStorageType_Regular || (offset + read_size) > ctx->end_offset)
if (!out || !bktrIsValidSubStorage(&(ctx->substorages[0])) || ctx->substorages[0].type != BucketTreeSubStorageType_Regular || (offset + read_size) > ctx->end_offset)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -856,7 +851,8 @@ static bool bktrReadCompressedStorage(BucketTreeVisitor *visitor, void *out, u64
NcaFsSectionContext *nca_fs_ctx = ctx->nca_fs_ctx;
u64 compressed_storage_base_offset = nca_fs_ctx->hash_region.size;
if (!out || !bktrIsValidSubstorage(&(ctx->substorages[0])) || ctx->substorages[0].type >= BucketTreeSubStorageType_AesCtrEx || (offset + read_size) > ctx->end_offset)
if (!out || !bktrIsValidSubStorage(&(ctx->substorages[0])) || ctx->substorages[0].type == BucketTreeSubStorageType_AesCtrEx || \
ctx->substorages[0].type == BucketTreeSubStorageType_Compressed || ctx->substorages[0].type >= BucketTreeSubStorageType_Count || (offset + read_size) > ctx->end_offset)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -1016,7 +1012,7 @@ end:
static bool bktrReadSubStorage(BucketTreeSubStorage *substorage, BucketTreeSubStorageReadParams *params)
{
if (!bktrIsValidSubstorage(substorage) || !params || !params->buffer || !params->size)
if (!bktrIsValidSubStorage(substorage) || !params || !params->buffer || !params->size)
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
@ -1041,8 +1037,7 @@ static bool bktrReadSubStorage(BucketTreeSubStorage *substorage, BucketTreeSubSt
}
} else {
/* Perform a read on the target BucketTree storage. */
BucketTreeContext *ctx = (BucketTreeContext*)substorage->bktr_ctx;
success = bktrReadStorage(ctx, params->buffer, params->size, params->offset);
success = bktrReadStorage(substorage->bktr_ctx, params->buffer, params->size, params->offset);
}
if (!success) LOG_MSG_ERROR("Failed to read 0x%lX-byte long chunk from offset 0x%lX!", params->size, params->offset);

View File

@ -169,7 +169,7 @@ static void configFreeConfigJson(void)
static bool configValidateJsonRootObject(const struct json_object *obj)
{
bool ret = false, overclock_found = false, naming_convention_found = false, dump_destination_found = false, gamecard_found = false;
bool ret = false, overclock_found = false, naming_convention_found = false, output_storage_found = false, gamecard_found = false;
bool nsp_found = false, ticket_found = false, nca_fs_found = false;
if (!jsonValidateObject(obj)) goto end;
@ -178,7 +178,7 @@ static bool configValidateJsonRootObject(const struct json_object *obj)
{
CONFIG_VALIDATE_FIELD(Boolean, overclock);
CONFIG_VALIDATE_FIELD(Integer, naming_convention, TitleNamingConvention_Full, TitleNamingConvention_Count - 1);
CONFIG_VALIDATE_FIELD(Integer, dump_destination, ConfigDumpDestination_SdCard, ConfigDumpDestination_Count - 1);
CONFIG_VALIDATE_FIELD(Integer, output_storage, ConfigOutputStorage_SdCard, ConfigOutputStorage_Count - 1);
CONFIG_VALIDATE_OBJECT(GameCard, gamecard);
CONFIG_VALIDATE_OBJECT(Nsp, nsp);
CONFIG_VALIDATE_OBJECT(Ticket, ticket);
@ -186,7 +186,7 @@ static bool configValidateJsonRootObject(const struct json_object *obj)
goto end;
}
ret = (overclock_found && naming_convention_found && dump_destination_found && gamecard_found && nsp_found && ticket_found && nca_fs_found);
ret = (overclock_found && naming_convention_found && output_storage_found && gamecard_found && nsp_found && ticket_found && nca_fs_found);
end:
return ret;
@ -194,13 +194,13 @@ end:
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;
bool ret = false, prepend_key_area_found = false, keep_certificate_found = false, trim_dump_found = false, calculate_checksum_found = false, checksum_lookup_method_found = false;
if (!jsonValidateObject(obj)) goto end;
json_object_object_foreach(obj, key, val)
{
CONFIG_VALIDATE_FIELD(Boolean, append_key_area);
CONFIG_VALIDATE_FIELD(Boolean, prepend_key_area);
CONFIG_VALIDATE_FIELD(Boolean, keep_certificate);
CONFIG_VALIDATE_FIELD(Boolean, trim_dump);
CONFIG_VALIDATE_FIELD(Boolean, calculate_checksum);
@ -208,7 +208,7 @@ static bool configValidateJsonGameCardObject(const struct json_object *obj)
goto end;
}
ret = (append_key_area_found && keep_certificate_found && trim_dump_found && calculate_checksum_found && checksum_lookup_method_found);
ret = (prepend_key_area_found && keep_certificate_found && trim_dump_found && calculate_checksum_found && checksum_lookup_method_found);
end:
return ret;

File diff suppressed because it is too large Load Diff

View File

@ -273,8 +273,9 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !*(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || patch->written || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || \
!patch->hash_region_count || patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT || !buf || !buf_size || (buf_offset + buf_size) > ctx->content_size) return;
if (!ctx || !*(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || patch->written || \
memcmp(patch->content_id.c, ctx->content_id.c, sizeof(NcmContentId)) != 0 || !patch->hash_region_count || \
patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT || !buf || !buf_size || (buf_offset + buf_size) > ctx->content_size) return;
patch->written = true;
@ -297,8 +298,8 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !*(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || patch->written || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || \
!buf || !buf_size || (buf_offset + buf_size) > ctx->content_size) return;
if (!ctx || !*(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || patch->written || \
memcmp(patch->content_id.c, ctx->content_id.c, sizeof(NcmContentId)) != 0 || !buf || !buf_size || (buf_offset + buf_size) > ctx->content_size) return;
patch->written = true;
@ -458,13 +459,12 @@ void ncaUpdateContentIdAndHash(NcaContext *ctx, u8 hash[SHA256_HASH_SIZE])
const char *ncaGetFsSectionTypeName(NcaFsSectionContext *ctx)
{
NcaContext *nca_ctx = NULL;
const char *str = "Invalid";
bool is_exefs = false;
if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx)) return str;
if (!ctx || !ctx->nca_ctx) return str;
is_exefs = (nca_ctx->content_type == NcmContentType_Program && ctx->section_idx == 0);
is_exefs = (ctx->nca_ctx->content_type == NcmContentType_Program && ctx->section_idx == 0);
switch(ctx->section_type)
{
@ -1019,7 +1019,7 @@ end:
static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
{
#if LOG_LEVEL <= LOG_LEVEL_WARNING
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
const char *content_id_str = ctx->nca_ctx->content_id_str;
#endif
bool success = false, valid = true;
@ -1038,7 +1038,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
if (!hash_data->hash_block_size || !hash_data->hash_region_count || hash_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT)
{
LOG_DATA_WARNING(hash_data, sizeof(NcaHierarchicalSha256Data), "Invalid HierarchicalSha256 data for FS section #%u in \"%s\". Skipping FS section. Hash data dump:", \
ctx->section_idx, nca_ctx->content_id_str);
ctx->section_idx, content_id_str);
break;
}
@ -1050,7 +1050,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
((i < (hash_data->hash_region_count - 1) || !ctx->has_sparse_layer) && (hash_region->offset + hash_region->size) > ctx->section_size))
{
LOG_MSG_WARNING("HierarchicalSha256 region #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
i, ctx->section_idx, nca_ctx->content_id_str);
i, ctx->section_idx, content_id_str);
valid = false;
break;
}
@ -1070,7 +1070,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
hash_data->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT)
{
LOG_DATA_WARNING(hash_data, sizeof(NcaIntegrityMetaInfo), "Invalid HierarchicalIntegrity data for FS section #%u in \"%s\". Skipping FS section. Hash data dump:", \
ctx->section_idx, nca_ctx->content_id_str);
ctx->section_idx, content_id_str);
break;
}
@ -1082,7 +1082,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
(!ctx->has_sparse_layer && ctx->section_type != NcaFsSectionType_PatchRomFs)) && (lvl_info->offset + lvl_info->size) > ctx->section_size))
{
LOG_MSG_WARNING("HierarchicalIntegrity level #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
i, ctx->section_idx, nca_ctx->content_id_str);
i, ctx->section_idx, content_id_str);
valid = false;
break;
}
@ -1095,7 +1095,7 @@ static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
break;
default:
LOG_MSG_WARNING("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", ctx->section_idx, nca_ctx->content_id_str, ctx->hash_type);
LOG_MSG_WARNING("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", ctx->section_idx, content_id_str, ctx->hash_type);
break;
}
@ -1115,7 +1115,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
size_t crypt_res = 0;
u64 sector_num = 0;
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
NcaContext *nca_ctx = ctx->nca_ctx;
u64 content_offset = (ctx->section_offset + offset);
u64 sparse_virtual_offset = ((ctx->has_sparse_layer && ctx->cur_sparse_virtual_offset) ? (ctx->section_offset + ctx->cur_sparse_virtual_offset) : 0);
@ -1299,7 +1299,7 @@ static bool _ncaReadAesCtrExStorage(NcaFsSectionContext *ctx, void *out, u64 rea
return false;
}
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
NcaContext *nca_ctx = ctx->nca_ctx;
u64 content_offset = (ctx->section_offset + offset);
u64 block_start_offset = 0, block_end_offset = 0, block_size = 0;
@ -1394,7 +1394,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data,
bool use_sha3 = false, success = false;
if (!ctx || !ctx->enabled || ctx->has_sparse_layer || ctx->has_compression_layer || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || \
if (!ctx || !ctx->enabled || ctx->has_sparse_layer || ctx->has_compression_layer || !(nca_ctx = ctx->nca_ctx) || \
(!is_integrity_patch && ((ctx->hash_type != NcaHashType_HierarchicalSha256 && ctx->hash_type != NcaHashType_HierarchicalSha3256) || \
!ctx->header.hash_data.hierarchical_sha256_data.hash_block_size || !(layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count) || \
layer_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT || !(last_layer_size = ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1].size))) || \
@ -1656,7 +1656,7 @@ static void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
size_t crypt_res = 0;
u64 sector_num = 0;
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
NcaContext *nca_ctx = ctx->nca_ctx;
u64 content_offset = (ctx->section_offset + data_offset);
u64 block_start_offset = 0, block_end_offset = 0, block_size = 0;

View File

@ -95,7 +95,7 @@ bool ncaStorageSetPatchOriginalSubStorage(NcaStorageContext *patch_ctx, NcaStora
NcaContext *patch_nca_ctx = NULL, *base_nca_ctx = NULL;
if (!ncaStorageIsValidContext(patch_ctx) || !ncaStorageIsValidContext(base_ctx) || patch_ctx->nca_fs_ctx == base_ctx->nca_fs_ctx || \
!(patch_nca_ctx = (NcaContext*)patch_ctx->nca_fs_ctx->nca_ctx) || !(base_nca_ctx = (NcaContext*)base_ctx->nca_fs_ctx->nca_ctx) || \
!(patch_nca_ctx = patch_ctx->nca_fs_ctx->nca_ctx) || !(base_nca_ctx = base_ctx->nca_fs_ctx->nca_ctx) || \
patch_ctx->nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
patch_nca_ctx->header.program_id != base_nca_ctx->header.program_id || patch_nca_ctx->header.content_type != base_nca_ctx->header.content_type || \
patch_nca_ctx->id_offset != base_nca_ctx->id_offset || patch_nca_ctx->title_version < base_nca_ctx->title_version || \

View File

@ -30,7 +30,7 @@ bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx
bool success = false, dump_meta_header = false, dump_acid_header = false, dump_aci_header = false;
PartitionFileSystemEntry *pfs_entry = NULL;
if (!out || !pfs_ctx || !ncaStorageIsValidContext(&(pfs_ctx->storage_ctx)) || !(nca_ctx = (NcaContext*)pfs_ctx->nca_fs_ctx->nca_ctx) || \
if (!out || !pfs_ctx || !ncaStorageIsValidContext(&(pfs_ctx->storage_ctx)) || !(nca_ctx = pfs_ctx->nca_fs_ctx->nca_ctx) || \
nca_ctx->content_type != NcmContentType_Program || !pfs_ctx->offset || !pfs_ctx->size || !pfs_ctx->is_exefs || \
pfs_ctx->header_size <= sizeof(PartitionFileSystemHeader) || !pfs_ctx->header)
{

View File

@ -31,12 +31,11 @@ static bool nsoGetSectionFromRodataSegment(NsoContext *nso_ctx, u8 *rodata_buf,
bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx, PartitionFileSystemEntry *pfs_entry)
{
NcaContext *nca_ctx = NULL;
u8 *rodata_buf = NULL;
bool success = false, dump_nso_header = false;
if (!out || !pfs_ctx || !ncaStorageIsValidContext(&(pfs_ctx->storage_ctx)) || !(nca_ctx = (NcaContext*)pfs_ctx->nca_fs_ctx->nca_ctx) || \
nca_ctx->content_type != NcmContentType_Program || !pfs_ctx->offset || !pfs_ctx->size || !pfs_ctx->is_exefs || \
if (!out || !pfs_ctx || !ncaStorageIsValidContext(&(pfs_ctx->storage_ctx)) || !pfs_ctx->nca_fs_ctx->nca_ctx || \
pfs_ctx->nca_fs_ctx->nca_ctx->content_type != NcmContentType_Program || !pfs_ctx->offset || !pfs_ctx->size || !pfs_ctx->is_exefs || \
pfs_ctx->header_size <= sizeof(PartitionFileSystemHeader) || !pfs_ctx->header || !pfs_entry)
{
LOG_MSG_ERROR("Invalid parameters!");

View File

@ -27,7 +27,6 @@
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
{
NcaContext *nca_ctx = NULL;
u32 magic = 0;
PartitionFileSystemHeader pfs_header = {0};
@ -36,8 +35,8 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
bool success = false, dump_fs_header = false;
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->has_sparse_layer || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || \
(nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256 && nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha3256) || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || \
(nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
(nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256 && nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha3256) || !nca_fs_ctx->nca_ctx || \
(nca_fs_ctx->nca_ctx->rights_id_available && !nca_fs_ctx->nca_ctx->titlekey_retrieved))
{
LOG_MSG_ERROR("Invalid parameters!");
return false;

View File

@ -38,12 +38,12 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base
base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)));
if (!out || !base_nca_fs_ctx || (!patch_nca_fs_ctx && (missing_base_romfs || base_nca_fs_ctx->has_sparse_layer)) || \
(!missing_base_romfs && (!(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || (base_nca_ctx->format_version == NcaVersion_Nca0 && \
(!missing_base_romfs && (!(base_nca_ctx = base_nca_fs_ctx->nca_ctx) || (base_nca_ctx->format_version == NcaVersion_Nca0 && \
(base_nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256)) || \
(base_nca_ctx->format_version != NcaVersion_Nca0 && (base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
(base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegrity && base_nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegritySha3))) || \
(base_nca_ctx->rights_id_available && !base_nca_ctx->titlekey_retrieved))) || (patch_nca_fs_ctx && (!patch_nca_fs_ctx->enabled || \
!(patch_nca_ctx = (NcaContext*)patch_nca_fs_ctx->nca_ctx) || (!missing_base_romfs && patch_nca_ctx->format_version != base_nca_ctx->format_version) || \
!(patch_nca_ctx = patch_nca_fs_ctx->nca_ctx) || (!missing_base_romfs && patch_nca_ctx->format_version != base_nca_ctx->format_version) || \
patch_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || (patch_nca_ctx->rights_id_available && !patch_nca_ctx->titlekey_retrieved))))
{
LOG_MSG_ERROR("Invalid parameters!");

View File

@ -22,6 +22,7 @@
#include <nxdt_utils.h>
#include <gamecard_tab.hpp>
#include <titles_tab.hpp>
#include <dump_options_frame.hpp>
namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
@ -34,7 +35,7 @@ namespace nxdt::views
this->list->setSpacing(this->list->getSpacing() / 2);
this->list->setMarginBottom(20);
/* Subscribe to gamecard status event. */
/* Subscribe to the gamecard status event. */
this->gc_status_task_sub = this->root_view->RegisterGameCardTaskListener([this](GameCardStatus gc_status) {
/* Process gamecard status. */
this->ProcessGameCardStatus(gc_status);
@ -179,9 +180,16 @@ namespace nxdt::views
brls::ListItem *dump_card_image = new brls::ListItem("gamecard_tab/list/dump_card_image/label"_i18n, "gamecard_tab/list/dump_card_image/description"_i18n);
/*dump_card_image->getClickEvent()->subscribe([](brls::View *view) {
dump_card_image->getClickEvent()->subscribe([this](brls::View *view) {
char *raw_filename = titleGenerateGameCardFileName(configGetInteger("naming_convention"), TitleFileNameIllegalCharReplaceType_None);
if (!raw_filename) return;
});*/
brls::Image *icon = new brls::Image();
icon->setImage(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
icon->setScaleType(brls::ImageScaleType::SCALE);
brls::Application::pushView(new DumpOptionsFrame(this->root_view, "gamecard_tab/list/dump_card_image/label"_i18n, icon, raw_filename, ".xci"), brls::ViewAnimation::SLIDE_LEFT);
});
this->list->addView(dump_card_image);

View File

@ -395,8 +395,8 @@ namespace nxdt::views
/* Overclock. */
brls::ToggleListItem *overclock = new brls::ToggleListItem("options_tab/overclock/label"_i18n, configGetBoolean("overclock"), \
"options_tab/overclock/description"_i18n, "options_tab/overclock/value_enabled"_i18n, \
"options_tab/overclock/value_disabled"_i18n);
"options_tab/overclock/description"_i18n, "generic/value_enabled"_i18n, \
"generic/value_disabled"_i18n);
overclock->getClickEvent()->subscribe([](brls::View* view) {
/* Get current value. */

View File

@ -81,7 +81,18 @@ namespace nxdt::views
this->usb_icon->setVerticalAlign(NVG_ALIGN_TOP);
this->usb_icon->setParent(this);
this->usb_host_speed_lbl = new brls::Label(brls::LabelStyle::SMALL, "root_view/not_connected"_i18n);
this->ums_counter_lbl = new brls::Label(brls::LabelStyle::SMALL, i18n::getStr("root_view/ums_counter"_i18n, usbHsFsGetPhysicalDeviceCount()));
this->ums_counter_lbl->setHorizontalAlign(NVG_ALIGN_RIGHT);
this->ums_counter_lbl->setVerticalAlign(NVG_ALIGN_TOP);
this->ums_counter_lbl->setParent(this);
this->cable_icon = new brls::Label(brls::LabelStyle::SMALL, "\uEFE6");
this->cable_icon->setFont(material);
this->cable_icon->setHorizontalAlign(NVG_ALIGN_RIGHT);
this->cable_icon->setVerticalAlign(NVG_ALIGN_TOP);
this->cable_icon->setParent(this);
this->usb_host_speed_lbl = new brls::Label(brls::LabelStyle::SMALL, "root_view/usb_host_not_connected"_i18n);
this->usb_host_speed_lbl->setHorizontalAlign(NVG_ALIGN_RIGHT);
this->usb_host_speed_lbl->setVerticalAlign(NVG_ALIGN_TOP);
this->usb_host_speed_lbl->setParent(this);
@ -128,8 +139,13 @@ namespace nxdt::views
this->time_lbl->setText(this->GetFormattedDateString(status_info_data->timeinfo));
/* Update battery labels. */
this->battery_icon->setText(charger_type != PsmChargerType_Unconnected ? "\uE1A3" : (charge_percentage <= 15 ? "\uE19C" : "\uE1A4"));
this->battery_icon->setColor(charger_type != PsmChargerType_Unconnected ? nvgRGB(0, 255, 0) : (charge_percentage <= 15 ? nvgRGB(255, 0, 0) : brls::Application::getTheme()->textColor));
this->battery_icon->setText(charger_type != PsmChargerType_Unconnected ? "\uE1A3" : (charge_percentage >= 100 ? "\uE1A4" : (charge_percentage >= 83 ? "\uEBD2" : \
(charge_percentage >= 67 ? "\uEBD4" : (charge_percentage >= 50 ? "\uEBE2" : \
(charge_percentage >= 33 ? "\uEBDD" : (charge_percentage >= 17 ? "\uEBE0" : \
(charge_percentage > 0 ? "\uEBD9" : "\uEBDC"))))))));
this->battery_icon->setColor(charger_type != PsmChargerType_Unconnected ? nvgRGB(0, 255, 0) : (charge_percentage <= 15 ? nvgRGB(255, 0, 0) : \
brls::Application::getTheme()->textColor));
this->battery_percentage->setText(fmt::format("{}%", charge_percentage));
@ -138,19 +154,24 @@ namespace nxdt::views
this->connection_status_lbl->setText(ip_addr ? std::string(ip_addr) : "root_view/not_connected"_i18n);
});
/* Subscribe to UMS event. */
this->ums_task_sub = this->ums_task->RegisterListener([this](const nxdt::tasks::UmsDeviceVector* ums_devices) {
/* Update UMS counter label. */
this->ums_counter_lbl->setText(i18n::getStr("root_view/ums_counter"_i18n, usbHsFsGetPhysicalDeviceCount()));
});
/* Subscribe to USB host event. */
this->usb_host_task_sub = this->usb_host_task->RegisterListener([this](UsbHostSpeed usb_host_speed) {
/* Update USB host speed label. */
this->usb_host_speed_lbl->setText(usb_host_speed ? fmt::format("USB {}.0", usb_host_speed) : "root_view/not_connected"_i18n);
this->usb_host_speed_lbl->setText(usb_host_speed ? i18n::getStr("root_view/usb_host_speed"_i18n, usb_host_speed) : "root_view/usb_host_not_connected"_i18n);
});
}
RootView::~RootView(void)
{
/* Unregister USB host task listener. */
/* Unregister task listeners. */
this->usb_host_task->UnregisterListener(this->usb_host_task_sub);
/* Unregister status info task listener. */
this->ums_task->UnregisterListener(this->ums_task_sub);
this->status_info_task->UnregisterListener(this->status_info_task_sub);
/* Stop background tasks. */
@ -168,6 +189,8 @@ namespace nxdt::views
delete this->connection_icon;
delete this->connection_status_lbl;
delete this->usb_icon;
delete this->ums_counter_lbl;
delete this->cable_icon;
delete this->usb_host_speed_lbl;
}
@ -212,12 +235,15 @@ namespace nxdt::views
this->connection_status_lbl->frame(ctx);
this->usb_icon->frame(ctx);
this->ums_counter_lbl->frame(ctx);
this->cable_icon->frame(ctx);
this->usb_host_speed_lbl->frame(ctx);
}
void RootView::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
int x_pos = 0, y_pos = 0;
int x_orig = 0, x_pos = 0, y_pos = 0;
brls::AppletFrame::layout(vg, style, stash);
@ -232,7 +258,7 @@ namespace nxdt::views
}
/* Time label. */
x_pos = (this->x + this->width - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing);
x_orig = x_pos = (this->x + this->width - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing);
y_pos = this->y + style->AppletFrame.imageTopPadding;
this->time_lbl->setBoundaries(x_pos, y_pos, 0, 0);
@ -259,8 +285,8 @@ namespace nxdt::views
this->battery_icon->setBoundaries(x_pos, y_pos, 0, 0);
this->battery_icon->invalidate();
/* USB host speed labels. */
x_pos = (this->x + this->width - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing);
/* UMS device counter and USB host speed labels. */
x_pos = x_orig;
y_pos += (this->connection_status_lbl->getTextHeight() + 5);
this->usb_host_speed_lbl->setBoundaries(x_pos, y_pos, 0, 0);
@ -268,6 +294,16 @@ namespace nxdt::views
x_pos -= (5 + this->usb_host_speed_lbl->getTextWidth());
this->cable_icon->setBoundaries(x_pos, y_pos, 0, 0);
this->cable_icon->invalidate();
x_pos -= (10 + this->cable_icon->getTextWidth());
this->ums_counter_lbl->setBoundaries(x_pos, y_pos, 0, 0);
this->ums_counter_lbl->invalidate();
x_pos -= (5 + this->ums_counter_lbl->getTextWidth());
this->usb_icon->setBoundaries(x_pos, y_pos, 0, 0);
this->usb_icon->invalidate();
}

View File

@ -236,6 +236,11 @@ namespace nxdt::tasks
}
}
const UmsDeviceVector* UmsTask::GetUmsDevices(void)
{
return &(this->ums_devices);
}
void UmsTask::PopulateUmsDeviceVector(void)
{
UsbHsFsDevice *ums_devices = NULL;

View File

@ -120,7 +120,7 @@ namespace nxdt::views
if (!app_metadata_count) return;
/* Populate list. */
for(TitleApplicationMetadata *cur_app_metadata : *app_metadata)
for(const TitleApplicationMetadata *cur_app_metadata : *app_metadata)
{
/* Create list item. */
TitlesTabItem *title = new TitlesTabItem(cur_app_metadata, this->is_system);