1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 12:12:02 +00:00

Cherry-pick improvements from the rewrite-yoga branch.

This commit is contained in:
Pablo Curiel 2021-06-23 14:27:06 -04:00
parent 4453e7950f
commit 4d1b3660c3
14 changed files with 460 additions and 222 deletions

View file

@ -40,6 +40,11 @@ include $(DEVKITPRO)/libnx/switch_rules
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
GIT_COMMIT := $(shell git rev-parse --short HEAD) GIT_COMMIT := $(shell git rev-parse --short HEAD)
GIT_REV := ${GIT_BRANCH}-${GIT_COMMIT}
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
GIT_REV := $(GIT_REV)-dirty
endif
VERSION_MAJOR := 2 VERSION_MAJOR := 2
VERSION_MINOR := 0 VERSION_MINOR := 0
@ -49,6 +54,7 @@ APP_TITLE := nxdumptool
APP_AUTHOR := DarkMatterCore APP_AUTHOR := DarkMatterCore
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
# TODO: remove this after the PoC builds are no longer needed.
ifneq ($(origin BUILD_TYPE),undefined) ifneq ($(origin BUILD_TYPE),undefined)
APP_TITLE := ${BUILD_TYPE} APP_TITLE := ${BUILD_TYPE}
endif endif
@ -61,11 +67,11 @@ ICON := romfs/icon/${APP_TITLE}.jpg
INCLUDES := include include/core include/fatfs INCLUDES := include include/core include/fatfs
ROMFS := romfs ROMFS := romfs
USBHSFS_PATH := $(TOPDIR)/libs/libusbhsfs
BOREALIS_PATH := libs/borealis BOREALIS_PATH := libs/borealis
BOREALIS_RESOURCES := romfs:/ BOREALIS_RESOURCES := romfs:/
USBHSFS_PATH := $(TOPDIR)/libs/libusbhsfs
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -74,12 +80,11 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__ CFLAGS := -g -Wall -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__
CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO} CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO}
CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\" CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\"
CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" -DGIT_REV=\"${GIT_REV}\"
CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\"" CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\""
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags` CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --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`
CFLAGS += `aarch64-none-elf-pkg-config libturbojpeg --cflags`
CXXFLAGS := $(CFLAGS) -std=c++20 -O2 -Wno-volatile -Wno-unused-parameter CXXFLAGS := $(CFLAGS) -std=c++20 -O2 -Wno-volatile -Wno-unused-parameter
@ -87,6 +92,7 @@ ASFLAGS := -g $(ARCH)
LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfs -lntfs-3g -llwext4 -lnx LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lusbhsfs -lntfs-3g -llwext4 -lnx
#LIBS += -ljson-c
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
@ -183,7 +189,7 @@ ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif endif
.PHONY: $(BUILD) clean clean_all all .PHONY: $(BUILD) all clean clean_all
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
all: $(BUILD) all: $(BUILD)

View file

@ -264,7 +264,7 @@ static void nspDump(TitleInfo *title_info, u64 free_space)
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset); consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
// don't go any further with this nca if we can't access its fs data because it's pointless // don't go any further with this nca if we can't access its fs data because it's pointless
// to do: add preload warning // TODO: add preload warning
if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved) if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved)
{ {
j++; j++;

View file

@ -254,7 +254,7 @@ static void dump_thread_func(void *arg)
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset); consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
// don't go any further with this nca if we can't access its fs data because it's pointless // don't go any further with this nca if we can't access its fs data because it's pointless
// to do: add preload warning // TODO: add preload warning
if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved) if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved)
{ {
j++; j++;

44
include/core/nxdt_bfsar.h Normal file
View file

@ -0,0 +1,44 @@
/*
* nxdt_bfsar.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 __NXDT_BFSAR_H__
#define __NXDT_BFSAR_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Initializes the BFSAR interface.
bool bfsarInitialize(void);
/// Closes the BFSAR interface.
void bfsarExit(void);
/// Returns a pointer to the BFSAR file path.
const char *bfsarGetFilePath(void);
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_BFSAR_H__ */

View file

@ -61,6 +61,7 @@
#define SPL_SYSMODULE_TID (u64)0x0100000000000028 #define SPL_SYSMODULE_TID (u64)0x0100000000000028
#define ES_SYSMODULE_TID (u64)0x0100000000000033 #define ES_SYSMODULE_TID (u64)0x0100000000000033
#define SYSTEM_UPDATE_TID (u64)0x0100000000000816 #define SYSTEM_UPDATE_TID (u64)0x0100000000000816
#define QLAUNCH_TID (u64)0x0100000000001000
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */ #define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */

View file

@ -33,18 +33,18 @@ namespace nxdt::views
private: private:
bool applet_mode = false; bool applet_mode = false;
brls::Label *applet_mode_lbl = nullptr;
brls::Label *time_lbl = nullptr;
brls::Label *battery_icon = nullptr, *battery_percentage = nullptr;
brls::Label *connection_icon = nullptr, *connection_status_lbl = nullptr;
nxdt::tasks::StatusInfoTask *status_info_task = nullptr; nxdt::tasks::StatusInfoTask *status_info_task = nullptr;
nxdt::tasks::GameCardTask *gc_status_task = nullptr; nxdt::tasks::GameCardTask *gc_status_task = nullptr;
nxdt::tasks::TitleTask *title_task = nullptr; nxdt::tasks::TitleTask *title_task = nullptr;
nxdt::tasks::UmsTask *ums_task = nullptr; nxdt::tasks::UmsTask *ums_task = nullptr;
nxdt::tasks::UsbHostTask *usb_host_task = nullptr; nxdt::tasks::UsbHostTask *usb_host_task = nullptr;
nxdt::tasks::VoidEvent::Subscription status_info_task_sub; nxdt::tasks::StatusInfoEvent::Subscription status_info_task_sub;
brls::Label *applet_mode_lbl = nullptr;
brls::Label *time_lbl = nullptr;
brls::Label *battery_icon = nullptr, *battery_percentage = nullptr;
brls::Label *connection_icon = nullptr, *connection_status_lbl = nullptr;
protected: protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override; void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;

View file

@ -33,32 +33,35 @@
namespace nxdt::tasks namespace nxdt::tasks
{ {
/* Custom event types used by the tasks defined below. */ /* Used to hold status info data. */
typedef brls::Event<GameCardStatus> GameCardStatusEvent; typedef struct {
typedef brls::VoidEvent VoidEvent; struct tm *timeinfo;
typedef brls::Event<bool> BooleanEvent; u32 charge_percentage;
PsmChargerType charger_type;
NifmInternetConnectionType connection_type;
char *ip_addr;
} StatusInfoData;
/* Custom vector type used to hold pointers to application metadata entries. */ /* Used to hold pointers to application metadata entries. */
typedef std::vector<TitleApplicationMetadata*> TitleApplicationMetadataVector; typedef std::vector<TitleApplicationMetadata*> TitleApplicationMetadataVector;
/* Custom vector type used to hold UMS devices. */ /* Used to hold UMS devices. */
typedef std::vector<UsbHsFsDevice> UmsDeviceVector; typedef std::vector<UsbHsFsDevice> UmsDeviceVector;
/* Custom event types. */
typedef brls::Event<const StatusInfoData*> StatusInfoEvent;
typedef brls::Event<GameCardStatus> GameCardStatusEvent;
typedef brls::Event<const TitleApplicationMetadataVector*> TitleEvent;
typedef brls::Event<const UmsDeviceVector*> UmsEvent;
typedef brls::Event<bool> UsbHostEvent;
/* Status info task. */ /* Status info task. */
/* Its event returns a pointer to a StatusInfoData struct. */
class StatusInfoTask: public brls::RepeatingTask class StatusInfoTask: public brls::RepeatingTask
{ {
private: private:
VoidEvent status_info_event; StatusInfoEvent status_info_event;
StatusInfoData status_info_data = {0};
std::string cur_time = "";
u32 charge_percentage = 0;
PsmChargerType charger_type = PsmChargerType_Unconnected;
NifmInternetConnectionType connection_type = (NifmInternetConnectionType)0;
u32 signal_strength = 0;
NifmInternetConnectionStatus connection_status = (NifmInternetConnectionStatus)0;
char *ip_addr = NULL;
protected: protected:
void run(retro_time_t current_time) override; void run(retro_time_t current_time) override;
@ -67,27 +70,23 @@ namespace nxdt::tasks
StatusInfoTask(void); StatusInfoTask(void);
~StatusInfoTask(void); ~StatusInfoTask(void);
std::string GetCurrentTimeString(void); ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback cb)
void GetBatteryStats(u32 *out_charge_percentage, PsmChargerType *out_charger_type);
void GetNetworkStats(NifmInternetConnectionType *out_connection_type, u32 *out_signal_strength, NifmInternetConnectionStatus *out_connection_status, char **out_ip_addr);
ALWAYS_INLINE VoidEvent::Subscription RegisterListener(VoidEvent::Callback cb)
{ {
return this->status_info_event.subscribe(cb); return this->status_info_event.subscribe(cb);
} }
ALWAYS_INLINE void UnregisterListener(VoidEvent::Subscription subscription) ALWAYS_INLINE void UnregisterListener(StatusInfoEvent::Subscription subscription)
{ {
this->status_info_event.unsubscribe(subscription); this->status_info_event.unsubscribe(subscription);
} }
}; };
/* Gamecard task. */ /* Gamecard task. */
/* Its event returns a GameCardStatus value. */
class GameCardTask: public brls::RepeatingTask class GameCardTask: public brls::RepeatingTask
{ {
private: private:
GameCardStatusEvent gc_status_event; GameCardStatusEvent gc_status_event;
GameCardStatus cur_gc_status = GameCardStatus_NotInserted; GameCardStatus cur_gc_status = GameCardStatus_NotInserted;
GameCardStatus prev_gc_status = GameCardStatus_NotInserted; GameCardStatus prev_gc_status = GameCardStatus_NotInserted;
@ -110,10 +109,11 @@ namespace nxdt::tasks
}; };
/* Title task. */ /* Title task. */
/* Its event returns a pointer to a TitleApplicationMetadataVector with metadata for user titles (system titles don't change at runtime). */
class TitleTask: public brls::RepeatingTask class TitleTask: public brls::RepeatingTask
{ {
private: private:
VoidEvent title_event; TitleEvent title_event;
TitleApplicationMetadataVector system_metadata; TitleApplicationMetadataVector system_metadata;
TitleApplicationMetadataVector user_metadata; TitleApplicationMetadataVector user_metadata;
@ -127,24 +127,26 @@ namespace nxdt::tasks
TitleTask(void); TitleTask(void);
~TitleTask(void); ~TitleTask(void);
/* Intentionally left here to let system titles views retrieve metadata. */
TitleApplicationMetadataVector* GetApplicationMetadata(bool is_system); TitleApplicationMetadataVector* GetApplicationMetadata(bool is_system);
ALWAYS_INLINE VoidEvent::Subscription RegisterListener(VoidEvent::Callback cb) ALWAYS_INLINE TitleEvent::Subscription RegisterListener(TitleEvent::Callback cb)
{ {
return this->title_event.subscribe(cb); return this->title_event.subscribe(cb);
} }
ALWAYS_INLINE void UnregisterListener(VoidEvent::Subscription subscription) ALWAYS_INLINE void UnregisterListener(TitleEvent::Subscription subscription)
{ {
this->title_event.unsubscribe(subscription); this->title_event.unsubscribe(subscription);
} }
}; };
/* USB Mass Storage task. */ /* USB Mass Storage task. */
/* Its event returns a pointer to a UmsDeviceVector. */
class UmsTask: public brls::RepeatingTask class UmsTask: public brls::RepeatingTask
{ {
private: private:
VoidEvent ums_event; UmsEvent ums_event;
UmsDeviceVector ums_devices; UmsDeviceVector ums_devices;
@ -157,14 +159,12 @@ namespace nxdt::tasks
UmsTask(void); UmsTask(void);
~UmsTask(void); ~UmsTask(void);
UmsDeviceVector* GetUmsDevices(void); ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback cb)
ALWAYS_INLINE VoidEvent::Subscription RegisterListener(VoidEvent::Callback cb)
{ {
return this->ums_event.subscribe(cb); return this->ums_event.subscribe(cb);
} }
ALWAYS_INLINE void UnregisterListener(VoidEvent::Subscription subscription) ALWAYS_INLINE void UnregisterListener(UmsEvent::Subscription subscription)
{ {
this->ums_event.unsubscribe(subscription); this->ums_event.unsubscribe(subscription);
} }
@ -174,7 +174,7 @@ namespace nxdt::tasks
class UsbHostTask: public brls::RepeatingTask class UsbHostTask: public brls::RepeatingTask
{ {
private: private:
BooleanEvent usb_host_event; UsbHostEvent usb_host_event;
bool cur_usb_host_status = false; bool cur_usb_host_status = false;
bool prev_usb_host_status = false; bool prev_usb_host_status = false;
@ -186,12 +186,12 @@ namespace nxdt::tasks
UsbHostTask(void); UsbHostTask(void);
~UsbHostTask(void); ~UsbHostTask(void);
ALWAYS_INLINE BooleanEvent::Subscription RegisterListener(BooleanEvent::Callback cb) ALWAYS_INLINE UsbHostEvent::Subscription RegisterListener(UsbHostEvent::Callback cb)
{ {
return this->usb_host_event.subscribe(cb); return this->usb_host_event.subscribe(cb);
} }
ALWAYS_INLINE void UnregisterListener(BooleanEvent::Subscription subscription) ALWAYS_INLINE void UnregisterListener(UsbHostEvent::Subscription subscription)
{ {
this->usb_host_event.unsubscribe(subscription); this->usb_host_event.unsubscribe(subscription);
} }

View file

@ -1,7 +1,13 @@
{ {
"__comment__": "Comments about how specific fields work use keys that follow the '__{field}_comment__' format. These don't have to be replicated in your translation files.",
"applet_mode": "\uE8B2 Applet Mode \uE8B2", "applet_mode": "\uE8B2 Applet Mode \uE8B2",
"time_format": "12",
"__time_format_comment__": "Use 12 for a 12-hour clock, or 24 for a 24-hour clock",
"date": "{1:02d}/{2:02d}/{0} {3:02d}:{4:02d}:{5:02d} {6}", "date": "{1:02d}/{2:02d}/{0} {3:02d}:{4:02d}:{5:02d} {6}",
"__date_comment__": "{0} = Year, {1} = Month, {2} = Day, {3} = Hour, {4} = Minute, {5} = Second, {6} = AM/PM (if time_format is set to 12)",
"not_connected": "Not connected", "not_connected": "Not connected",

204
source/core/nxdt_bfsar.c Normal file
View file

@ -0,0 +1,204 @@
/*
* nxdt_bfsar.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 "nxdt_bfsar.h"
#include "romfs.h"
#include "title.h"
#define BFSAR_FILENAME "qlaunch.bfsar"
#define BFSAR_ROMFS_PATH "/sound/" BFSAR_FILENAME
/* Global variables. */
static Mutex g_bfsarMutex = 0;
static bool g_bfsarInterfaceInit = false;
static char g_bfsarPath[FS_MAX_PATH] = {0};
bool bfsarInitialize(void)
{
bool use_root = true;
const char *launch_path = utilsGetLaunchPath();
char *ptr1 = NULL, *ptr2 = NULL;
TitleInfo *title_info = NULL;
NcaContext *nca_ctx = NULL;
RomFileSystemContext romfs_ctx = {0};
RomFileSystemFileEntry *romfs_file_entry = NULL;
FILE *bfsar_file = NULL;
u8 *bfsar_data = NULL;
size_t bfsar_size = 0, wr = 0;
bool ret = false;
SCOPED_LOCK(&g_bfsarMutex)
{
ret = g_bfsarInterfaceInit;
if (ret) break;
/* Generate BFSAR file path. */
if (launch_path)
{
ptr1 = strchr(launch_path, '/');
ptr2 = strrchr(launch_path, '/');
if (ptr1 && ptr2 && ptr1 != ptr2)
{
/* Create BFSAR file in the current working directory. */
snprintf(g_bfsarPath, sizeof(g_bfsarPath), "%.*s" BFSAR_FILENAME, (int)((ptr2 - ptr1) + 1), ptr1);
use_root = false;
}
}
/* Create BFSAR file in the SD card root directory. */
if (use_root) sprintf(g_bfsarPath, "/" BFSAR_FILENAME);
LOG_MSG("BFSAR path: \"%s\".", g_bfsarPath);
/* Check if the BFSAR file is already available and not empty. */
bfsar_file = fopen(g_bfsarPath, "rb");
if (bfsar_file)
{
fseek(bfsar_file, 0, SEEK_END);
bfsar_size = ftell(bfsar_file);
if (bfsar_size)
{
ret = g_bfsarInterfaceInit = true;
break;
}
}
/* Get title info. */
if (!(title_info = titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, QLAUNCH_TID)))
{
LOG_MSG("Failed to get title info for qlaunch!");
break;
}
/* Allocate memory for a temporary NCA context. */
nca_ctx = calloc(1, sizeof(NcaContext));
if (!nca_ctx)
{
LOG_MSG("Failed to allocate memory for temporary NCA context!");
break;
}
/* Initialize NCA context. */
if (!ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Program, 0), NULL))
{
LOG_MSG("Failed to initialize qlaunch Program NCA context!");
break;
}
/* Initialize RomFS context. */
if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[1])))
{
LOG_MSG("Failed to initialize RomFS context for qlaunch Program NCA!");
break;
}
/* Get RomFS file entry. */
if (!(romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, BFSAR_ROMFS_PATH)))
{
LOG_MSG("Failed to retrieve RomFS file entry for \"" BFSAR_ROMFS_PATH "\"!");
break;
}
/* Check file size. */
bfsar_size = romfs_file_entry->size;
if (!bfsar_size)
{
LOG_MSG("File size for qlaunch's \"" BFSAR_ROMFS_PATH "\" is zero!");
break;
}
/* Allocate memory for BFSAR data. */
if (!(bfsar_data = malloc(bfsar_size)))
{
LOG_MSG("Failed to allocate 0x%lX bytes for qlaunch's \"" BFSAR_ROMFS_PATH "\"!", bfsar_size);
break;
}
/* Read BFSAR data. */
if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, bfsar_data, bfsar_size, 0))
{
LOG_MSG("Failed to read 0x%lX bytes long \"" BFSAR_ROMFS_PATH "\" from qlaunch!", bfsar_size);
break;
}
/* Create BFSAR file. */
bfsar_file = fopen(g_bfsarPath, "wb");
if (!bfsar_file)
{
LOG_MSG("Failed to open \"%s\" for writing!", g_bfsarPath);
break;
}
/* Write BFSAR data. */
wr = fwrite(bfsar_data, 1, bfsar_size, bfsar_file);
if (wr != bfsar_size)
{
LOG_MSG("Failed to write 0x%lX bytes block to \"%s\"!", bfsar_size, g_bfsarPath);
break;
}
/* Update flags. */
ret = g_bfsarInterfaceInit = true;
}
if (bfsar_file) fclose(bfsar_file);
if (bfsar_data) free(bfsar_data);
romfsFreeContext(&romfs_ctx);
if (nca_ctx) free(nca_ctx);
titleFreeTitleInfo(&title_info);
return ret;
}
void bfsarExit(void)
{
SCOPED_LOCK(&g_bfsarMutex)
{
/* Clear BFSAR file path. */
*g_bfsarPath = '\0';
g_bfsarInterfaceInit = false;
}
}
const char *bfsarGetFilePath(void)
{
const char *ret = NULL;
SCOPED_TRY_LOCK(&g_bfsarMutex)
{
if (g_bfsarInterfaceInit) ret = (const char*)g_bfsarPath;
}
return ret;
}

