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:
parent
4453e7950f
commit
4d1b3660c3
14 changed files with 460 additions and 222 deletions
18
Makefile
18
Makefile
|
@ -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)
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
|
@ -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
44
include/core/nxdt_bfsar.h
Normal 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__ */
|
|
@ -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). */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
204
source/core/nxdt_bfsar.c
Normal 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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
168
source/tasks.cpp
168
source/tasks.cpp
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue