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_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_MINOR := 0
@ -49,6 +54,7 @@ APP_TITLE := nxdumptool
APP_AUTHOR := DarkMatterCore
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
# TODO: remove this after the PoC builds are no longer needed.
ifneq ($(origin BUILD_TYPE),undefined)
APP_TITLE := ${BUILD_TYPE}
endif
@ -61,11 +67,11 @@ ICON := romfs/icon/${APP_TITLE}.jpg
INCLUDES := include include/core include/fatfs
ROMFS := romfs
USBHSFS_PATH := $(TOPDIR)/libs/libusbhsfs
BOREALIS_PATH := libs/borealis
BOREALIS_RESOURCES := romfs:/
USBHSFS_PATH := $(TOPDIR)/libs/libusbhsfs
#---------------------------------------------------------------------------------
# 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 += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO}
CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DAPP_AUTHOR=\"${APP_AUTHOR}\" -DAPP_VERSION=\"${APP_VERSION}\"
CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\"
CFLAGS += -DGIT_BRANCH=\"${GIT_BRANCH}\" -DGIT_COMMIT=\"${GIT_COMMIT}\" -DGIT_REV=\"${GIT_REV}\"
CFLAGS += -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\""
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags`
CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
CFLAGS += `aarch64-none-elf-pkg-config libturbojpeg --cflags`
#CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
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)
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
@ -183,7 +189,7 @@ ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean clean_all all
.PHONY: $(BUILD) all clean clean_all
#---------------------------------------------------------------------------------
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);
// 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)
{
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);
// 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)
{
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 ES_SYSMODULE_TID (u64)0x0100000000000033
#define SYSTEM_UPDATE_TID (u64)0x0100000000000816
#define QLAUNCH_TID (u64)0x0100000000001000
#define FAT32_FILESIZE_LIMIT (u64)0xFFFFFFFF /* 4 GiB - 1 (4294967295 bytes). */

View File

@ -33,18 +33,18 @@ namespace nxdt::views
private:
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::GameCardTask *gc_status_task = nullptr;
nxdt::tasks::TitleTask *title_task = nullptr;
nxdt::tasks::UmsTask *ums_task = nullptr;
nxdt::tasks::UsbHostTask *usb_host_task = nullptr;
nxdt::tasks::VoidEvent::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;
nxdt::tasks::StatusInfoEvent::Subscription status_info_task_sub;
protected:
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
{
/* Custom event types used by the tasks defined below. */
typedef brls::Event<GameCardStatus> GameCardStatusEvent;
typedef brls::VoidEvent VoidEvent;
typedef brls::Event<bool> BooleanEvent;
/* Used to hold status info data. */
typedef struct {
struct tm *timeinfo;
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;
/* Custom vector type used to hold UMS devices. */
/* Used to hold UMS devices. */
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. */
/* Its event returns a pointer to a StatusInfoData struct. */
class StatusInfoTask: public brls::RepeatingTask
{
private:
VoidEvent status_info_event;
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;
StatusInfoEvent status_info_event;
StatusInfoData status_info_data = {0};
protected:
void run(retro_time_t current_time) override;
@ -67,27 +70,23 @@ namespace nxdt::tasks
StatusInfoTask(void);
~StatusInfoTask(void);
std::string GetCurrentTimeString(void);
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)
ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback 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);
}
};
/* Gamecard task. */
/* Its event returns a GameCardStatus value. */
class GameCardTask: public brls::RepeatingTask
{
private:
GameCardStatusEvent gc_status_event;
GameCardStatus cur_gc_status = GameCardStatus_NotInserted;
GameCardStatus prev_gc_status = GameCardStatus_NotInserted;
@ -110,10 +109,11 @@ namespace nxdt::tasks
};
/* 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
{
private:
VoidEvent title_event;
TitleEvent title_event;
TitleApplicationMetadataVector system_metadata;
TitleApplicationMetadataVector user_metadata;
@ -127,24 +127,26 @@ namespace nxdt::tasks
TitleTask(void);
~TitleTask(void);
/* Intentionally left here to let system titles views retrieve metadata. */
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);
}
ALWAYS_INLINE void UnregisterListener(VoidEvent::Subscription subscription)
ALWAYS_INLINE void UnregisterListener(TitleEvent::Subscription subscription)
{
this->title_event.unsubscribe(subscription);
}
};
/* USB Mass Storage task. */
/* Its event returns a pointer to a UmsDeviceVector. */
class UmsTask: public brls::RepeatingTask
{
private:
VoidEvent ums_event;
UmsEvent ums_event;
UmsDeviceVector ums_devices;
@ -157,14 +159,12 @@ namespace nxdt::tasks
UmsTask(void);
~UmsTask(void);
UmsDeviceVector* GetUmsDevices(void);
ALWAYS_INLINE VoidEvent::Subscription RegisterListener(VoidEvent::Callback cb)
ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback 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);
}
@ -174,7 +174,7 @@ namespace nxdt::tasks
class UsbHostTask: public brls::RepeatingTask
{
private:
BooleanEvent usb_host_event;
UsbHostEvent usb_host_event;
bool cur_usb_host_status = false;
bool prev_usb_host_status = false;
@ -186,12 +186,12 @@ namespace nxdt::tasks
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);
}
ALWAYS_INLINE void UnregisterListener(BooleanEvent::Subscription subscription)
ALWAYS_INLINE void UnregisterListener(UsbHostEvent::Subscription 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",
"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_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",

View File

@ -961,7 +961,7 @@ static bool gamecardGetHandleAndStorage(u32 partition)
NX_INLINE void gamecardCloseHandle(void)
{
/* TO DO: find a way to properly close a gamecard handle. */
/* TODO: find a way to properly close a gamecard handle. */
if (!g_gameCardHandle.value) return;
svcCloseHandle(g_gameCardHandle.value);
g_gameCardHandle.value = 0;

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 "title.h"
#include "bfttf.h"
#include "nxdt_bfsar.h"
#include "fatfs/ff.h"
/* 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)
{
Result rc = 0;
bool ret = false;
bool ret = false, flag = false;
SCOPED_LOCK(&g_resourcesMutex)
{
@ -105,7 +106,7 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
/* Create logfile. */
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);
/* Log Horizon OS version. */
@ -153,10 +154,18 @@ bool utilsInitializeResources(const int program_argc, const char **program_argv)
/* Initialize BFTTF interface. */
if (!bfttfInitialize()) break;
/* Initialize BFSAR interface. */
//if (!bfsarInitialize()) break;
/* Mount eMMC BIS System partition. */
if (!utilsMountEmmcBisSystemPartitionStorage()) break;
/* Enable video recording. */
rc = appletIsGamePlayRecordingSupported(&flag);
if (R_SUCCEEDED(rc) && flag) appletInitializeGamePlayRecording();
/* 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);
/* Overclock system. */
@ -212,6 +221,9 @@ void utilsCloseResources(void)
/* Unmount eMMC BIS System partition. */
utilsUnmountEmmcBisSystemPartitionStorage();
/* Deinitialize BFSAR interface. */
bfsarExit();
/* Deinitialize BFTTF interface. */
bfttfExit();
@ -832,7 +844,7 @@ static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param)
if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return;
/* TO DO: read config here to actually know the value to use with utilsOverclockSystem. */
/* TODO: read config here to actually know the value to use with utilsOverclockSystem. */
utilsOverclockSystem(true);
}

View File

@ -972,17 +972,21 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
TitleInfo **titles = title_storage->titles;
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))
{
LOG_MSG("Invalid parameters!");
break;
}
GameCardHeader gc_header = {0};
size_t cur_filename_len = 0;
char app_name[0x400] = {0};
bool error = false;
/* Check if the gamecard title storage is empty. */
/* This is especially true for Kiosk / Quest gamecards. */
if (!titles || !title_count) goto fallback;
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;
}
fallback:
if (!filename && !error)
{
LOG_MSG("Error: the inserted gamecard doesn't hold any user applications!");
/* 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");
if (gamecardGetHeader(&gc_header))
@ -1074,6 +1078,7 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
}
filename = strdup(app_name);
if (!filename) LOG_MSG("Failed to duplicate fallback filename!");
}
}
@ -1093,41 +1098,29 @@ const char *titleGetNcmContentMetaTypeName(u8 content_meta_type)
NX_INLINE void titleFreeApplicationMetadata(void)
{
if (g_systemMetadata)
for(u8 i = 0; i < 2; i++)
{
for(u32 i = 0; i < g_systemMetadataCount; i++)
{
TitleApplicationMetadata *cur_app_metadata = g_systemMetadata[i];
if (cur_app_metadata)
{
if (cur_app_metadata->icon) free(cur_app_metadata->icon);
free(cur_app_metadata);
}
}
TitleApplicationMetadata **cached_app_metadata = (i == 0 ? g_systemMetadata : g_userMetadata);
u32 cached_app_metadata_count = (i == 0 ? g_systemMetadataCount : g_userMetadataCount);
free(g_systemMetadata);
g_systemMetadata = NULL;
if (cached_app_metadata)
{
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->icon) free(cur_app_metadata->icon);
free(cur_app_metadata);
}
}
free(cached_app_metadata);
}
}
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_userMetadata = NULL;
}
g_userMetadataCount = 0;
g_systemMetadata = g_userMetadata = NULL;
g_systemMetadataCount = g_userMetadataCount = 0;
}
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool is_system, bool free_entries)

View File

@ -27,12 +27,18 @@
//#include <options_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
{
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. */
this->applet_mode = utilsAppletModeCheck();
@ -65,11 +71,6 @@ namespace nxdt::views
this->ums_task = new nxdt::tasks::UmsTask();
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. */
this->addTab("root_view/tabs/gamecard"_i18n, new GameCardTab(this->gc_status_task));
this->addSeparator();
@ -81,20 +82,34 @@ namespace nxdt::views
this->addTab("root_view/tabs/about"_i18n, new AboutTab());
/* Subscribe to status info event. */
this->status_info_task_sub = this->status_info_task->RegisterListener([this](void) {
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;
this->status_info_task_sub = this->status_info_task->RegisterListener([this](const nxdt::tasks::StatusInfoData *status_info_data) {
/* 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. */
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->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));
/* 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_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. */
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. */
delete this->applet_mode_lbl;
delete this->time_lbl;
@ -124,16 +146,12 @@ namespace nxdt::views
delete this->battery_percentage;
delete this->connection_icon;
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)
{
brls::AppletFrame::draw(vg, x, y, width, height, style, ctx);
if (this->applet_mode) this->applet_mode_lbl->frame(ctx);
this->time_lbl->frame(ctx);
@ -143,8 +161,6 @@ namespace nxdt::views
this->connection_icon->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)

View File

@ -23,16 +23,13 @@
#include <tasks.hpp>
#include <arpa/inet.h>
#define NXDT_TASK_INTERVAL 100 /* 100 ms. */
namespace i18n = brls::i18n; /* For getStr(). */
using namespace i18n::literals; /* For _i18n. */
#define NXDT_TASK_INTERVAL 250 /* 250 ms. */
namespace nxdt::tasks
{
/* Status info task. */
StatusInfoTask::StatusInfoTask(void) : brls::RepeatingTask(1000)
StatusInfoTask::StatusInfoTask(void) : brls::RepeatingTask(NXDT_TASK_INTERVAL)
{
brls::RepeatingTask::start();
brls::Logger::debug("Status info task started.");
@ -40,9 +37,6 @@ namespace nxdt::tasks
StatusInfoTask::~StatusInfoTask(void)
{
/* Clear current time string. */
this->cur_time.clear();
brls::Logger::debug("Status info task stopped.");
}
@ -50,72 +44,37 @@ namespace nxdt::tasks
{
brls::RepeatingTask::run(current_time);
StatusInfoData *status_info_data = &(this->status_info_data);
/* Get current time. */
bool is_am = true;
time_t unix_time = time(NULL);
struct tm *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");
status_info_data->timeinfo = localtime(&unix_time);
/* Get battery stats. */
psmGetBatteryChargePercentage(&(this->charge_percentage));
psmGetChargerType(&(this->charger_type));
psmGetBatteryChargePercentage(&(status_info_data->charge_percentage));
psmGetChargerType(&(status_info_data->charger_type));
/* 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 (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 };
nifmGetCurrentIpAddress(&(addr.s_addr));
this->ip_addr = inet_ntoa(addr);
status_info_data->ip_addr = inet_ntoa(addr);
} else {
this->ip_addr = NULL;
status_info_data->ip_addr = NULL;
}
} else {
this->connection_type = (NifmInternetConnectionType)0;
this->signal_strength = 0;
this->connection_status = (NifmInternetConnectionStatus)0;
this->ip_addr = NULL;
status_info_data->connection_type = (NifmInternetConnectionType)0;
status_info_data->ip_addr = NULL;
}
this->status_info_event.fire();
}
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;
this->status_info_event.fire(status_info_data);
}
/* Gamecard task. */
@ -168,28 +127,6 @@ namespace nxdt::tasks
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)
{
brls::RepeatingTask::run(current_time);
@ -200,7 +137,7 @@ namespace nxdt::tasks
this->PopulateApplicationMetadataVector(false);
/* Fire task event. */
this->title_event.fire();
this->title_event.fire(&(this->user_metadata));
brls::Logger::debug("Title info updated.");
}
}
@ -210,6 +147,29 @@ namespace nxdt::tasks
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. */
UmsTask::UmsTask(void) : brls::RepeatingTask(NXDT_TASK_INTERVAL)
@ -226,27 +186,6 @@ namespace nxdt::tasks
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)
{
brls::RepeatingTask::run(current_time);
@ -257,14 +196,31 @@ namespace nxdt::tasks
this->PopulateUmsDeviceVector();
/* Fire task event. */
this->ums_event.fire();
this->ums_event.fire(&(this->ums_devices));
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. */
@ -287,8 +243,8 @@ namespace nxdt::tasks
this->cur_usb_host_status = usbIsReady();
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->usb_host_event.fire(this->cur_usb_host_status);
brls::Logger::debug("USB host status change triggered: {}.", this->cur_usb_host_status);
}
}