View file

@ -29,6 +29,7 @@
#include "usb.h" #include "usb.h"
#include "title.h" #include "title.h"
#include "bfttf.h" #include "bfttf.h"
#include "nxdt_bfsar.h"
#include "fatfs/ff.h" #include "fatfs/ff.h"
/* Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. */ /* Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. */
@ -86,7 +87,7 @@ static size_t utilsGetUtf8CodepointCount(const char *str, size_t str_size, size_
bool utilsInitializeResources(const int program_argc, const char **program_argv) bool utilsInitializeResources(const int program_argc, const char **program_argv)
{ {
Result rc = 0; Result rc = 0;
bool ret = false; bool ret = false, flag = false;
SCOPED_LOCK(&g_resourcesMutex) SCOPED_LOCK(&g_resourcesMutex)
{ {
@ -105,7 +106,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
/* Create logfile. */ /* Create logfile. */
logWriteStringToLogFile("________________________________________________________________\r\n"); logWriteStringToLogFile("________________________________________________________________\r\n");
LOG_MSG(APP_TITLE " v%u.%u.%u starting (" GIT_COMMIT "). Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); LOG_MSG(APP_TITLE " v%u.%u.%u starting (" GIT_REV "). Built on " __DATE__ " - " __TIME__ ".", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
if (g_appLaunchPath) LOG_MSG("Launch path: \"%s\".", g_appLaunchPath); if (g_appLaunchPath) LOG_MSG("Launch path: \"%s\".", g_appLaunchPath);
/* Log Horizon OS version. */ /* Log Horizon OS version. */
@ -153,10 +154,18 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
/* Initialize BFTTF interface. */ /* Initialize BFTTF interface. */
if (!bfttfInitialize()) break; if (!bfttfInitialize()) break;
/* Initialize BFSAR interface. */
//if (!bfsarInitialize()) break;
/* Mount eMMC BIS System partition. */ /* Mount eMMC BIS System partition. */
if (!utilsMountEmmcBisSystemPartitionStorage()) break; if (!utilsMountEmmcBisSystemPartitionStorage()) break;
/* Enable video recording. */
rc = appletIsGamePlayRecordingSupported(&flag);
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
/* Disable screen dimming and auto sleep. */ /* 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); appletSetMediaPlaybackState(true);
/* Overclock system. */ /* Overclock system. */
@ -212,6 +221,9 @@ void utilsCloseResources(void)
/* Unmount eMMC BIS System partition. */ /* Unmount eMMC BIS System partition. */
utilsUnmountEmmcBisSystemPartitionStorage(); utilsUnmountEmmcBisSystemPartitionStorage();
/* Deinitialize BFSAR interface. */
bfsarExit();
/* Deinitialize BFTTF interface. */ /* Deinitialize BFTTF interface. */
bfttfExit(); bfttfExit();

View file

@ -972,17 +972,21 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
TitleInfo **titles = title_storage->titles; TitleInfo **titles = title_storage->titles;
u32 title_count = title_storage->title_count; u32 title_count = title_storage->title_count;
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || !titles || !title_count || name_convention > TitleFileNameConvention_IdAndVersionOnly || \ GameCardHeader gc_header = {0};
size_t cur_filename_len = 0;
char app_name[0x400] = {0};
bool error = false;
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || name_convention > TitleFileNameConvention_IdAndVersionOnly || \
(name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) (name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
break; break;
} }
GameCardHeader gc_header = {0}; /* Check if the gamecard title storage is empty. */
size_t cur_filename_len = 0; /* This is especially true for Kiosk / Quest gamecards. */
char app_name[0x400] = {0}; if (!titles || !title_count) goto fallback;
bool error = false;
for(u32 i = 0; i < title_count; i++) for(u32 i = 0; i < title_count; i++)
{ {
@ -1058,12 +1062,12 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
cur_filename_len += app_name_len; cur_filename_len += app_name_len;
} }
fallback:
if (!filename && !error) if (!filename && !error)
{ {
LOG_MSG("Error: the inserted gamecard doesn't hold any user applications!"); LOG_MSG("Error: the inserted gamecard doesn't hold any user applications!");
/* Fallback string if no applications can be found. */ /* Fallback string if no applications can be found. */
/* This function is guaranteed to fail with Kiosk / Quest gamecards, so that's why this is needed. */
sprintf(app_name, "gamecard"); sprintf(app_name, "gamecard");
if (gamecardGetHeader(&gc_header)) if (gamecardGetHeader(&gc_header))
@ -1074,6 +1078,7 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
} }
filename = strdup(app_name); filename = strdup(app_name);
if (!filename) LOG_MSG("Failed to duplicate fallback filename!");
} }
} }
@ -1093,11 +1098,16 @@ const char *titleGetNcmContentMetaTypeName(u8 content_meta_type)
NX_INLINE void titleFreeApplicationMetadata(void) NX_INLINE void titleFreeApplicationMetadata(void)
{ {
if (g_systemMetadata) for(u8 i = 0; i < 2; i++)
{ {
for(u32 i = 0; i < g_systemMetadataCount; i++) TitleApplicationMetadata **cached_app_metadata = (i == 0 ? g_systemMetadata : g_userMetadata);
u32 cached_app_metadata_count = (i == 0 ? g_systemMetadataCount : g_userMetadataCount);
if (cached_app_metadata)
{ {
TitleApplicationMetadata *cur_app_metadata = g_systemMetadata[i]; for(u32 j = 0; j < cached_app_metadata_count; j++)
{
TitleApplicationMetadata *cur_app_metadata = cached_app_metadata[j];
if (cur_app_metadata) if (cur_app_metadata)
{ {
if (cur_app_metadata->icon) free(cur_app_metadata->icon); if (cur_app_metadata->icon) free(cur_app_metadata->icon);
@ -1105,29 +1115,12 @@ NX_INLINE void titleFreeApplicationMetadata(void)
} }
} }
free(g_systemMetadata); free(cached_app_metadata);
g_systemMetadata = NULL;
}
g_systemMetadataCount = 0;
if (g_userMetadata)
{
for(u32 i = 0; i < g_userMetadataCount; i++)
{
TitleApplicationMetadata *cur_app_metadata = g_userMetadata[i];
if (cur_app_metadata)
{
if (cur_app_metadata->icon) free(cur_app_metadata->icon);
free(cur_app_metadata);
} }
} }
free(g_userMetadata); g_systemMetadata = g_userMetadata = NULL;
g_userMetadata = NULL; g_systemMetadataCount = g_userMetadataCount = 0;
}
g_userMetadataCount = 0;
} }
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool is_system, bool free_entries) static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool is_system, bool free_entries)

View file

@ -27,12 +27,18 @@
//#include <options_tab.hpp> //#include <options_tab.hpp>
#include <about_tab.hpp> #include <about_tab.hpp>
using namespace brls::i18n::literals; /* For _i18n. */ namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
namespace nxdt::views namespace nxdt::views
{ {
RootView::RootView(void) : brls::TabFrame() RootView::RootView(void) : brls::TabFrame()
{ {
/* Set UI properties. */
this->setTitle(APP_TITLE);
this->setIcon(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
this->setFooterText("v" APP_VERSION);
/* Check if we're running under applet mode. */ /* Check if we're running under applet mode. */
this->applet_mode = utilsAppletModeCheck(); this->applet_mode = utilsAppletModeCheck();
@ -65,11 +71,6 @@ namespace nxdt::views
this->ums_task = new nxdt::tasks::UmsTask(); this->ums_task = new nxdt::tasks::UmsTask();
this->usb_host_task = new nxdt::tasks::UsbHostTask(); this->usb_host_task = new nxdt::tasks::UsbHostTask();
/* Set UI properties. */
this->setTitle(APP_TITLE);
this->setIcon(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
this->setFooterText("v" APP_VERSION);
/* Add tabs. */ /* Add tabs. */
this->addTab("root_view/tabs/gamecard"_i18n, new GameCardTab(this->gc_status_task)); this->addTab("root_view/tabs/gamecard"_i18n, new GameCardTab(this->gc_status_task));
this->addSeparator(); this->addSeparator();
@ -81,20 +82,34 @@ namespace nxdt::views
this->addTab("root_view/tabs/about"_i18n, new AboutTab()); this->addTab("root_view/tabs/about"_i18n, new AboutTab());
/* Subscribe to status info event. */ /* Subscribe to status info event. */
this->status_info_task_sub = this->status_info_task->RegisterListener([this](void) { this->status_info_task_sub = this->status_info_task->RegisterListener([this](const nxdt::tasks::StatusInfoData *status_info_data) {
u32 charge_percentage = 0;
PsmChargerType charger_type = PsmChargerType_Unconnected;
NifmInternetConnectionType connection_type = (NifmInternetConnectionType)0;
u32 signal_strength = 0;
NifmInternetConnectionStatus connection_status = (NifmInternetConnectionStatus)0;
char *ip_addr = NULL;
/* Update time label. */ /* Update time label. */
this->time_lbl->setText(this->status_info_task->GetCurrentTimeString()); bool is_am = true;
struct tm *timeinfo = status_info_data->timeinfo;
timeinfo->tm_mon++;
timeinfo->tm_year += 1900;
if ("root_view/time_format"_i18n.compare("12") == 0)
{
/* Adjust time for 12-hour clock. */
if (timeinfo->tm_hour > 12)
{
timeinfo->tm_hour -= 12;
is_am = false;
} else
if (!timeinfo->tm_hour)
{
timeinfo->tm_hour = 12;
}
}
this->time_lbl->setText(i18n::getStr("root_view/date"_i18n, timeinfo->tm_year, timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, \
is_am ? "AM" : "PM"));
/* Update battery labels. */ /* Update battery labels. */
this->status_info_task->GetBatteryStats(&charge_percentage, &charger_type); u32 charge_percentage = status_info_data->charge_percentage;
PsmChargerType charger_type = status_info_data->charger_type;
this->battery_icon->setText(charger_type != PsmChargerType_Unconnected ? "\uE1A3" : (charge_percentage <= 15 ? "\uE19C" : "\uE1A4")); 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->setColor(charger_type != PsmChargerType_Unconnected ? nvgRGB(0, 255, 0) : (charge_percentage <= 15 ? nvgRGB(255, 0, 0) : brls::Application::getTheme()->textColor));
@ -102,7 +117,8 @@ namespace nxdt::views
this->battery_percentage->setText(fmt::format("{}%", charge_percentage)); this->battery_percentage->setText(fmt::format("{}%", charge_percentage));
/* Update network label. */ /* Update network label. */
this->status_info_task->GetNetworkStats(&connection_type, &signal_strength, &connection_status, &ip_addr); NifmInternetConnectionType connection_type = status_info_data->connection_type;
char *ip_addr = status_info_data->ip_addr;
this->connection_icon->setText(!connection_type ? "\uE195" : (connection_type == NifmInternetConnectionType_WiFi ? "\uE63E" : "\uE8BE")); this->connection_icon->setText(!connection_type ? "\uE195" : (connection_type == NifmInternetConnectionType_WiFi ? "\uE63E" : "\uE8BE"));
this->connection_status_lbl->setText(ip_addr ? std::string(ip_addr) : "root_view/not_connected"_i18n); this->connection_status_lbl->setText(ip_addr ? std::string(ip_addr) : "root_view/not_connected"_i18n);
@ -117,6 +133,12 @@ namespace nxdt::views
/* Unregister status info task listener. */ /* Unregister status info task listener. */
this->status_info_task->UnregisterListener(this->status_info_task_sub); this->status_info_task->UnregisterListener(this->status_info_task_sub);
/* Stop background tasks. */
this->gc_status_task->stop();
this->title_task->stop();
this->ums_task->stop();
this->usb_host_task->stop();
/* Destroy labels. */ /* Destroy labels. */
delete this->applet_mode_lbl; delete this->applet_mode_lbl;
delete this->time_lbl; delete this->time_lbl;
@ -124,16 +146,12 @@ namespace nxdt::views
delete this->battery_percentage; delete this->battery_percentage;
delete this->connection_icon; delete this->connection_icon;
delete this->connection_status_lbl; delete this->connection_status_lbl;
/* Stop background tasks. */
this->gc_status_task->stop();
this->title_task->stop();
this->ums_task->stop();
this->usb_host_task->stop();
} }
void RootView::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) void RootView::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{ {
brls::AppletFrame::draw(vg, x, y, width, height, style, ctx);
if (this->applet_mode) this->applet_mode_lbl->frame(ctx); if (this->applet_mode) this->applet_mode_lbl->frame(ctx);
this->time_lbl->frame(ctx); this->time_lbl->frame(ctx);
@ -143,8 +161,6 @@ namespace nxdt::views
this->connection_icon->frame(ctx); this->connection_icon->frame(ctx);
this->connection_status_lbl->frame(ctx); this->connection_status_lbl->frame(ctx);
brls::AppletFrame::draw(vg, x, y, width, height, style, ctx);
} }
void RootView::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) void RootView::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)

View file

@ -23,16 +23,13 @@
#include <tasks.hpp> #include <tasks.hpp>
#include <arpa/inet.h> #include <arpa/inet.h>
#define NXDT_TASK_INTERVAL 100 /* 100 ms. */ #define NXDT_TASK_INTERVAL 250 /* 250 ms. */
namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
namespace nxdt::tasks namespace nxdt::tasks
{ {
/* Status info task. */ /* Status info task. */
StatusInfoTask::StatusInfoTask(void) : brls::RepeatingTask(1000) StatusInfoTask::StatusInfoTask(void) : brls::RepeatingTask(NXDT_TASK_INTERVAL)
{ {
brls::RepeatingTask::start(); brls::RepeatingTask::start();
brls::Logger::debug("Status info task started."); brls::Logger::debug("Status info task started.");
@ -40,9 +37,6 @@ namespace nxdt::tasks
StatusInfoTask::~StatusInfoTask(void) StatusInfoTask::~StatusInfoTask(void)
{ {
/* Clear current time string. */
this->cur_time.clear();
brls::Logger::debug("Status info task stopped."); brls::Logger::debug("Status info task stopped.");
} }
@ -50,72 +44,37 @@ namespace nxdt::tasks
{ {
brls::RepeatingTask::run(current_time); brls::RepeatingTask::run(current_time);
StatusInfoData *status_info_data = &(this->status_info_data);
/* Get current time. */ /* Get current time. */
bool is_am = true;
time_t unix_time = time(NULL); time_t unix_time = time(NULL);
struct tm *timeinfo = localtime(&unix_time); status_info_data->timeinfo = localtime(&unix_time);
if (timeinfo->tm_hour > 12)
{
timeinfo->tm_hour -= 12;
is_am = false;
} else
if (!timeinfo->tm_hour)
{
timeinfo->tm_hour = 12;
}
timeinfo->tm_mon++;
timeinfo->tm_year += 1900;
this->cur_time.clear();
this->cur_time = i18n::getStr("root_view/date"_i18n, timeinfo->tm_year, timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, is_am ? "AM" : "PM");
/* Get battery stats. */ /* Get battery stats. */
psmGetBatteryChargePercentage(&(this->charge_percentage)); psmGetBatteryChargePercentage(&(status_info_data->charge_percentage));
psmGetChargerType(&(this->charger_type)); psmGetChargerType(&(status_info_data->charger_type));
/* Get network connection status. */ /* Get network connection status. */
Result rc = nifmGetInternetConnectionStatus(&(this->connection_type), &(this->signal_strength), &(this->connection_status)); u32 signal_strength = 0;
NifmInternetConnectionStatus connection_status = (NifmInternetConnectionStatus)0;
Result rc = nifmGetInternetConnectionStatus(&(status_info_data->connection_type), &signal_strength, &connection_status);
if (R_SUCCEEDED(rc)) if (R_SUCCEEDED(rc))
{ {
if (this->connection_type && this->connection_status == NifmInternetConnectionStatus_Connected) if (status_info_data->connection_type && connection_status == NifmInternetConnectionStatus_Connected)
{ {
struct in_addr addr = { .s_addr = 0 }; struct in_addr addr = { .s_addr = 0 };
nifmGetCurrentIpAddress(&(addr.s_addr)); nifmGetCurrentIpAddress(&(addr.s_addr));
this->ip_addr = inet_ntoa(addr); status_info_data->ip_addr = inet_ntoa(addr);
} else { } else {
this->ip_addr = NULL; status_info_data->ip_addr = NULL;
} }
} else { } else {
this->connection_type = (NifmInternetConnectionType)0; status_info_data->connection_type = (NifmInternetConnectionType)0;
this->signal_strength = 0; status_info_data->ip_addr = NULL;
this->connection_status = (NifmInternetConnectionStatus)0;
this->ip_addr = NULL;
} }
this->status_info_event.fire(); this->status_info_event.fire(status_info_data);
}
std::string StatusInfoTask::GetCurrentTimeString(void)
{
return this->cur_time;
}
void StatusInfoTask::GetBatteryStats(u32 *out_charge_percentage, PsmChargerType *out_charger_type)
{
if (!out_charge_percentage || !out_charger_type) return;
*out_charge_percentage = this->charge_percentage;
*out_charger_type = this->charger_type;
}
void StatusInfoTask::GetNetworkStats(NifmInternetConnectionType *out_connection_type, u32 *out_signal_strength, NifmInternetConnectionStatus *out_connection_status, char **out_ip_addr)
{
if (!out_connection_type || !out_signal_strength || !out_connection_status || !out_ip_addr) return;
*out_connection_type = this->connection_type;
*out_signal_strength = this->signal_strength;
*out_connection_status = this->connection_status;
*out_ip_addr = this->ip_addr;
} }
/* Gamecard task. */ /* Gamecard task. */
@ -168,28 +127,6 @@ namespace nxdt::tasks
brls::Logger::debug("Title task stopped."); brls::Logger::debug("Title task stopped.");
} }
void TitleTask::PopulateApplicationMetadataVector(bool is_system)
{
TitleApplicationMetadata **app_metadata = NULL;
u32 app_metadata_count = 0;
/* Get pointer to output vector. */
TitleApplicationMetadataVector *vector = (is_system ? &(this->system_metadata) : &(this->user_metadata));
if (vector->size()) vector->clear();
/* Get application metadata entries. */
app_metadata = titleGetApplicationMetadataEntries(is_system, &app_metadata_count);
if (!app_metadata) return;
/* Fill output vector. */
for(u32 i = 0; i < app_metadata_count; i++) vector->push_back(app_metadata[i]);
/* Free application metadata array. */
free(app_metadata);
brls::Logger::debug("Retrieved {} {} metadata {}.", app_metadata_count, is_system ? "system" : "user", app_metadata_count == 1 ? "entry" : "entries");
}
void TitleTask::run(retro_time_t current_time) void TitleTask::run(retro_time_t current_time)
{ {
brls::RepeatingTask::run(current_time); brls::RepeatingTask::run(current_time);
@ -200,7 +137,7 @@ namespace nxdt::tasks
this->PopulateApplicationMetadataVector(false); this->PopulateApplicationMetadataVector(false);
/* Fire task event. */ /* Fire task event. */
this->title_event.fire(); this->title_event.fire(&(this->user_metadata));
brls::Logger::debug("Title info updated."); brls::Logger::debug("Title info updated.");
} }
} }
@ -210,6 +147,29 @@ namespace nxdt::tasks
return (is_system ? &(this->system_metadata) : &(this->user_metadata)); return (is_system ? &(this->system_metadata) : &(this->user_metadata));
} }
void TitleTask::PopulateApplicationMetadataVector(bool is_system)
{
TitleApplicationMetadata **app_metadata = NULL;
u32 app_metadata_count = 0;
/* Get pointer to output vector. */
TitleApplicationMetadataVector *vector = (is_system ? &(this->system_metadata) : &(this->user_metadata));
vector->clear();
/* Get application metadata entries. */
app_metadata = titleGetApplicationMetadataEntries(is_system, &app_metadata_count);
if (app_metadata)
{
/* Fill output vector. */
for(u32 i = 0; i < app_metadata_count; i++) vector->push_back(app_metadata[i]);
/* Free application metadata array. */
free(app_metadata);
}
brls::Logger::debug("Retrieved {} {} metadata {}.", app_metadata_count, is_system ? "system" : "user", app_metadata_count == 1 ? "entry" : "entries");
}
/* USB Mass Storage task. */ /* USB Mass Storage task. */
UmsTask::UmsTask(void) : brls::RepeatingTask(NXDT_TASK_INTERVAL) UmsTask::UmsTask(void) : brls::RepeatingTask(NXDT_TASK_INTERVAL)
@ -226,27 +186,6 @@ namespace nxdt::tasks
brls::Logger::debug("UMS task stopped."); brls::Logger::debug("UMS task stopped.");
} }
void UmsTask::PopulateUmsDeviceVector(void)
{
UsbHsFsDevice *ums_devices = NULL;
u32 ums_device_count = 0;
/* Clear UMS device vector (if needed). */
if (this->ums_devices.size()) this->ums_devices.clear();
/* Get UMS devices. */
ums_devices = umsGetDevices(&ums_device_count);
if (!ums_devices) return;
/* Fill UMS device vector. */
for(u32 i = 0; i < ums_device_count; i++) this->ums_devices.push_back(ums_devices[i]);
/* Free UMS devices array. */
free(ums_devices);
brls::Logger::debug("Retrieved info for {} UMS {}.", ums_device_count, ums_device_count == 1 ? "device" : "devices");
}
void UmsTask::run(retro_time_t current_time) void UmsTask::run(retro_time_t current_time)
{ {
brls::RepeatingTask::run(current_time); brls::RepeatingTask::run(current_time);
@ -257,14 +196,31 @@ namespace nxdt::tasks
this->PopulateUmsDeviceVector(); this->PopulateUmsDeviceVector();
/* Fire task event. */ /* Fire task event. */
this->ums_event.fire(); this->ums_event.fire(&(this->ums_devices));
brls::Logger::debug("UMS device info updated."); brls::Logger::debug("UMS device info updated.");
} }
} }
UmsDeviceVector* UmsTask::GetUmsDevices(void) void UmsTask::PopulateUmsDeviceVector(void)
{ {
return &(this->ums_devices); UsbHsFsDevice *ums_devices = NULL;
u32 ums_device_count = 0;
/* Clear UMS device vector. */
this->ums_devices.clear();
/* Get UMS devices. */
ums_devices = umsGetDevices(&ums_device_count);
if (ums_devices)
{
/* Fill UMS device vector. */
for(u32 i = 0; i < ums_device_count; i++) this->ums_devices.push_back(ums_devices[i]);
/* Free UMS devices array. */
free(ums_devices);
}
brls::Logger::debug("Retrieved info for {} UMS {}.", ums_device_count, ums_device_count == 1 ? "device" : "devices");
} }
/* USB host device connection task. */ /* USB host device connection task. */
@ -287,8 +243,8 @@ namespace nxdt::tasks
this->cur_usb_host_status = usbIsReady(); this->cur_usb_host_status = usbIsReady();
if (this->cur_usb_host_status != this->prev_usb_host_status) if (this->cur_usb_host_status != this->prev_usb_host_status)
{ {
this->usb_host_event.fire(this->cur_usb_host_status);
this->prev_usb_host_status = this->cur_usb_host_status; this->prev_usb_host_status = this->cur_usb_host_status;
this->usb_host_event.fire(this->cur_usb_host_status);
brls::Logger::debug("USB host status change triggered: {}.", this->cur_usb_host_status); brls::Logger::debug("USB host status change triggered: {}.", this->cur_usb_host_status);
} }
} }