diff --git a/include/core/gamecard.h b/include/core/gamecard.h index 1eab01f..7665c3b 100644 --- a/include/core/gamecard.h +++ b/include/core/gamecard.h @@ -268,6 +268,14 @@ bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemConte /// If you need to get entry information by index, just retrieve the Hash FS context for the target partition and use Hash FS functions on it. bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size); +/// Takes a GameCardFwVersion value. Returns a pointer to a string that represents the minimum HOS version that matches the provided LAFW version. +/// Returns NULL if the provided value is out of range. +const char *gamecardGetRequiredHosVersionString(u64 fw_version); + +/// Takes a GameCardCompatibilityType value. Returns a pointer to a string that represents the provided compatibility type. +/// Returns NULL if the provided value is out of range. +const char *gamecardGetCompatibilityTypeString(u8 compatibility_type); + #ifdef __cplusplus } #endif diff --git a/include/gamecard_tab.hpp b/include/gamecard_tab.hpp index e09ff86..4346da0 100644 --- a/include/gamecard_tab.hpp +++ b/include/gamecard_tab.hpp @@ -24,7 +24,7 @@ #ifndef __GAMECARD_TAB_HPP__ #define __GAMECARD_TAB_HPP__ -#include "tasks.hpp" +#include "root_view.hpp" #include "layered_error_frame.hpp" #include "focusable_item.hpp" @@ -35,7 +35,8 @@ namespace nxdt::views typedef bool (*GameCardSizeFunc)(u64 *out_size); private: - nxdt::tasks::GameCardTask *gc_status_task = nullptr; + RootView *root_view = nullptr; + nxdt::tasks::GameCardStatusEvent::Subscription gc_status_task_sub; GameCardStatus gc_status = GameCardStatus_NotInserted; @@ -58,7 +59,7 @@ namespace nxdt::views std::string GetFormattedSizeString(GameCardSizeFunc func); public: - GameCardTab(nxdt::tasks::GameCardTask *gc_status_task); + GameCardTab(RootView *root_view); ~GameCardTab(void); }; } diff --git a/include/options_tab.hpp b/include/options_tab.hpp index 0ed0bec..2b3a0e0 100644 --- a/include/options_tab.hpp +++ b/include/options_tab.hpp @@ -26,7 +26,7 @@ #include -#include "tasks.hpp" +#include "root_view.hpp" namespace nxdt::views { @@ -96,7 +96,7 @@ namespace nxdt::views class OptionsTab: public brls::List { private: - nxdt::tasks::StatusInfoTask *status_info_task = nullptr; + RootView *root_view = nullptr; bool display_notification = true; brls::menu_timer_t notification_timer = 0.0f; @@ -104,7 +104,7 @@ namespace nxdt::views void DisplayNotification(std::string str); public: - OptionsTab(nxdt::tasks::StatusInfoTask *status_info_task); + OptionsTab(RootView *root_view); ~OptionsTab(void); }; } diff --git a/include/root_view.hpp b/include/root_view.hpp index 8238833..40e4816 100644 --- a/include/root_view.hpp +++ b/include/root_view.hpp @@ -26,6 +26,10 @@ #include "tasks.hpp" +#define EVENT_SUBSCRIPTION(func_name, event_type, task_name) \ + ALWAYS_INLINE nxdt::tasks::event_type::Subscription Register##func_name##Listener(nxdt::tasks::event_type::Callback cb) { return this->task_name->RegisterListener(cb); } \ + ALWAYS_INLINE void Unregister##func_name##Listener(nxdt::tasks::event_type::Subscription subscription) { this->task_name->UnregisterListener(subscription); } + namespace nxdt::views { class RootView: public brls::TabFrame @@ -58,7 +62,27 @@ namespace nxdt::views ~RootView(void); static std::string GetFormattedDateString(const struct tm& timeinfo); + + /* Wrappers for task functions. */ + + ALWAYS_INLINE bool IsInternetConnectionAvailable(void) + { + return this->status_info_task->IsInternetConnectionAvailable(); + } + + ALWAYS_INLINE const nxdt::tasks::TitleApplicationMetadataVector* GetApplicationMetadata(bool is_system) + { + return this->title_task->GetApplicationMetadata(is_system); + } + + EVENT_SUBSCRIPTION(StatusInfoTask, StatusInfoEvent, status_info_task); + EVENT_SUBSCRIPTION(GameCardTask, GameCardStatusEvent, gc_status_task); + EVENT_SUBSCRIPTION(TitleTask, TitleEvent, title_task); + EVENT_SUBSCRIPTION(UmsTask, UmsEvent, ums_task); + EVENT_SUBSCRIPTION(UsbHostTask, UsbHostEvent, usb_host_task); }; } +#undef EVENT_SUBSCRIPTION + #endif /* __ROOT_VIEW_HPP__ */ diff --git a/include/tasks.hpp b/include/tasks.hpp index 38b5e06..ad7ccbd 100644 --- a/include/tasks.hpp +++ b/include/tasks.hpp @@ -33,6 +33,10 @@ #include "core/usb.h" #include "download_task.hpp" +#define EVENT_SUBSCRIPTION(event_type, event_name) \ + ALWAYS_INLINE event_type::Subscription RegisterListener(event_type::Callback cb) { return this->event_name.subscribe(cb); } \ + ALWAYS_INLINE void UnregisterListener(event_type::Subscription subscription) { this->event_name.unsubscribe(subscription); } + namespace nxdt::tasks { /* Used to hold status info data. */ @@ -74,15 +78,7 @@ namespace nxdt::tasks bool IsInternetConnectionAvailable(void); - ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback cb) - { - return this->status_info_event.subscribe(cb); - } - - ALWAYS_INLINE void UnregisterListener(StatusInfoEvent::Subscription subscription) - { - this->status_info_event.unsubscribe(subscription); - } + EVENT_SUBSCRIPTION(StatusInfoEvent, status_info_event); }; /* Gamecard task. */ @@ -102,15 +98,7 @@ namespace nxdt::tasks GameCardTask(void); ~GameCardTask(void); - ALWAYS_INLINE GameCardStatusEvent::Subscription RegisterListener(GameCardStatusEvent::Callback cb) - { - return this->gc_status_event.subscribe(cb); - } - - ALWAYS_INLINE void UnregisterListener(GameCardStatusEvent::Subscription subscription) - { - this->gc_status_event.unsubscribe(subscription); - } + EVENT_SUBSCRIPTION(GameCardStatusEvent, gc_status_event); }; /* Title task. */ @@ -135,15 +123,7 @@ namespace nxdt::tasks /* Intentionally left here to let system titles views retrieve metadata. */ const TitleApplicationMetadataVector* GetApplicationMetadata(bool is_system); - ALWAYS_INLINE TitleEvent::Subscription RegisterListener(TitleEvent::Callback cb) - { - return this->title_event.subscribe(cb); - } - - ALWAYS_INLINE void UnregisterListener(TitleEvent::Subscription subscription) - { - this->title_event.unsubscribe(subscription); - } + EVENT_SUBSCRIPTION(TitleEvent, title_event); }; /* USB Mass Storage task. */ @@ -152,7 +132,6 @@ namespace nxdt::tasks { private: UmsEvent ums_event; - UmsDeviceVector ums_devices; void PopulateUmsDeviceVector(void); @@ -164,15 +143,7 @@ namespace nxdt::tasks UmsTask(void); ~UmsTask(void); - ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback cb) - { - return this->ums_event.subscribe(cb); - } - - ALWAYS_INLINE void UnregisterListener(UmsEvent::Subscription subscription) - { - this->ums_event.unsubscribe(subscription); - } + EVENT_SUBSCRIPTION(UmsEvent, ums_event); }; /* USB host device connection task. */ @@ -190,16 +161,10 @@ namespace nxdt::tasks UsbHostTask(void); ~UsbHostTask(void); - ALWAYS_INLINE UsbHostEvent::Subscription RegisterListener(UsbHostEvent::Callback cb) - { - return this->usb_host_event.subscribe(cb); - } - - ALWAYS_INLINE void UnregisterListener(UsbHostEvent::Subscription subscription) - { - this->usb_host_event.unsubscribe(subscription); - } + EVENT_SUBSCRIPTION(UsbHostEvent, usb_host_event); }; } +#undef EVENT_SUBSCRIPTION + #endif /* __TASKS_HPP__ */ diff --git a/include/titles_tab.hpp b/include/titles_tab.hpp index 5b92fa4..bcae7e6 100644 --- a/include/titles_tab.hpp +++ b/include/titles_tab.hpp @@ -24,7 +24,7 @@ #ifndef __TITLES_TAB_HPP__ #define __TITLES_TAB_HPP__ -#include "tasks.hpp" +#include "root_view.hpp" #include "layered_error_frame.hpp" namespace nxdt::views @@ -68,15 +68,15 @@ namespace nxdt::views class TitlesTab: public LayeredErrorFrame { private: - nxdt::tasks::TitleTask *title_task = nullptr; - nxdt::tasks::TitleEvent::Subscription title_task_sub; + RootView *root_view = nullptr; + nxdt::tasks::TitleEvent::Subscription title_task_sub; bool is_system = false; void PopulateList(const nxdt::tasks::TitleApplicationMetadataVector* app_metadata); public: - TitlesTab(nxdt::tasks::TitleTask *title_task, bool is_system); + TitlesTab(RootView *root_view, bool is_system); ~TitlesTab(void); }; } diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 4f5ae4a..c29f6fd 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -149,6 +149,20 @@ static const char *g_gameCardHfsPartitionNames[] = { [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Boot)] = "boot" }; +static const char *g_gameCardHosVersionStrings[GameCardFwVersion_Count] = { + [GameCardFwVersion_ForDev] = "1.0.0", + [GameCardFwVersion_Since100NUP] = "1.0.0", + [GameCardFwVersion_Since400NUP] = "4.0.0", + [GameCardFwVersion_Since900NUP] = "9.0.0", + [GameCardFwVersion_Since1100NUP] = "11.0.0", + [GameCardFwVersion_Since1200NUP] = "12.0.0" +}; + +static const char *g_gameCardCompatibilityTypeStrings[GameCardCompatibilityType_Count] = { + [GameCardCompatibilityType_Normal] = "Normal", + [GameCardCompatibilityType_Terra] = "Terra" +}; + /* Function prototypes. */ static bool gamecardGetLotusAsicFirmwareVersion(void); @@ -518,6 +532,16 @@ bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char return ret; } +const char *gamecardGetRequiredHosVersionString(u64 fw_version) +{ + return (fw_version < GameCardFwVersion_Count ? g_gameCardHosVersionStrings[fw_version] : NULL); +} + +const char *gamecardGetCompatibilityTypeString(u8 compatibility_type) +{ + return (compatibility_type < GameCardCompatibilityType_Count ? g_gameCardCompatibilityTypeStrings[compatibility_type] : NULL); +} + static bool gamecardGetLotusAsicFirmwareVersion(void) { u64 fw_version = 0; diff --git a/source/gamecard_tab.cpp b/source/gamecard_tab.cpp index 2fadc17..64850c9 100644 --- a/source/gamecard_tab.cpp +++ b/source/gamecard_tab.cpp @@ -27,21 +27,7 @@ using namespace i18n::literals; /* For _i18n. */ namespace nxdt::views { - static const char *GameCardFwVersionStrings[GameCardFwVersion_Count] = { - [GameCardFwVersion_ForDev] = "1.0.0+", - [GameCardFwVersion_Since100NUP] = "1.0.0+", - [GameCardFwVersion_Since400NUP] = "4.0.0+", - [GameCardFwVersion_Since900NUP] = "9.0.0+", - [GameCardFwVersion_Since1100NUP] = "11.0.0+", - [GameCardFwVersion_Since1200NUP] = "12.0.0+" - }; - - static const char *GameCardCompatibilityTypeStrings[GameCardCompatibilityType_Count] = { - [GameCardCompatibilityType_Normal] = "Normal", - [GameCardCompatibilityType_Terra] = "Terra" - }; - - GameCardTab::GameCardTab(nxdt::tasks::GameCardTask *gc_status_task) : LayeredErrorFrame("gamecard_tab/error_frame/not_inserted"_i18n), gc_status_task(gc_status_task) + GameCardTab::GameCardTab(RootView *root_view) : LayeredErrorFrame("gamecard_tab/error_frame/not_inserted"_i18n), root_view(root_view) { /* Set custom spacing. */ this->list->setSpacing(this->list->getSpacing() / 2); @@ -64,6 +50,11 @@ namespace nxdt::views this->list->addView(new brls::Header("gamecard_tab/list/dump_options"_i18n)); this->dump_card_image = new brls::ListItem("gamecard_tab/list/dump_card_image/label"_i18n, "gamecard_tab/list/dump_card_image/description"_i18n); + + /*this->dump_card_image->getClickEvent()->subscribe([](brls::View *view) { + + });*/ + this->list->addView(this->dump_card_image); this->dump_certificate = new brls::ListItem("gamecard_tab/list/dump_certificate/label"_i18n, "gamecard_tab/list/dump_certificate/description"_i18n); @@ -82,7 +73,8 @@ namespace nxdt::views this->list->addView(this->dump_hfs_partitions); /* Subscribe to gamecard status event. */ - this->gc_status_task_sub = this->gc_status_task->RegisterListener([this](GameCardStatus gc_status) { + this->gc_status_task_sub = this->root_view->RegisterGameCardTaskListener([this](GameCardStatus gc_status) { + /* Switch to the error layer if gamecard info hasn't been loaded. */ if (gc_status < GameCardStatus_InsertedAndInfoLoaded) this->SwitchLayerView(true); switch(gc_status) @@ -104,6 +96,7 @@ namespace nxdt::views break; case GameCardStatus_InsertedAndInfoLoaded: { + /* Fill properties table. */ GameCardInfo card_info = {0}; gamecardGetDecryptedCardInfoArea(&card_info); @@ -116,16 +109,17 @@ namespace nxdt::views upp_version->minor_relstep, upp_version->value)); u64 fw_version = card_info.fw_version; - this->lafw_version->setValue(fmt::format("{} ({})", fw_version, fw_version >= GameCardFwVersion_Count ? "generic/unknown"_i18n : GameCardFwVersionStrings[fw_version])); + this->lafw_version->setValue(fmt::format("{} ({})", fw_version, fw_version >= GameCardFwVersion_Count ? "generic/unknown"_i18n : gamecardGetRequiredHosVersionString(fw_version))); const VersionType2 *fw_mode = &(card_info.fw_mode); this->sdk_version->setValue(fmt::format("{}.{}.{}-{} (v{})", fw_mode->major, fw_mode->minor, fw_mode->micro, fw_mode->relstep, fw_mode->value)); u8 compatibility_type = card_info.compatibility_type; this->compatibility_type->setValue(fmt::format("{} ({})", \ - compatibility_type >= GameCardCompatibilityType_Count ? "generic/unknown"_i18n : GameCardCompatibilityTypeStrings[compatibility_type], \ + compatibility_type >= GameCardCompatibilityType_Count ? "generic/unknown"_i18n : gamecardGetCompatibilityTypeString(compatibility_type), \ compatibility_type)); + /* Switch to the list view. */ this->SwitchLayerView(false); break; @@ -134,6 +128,7 @@ namespace nxdt::views break; } + /* Update internal gamecard status. */ this->gc_status = gc_status; }); } @@ -141,7 +136,7 @@ namespace nxdt::views GameCardTab::~GameCardTab(void) { /* Unregister task listener. */ - this->gc_status_task->UnregisterListener(this->gc_status_task_sub); + this->root_view->UnregisterGameCardTaskListener(this->gc_status_task_sub); } std::string GameCardTab::GetFormattedSizeString(GameCardSizeFunc func) diff --git a/source/options_tab.cpp b/source/options_tab.cpp index d5ac50a..f0f14cc 100644 --- a/source/options_tab.cpp +++ b/source/options_tab.cpp @@ -198,7 +198,7 @@ namespace nxdt::views this->addStage(this->update_progress); /* Register cancel action. */ - this->registerAction("brls/hints/back"_i18n, brls::Key::B, [this](void){ + this->registerAction("brls/hints/back"_i18n, brls::Key::B, [this](void) { return this->onCancel(); }); @@ -294,7 +294,7 @@ namespace nxdt::views this->json_task.cancel(); /* Pop view. */ - brls::Application::popView(); + brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT); return true; } @@ -341,7 +341,7 @@ namespace nxdt::views } /* Register update action. */ - this->registerAction("options_tab/update_app/frame/update_action"_i18n, brls::Key::PLUS, [this](void){ + this->registerAction("options_tab/update_app/frame/update_action"_i18n, brls::Key::PLUS, [this](void) { /* Display update progress. */ this->DisplayUpdateProgress(); @@ -358,12 +358,12 @@ namespace nxdt::views void OptionsTabUpdateApplicationFrame::DisplayUpdateProgress(void) { /* Remove update action. */ - this->registerAction("options_tab/update_app/frame/update_action"_i18n, brls::Key::PLUS, [](void){ + this->registerAction("options_tab/update_app/frame/update_action"_i18n, brls::Key::PLUS, [](void) { return true; }, true); /* Register cancel action once more, using a different label. */ - this->registerAction("options_tab/update_dialog/cancel"_i18n, brls::Key::B, [this](void){ + this->registerAction("options_tab/update_dialog/cancel"_i18n, brls::Key::B, [this](void) { return this->onCancel(); }); @@ -397,7 +397,7 @@ namespace nxdt::views this->nextStage(); } - OptionsTab::OptionsTab(nxdt::tasks::StatusInfoTask *status_info_task) : brls::List(), status_info_task(status_info_task) + OptionsTab::OptionsTab(RootView *root_view) : brls::List(), root_view(root_view) { /* Set custom spacing. */ this->setSpacing(this->getSpacing() / 2); @@ -437,7 +437,7 @@ namespace nxdt::views }, static_cast(configGetInteger("naming_convention")), "options_tab/naming_convention/description"_i18n); - naming_convention->getValueSelectedEvent()->subscribe([](int selected){ + naming_convention->getValueSelectedEvent()->subscribe([](int selected) { /* Make sure the current value isn't out of bounds. */ if (selected < 0 || selected > static_cast(TitleNamingConvention_Count)) return; @@ -453,7 +453,7 @@ namespace nxdt::views brls::ListItem *update_nswdb_xml = new brls::ListItem("options_tab/update_nswdb_xml/label"_i18n, "options_tab/update_nswdb_xml/description"_i18n); update_nswdb_xml->getClickEvent()->subscribe([this](brls::View* view) { - if (!this->status_info_task->IsInternetConnectionAvailable()) + if (!this->root_view->IsInternetConnectionAvailable()) { /* Display a notification if no Internet connection is available. */ this->DisplayNotification("options_tab/notifications/no_internet_connection"_i18n); @@ -477,7 +477,7 @@ namespace nxdt::views this->DisplayNotification("options_tab/notifications/is_nso"_i18n); return; } else - if (!this->status_info_task->IsInternetConnectionAvailable()) + if (!this->root_view->IsInternetConnectionAvailable()) { /* Display a notification if no Internet connection is available. */ this->DisplayNotification("options_tab/notifications/no_internet_connection"_i18n); @@ -491,7 +491,7 @@ namespace nxdt::views } /* Display update frame. */ - brls::Application::pushView(new OptionsTabUpdateApplicationFrame(), brls::ViewAnimation::FADE, false); + brls::Application::pushView(new OptionsTabUpdateApplicationFrame(), brls::ViewAnimation::SLIDE_LEFT, false); }); this->addView(update_app); diff --git a/source/root_view.cpp b/source/root_view.cpp index 0fc8708..4b6e9f9 100644 --- a/source/root_view.cpp +++ b/source/root_view.cpp @@ -94,23 +94,23 @@ namespace nxdt::views this->usb_host_task = new nxdt::tasks::UsbHostTask(); /* Add tabs. */ - GameCardTab *gamecard_tab = new GameCardTab(this->gc_status_task); + GameCardTab *gamecard_tab = new GameCardTab(this); this->addTab("root_view/tabs/gamecard"_i18n, gamecard_tab); gamecard_tab->SetParentSidebarItem(static_cast(this->sidebar->getChild(this->sidebar->getViewsCount() - 1))); this->addSeparator(); - TitlesTab *user_titles_tab = new TitlesTab(this->title_task, false); + TitlesTab *user_titles_tab = new TitlesTab(this, false); this->addTab("root_view/tabs/user_titles"_i18n, user_titles_tab); user_titles_tab->SetParentSidebarItem(static_cast(this->sidebar->getChild(this->sidebar->getViewsCount() - 1))); - TitlesTab *system_titles_tab = new TitlesTab(this->title_task, true); + TitlesTab *system_titles_tab = new TitlesTab(this, true); this->addTab("root_view/tabs/system_titles"_i18n, system_titles_tab); system_titles_tab->SetParentSidebarItem(static_cast(this->sidebar->getChild(this->sidebar->getViewsCount() - 1))); this->addSeparator(); - this->addTab("root_view/tabs/options"_i18n, new OptionsTab(this->status_info_task)); + this->addTab("root_view/tabs/options"_i18n, new OptionsTab(this)); this->addSeparator(); @@ -154,6 +154,7 @@ namespace nxdt::views this->status_info_task->UnregisterListener(this->status_info_task_sub); /* Stop background tasks. */ + this->status_info_task->stop(); this->gc_status_task->stop(); this->title_task->stop(); this->ums_task->stop(); diff --git a/source/titles_tab.cpp b/source/titles_tab.cpp index 89f745c..bc0503b 100644 --- a/source/titles_tab.cpp +++ b/source/titles_tab.cpp @@ -75,15 +75,15 @@ namespace nxdt::views this->setValue(fmt::format("{:016X}", this->app_metadata->title_id), false, false); } - TitlesTab::TitlesTab(nxdt::tasks::TitleTask *title_task, bool is_system) : LayeredErrorFrame("titles_tab/no_titles_available"_i18n), title_task(title_task), is_system(is_system) + TitlesTab::TitlesTab(RootView *root_view, bool is_system) : LayeredErrorFrame("titles_tab/no_titles_available"_i18n), root_view(root_view), is_system(is_system) { /* Populate list. */ - this->PopulateList(this->title_task->GetApplicationMetadata(this->is_system)); + this->PopulateList(this->root_view->GetApplicationMetadata(this->is_system)); /* Subscribe to the title event if this is the user titles tab. */ if (!this->is_system) { - this->title_task_sub = this->title_task->RegisterListener([this](const nxdt::tasks::TitleApplicationMetadataVector* app_metadata) { + this->title_task_sub = this->root_view->RegisterTitleTaskListener([this](const nxdt::tasks::TitleApplicationMetadataVector* app_metadata) { /* Update list. */ this->PopulateList(app_metadata); }); @@ -93,7 +93,7 @@ namespace nxdt::views TitlesTab::~TitlesTab(void) { /* Unregister task listener if this is the user titles tab. */ - if (!this->is_system) this->title_task->UnregisterListener(this->title_task_sub); + if (!this->is_system) this->root_view->UnregisterTitleTaskListener(this->title_task_sub); } void TitlesTab::PopulateList(const nxdt::tasks::TitleApplicationMetadataVector* app_metadata) @@ -120,7 +120,7 @@ namespace nxdt::views TitlesTabItem *title = new TitlesTabItem(cur_app_metadata, this->is_system); /* Register click event. */ - title->getClickEvent()->subscribe([](brls::View *view){ + title->getClickEvent()->subscribe([](brls::View *view) { TitlesTabItem *item = static_cast(view); const TitleApplicationMetadata *app_metadata = item->GetApplicationMetadata(); bool is_system = item->IsSystemTitle();