From 1181a95d178bf1afd6bf82b7db249e77d1e8c73b Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sat, 20 Apr 2024 23:52:56 +0200 Subject: [PATCH] [ci skip] DumpOptionsFrame: add GetOutputFilePath method Other changes include: * DumpOptionsFrame: update contructors to also take a base output path string. * GameCardImageDumpOptionsFrame: simplify constructor by letting it take care of retrieving the title on its own. * nxdt_utils: remove utilsCreateOutputDirectories() function -- we'll be using utilsCreateDirectoryTree() anyway, so it's okay. --- include/core/nxdt_utils.h | 4 - include/defines.h | 12 +-- include/download_task.hpp | 2 +- include/dump_options_frame.hpp | 13 ++- include/gamecard_image_dump_options_frame.hpp | 2 +- source/core/nxdt_utils.c | 36 +------- source/dump_options_frame.cpp | 83 +++++++++++++------ source/gamecard_image_dump_options_frame.cpp | 15 +++- source/gamecard_tab.cpp | 2 +- 9 files changed, 83 insertions(+), 86 deletions(-) diff --git a/include/core/nxdt_utils.h b/include/core/nxdt_utils.h index 62f008f..dab2ecc 100644 --- a/include/core/nxdt_utils.h +++ b/include/core/nxdt_utils.h @@ -152,10 +152,6 @@ void utilsGenerateFormattedSizeString(double size, char *dst, size_t dst_size); /// Returns false if there's an error. bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_free); -/// Creates output directories in the specified device. -/// If 'device' is NULL, output directories will be created on the SD card. -void utilsCreateOutputDirectories(const char *device); - /// Returns true if a file exists. bool utilsCheckIfFileExists(const char *path); diff --git a/include/defines.h b/include/defines.h index 01b946f..6e6a7c1 100644 --- a/include/defines.h +++ b/include/defines.h @@ -81,12 +81,12 @@ #define HBMENU_BASE_PATH "/switch/" #define APP_BASE_PATH HBMENU_BASE_PATH APP_TITLE "/" -#define GAMECARD_PATH APP_BASE_PATH "Gamecard/" -#define HFS_PATH APP_BASE_PATH "HFS/" -#define NSP_PATH APP_BASE_PATH "NSP/" -#define TICKET_PATH APP_BASE_PATH "Ticket/" -#define NCA_PATH APP_BASE_PATH "NCA/" -#define NCA_FS_PATH APP_BASE_PATH "NCA FS/" +#define GAMECARD_SUBDIR "Gamecard" +#define HFS_SUBDIR "HFS" +#define NSP_SUBDIR "NSP" +#define TICKET_SUBDIR "Ticket" +#define NCA_SUBDIR "NCA" +#define NCA_FS_SUBDIR "NCA FS" #define CONFIG_FILE_NAME APP_TITLE "_config.json" #define DEFAULT_CONFIG_PATH "romfs:/default_config.json" diff --git a/include/download_task.hpp b/include/download_task.hpp index ef49dcc..2aef901 100644 --- a/include/download_task.hpp +++ b/include/download_task.hpp @@ -80,7 +80,7 @@ namespace nxdt::tasks /* Runs in the background thread. */ bool doInBackground(const std::string& path, const std::string& url, const bool& force_https) override final { - /* If the process fails or if it's cancelled, httpDownloadFile() will take care of closing the incomplete output file and delete it. */ + /* If the process fails or if it's cancelled, httpDownloadFile() will take care of closing the incomplete output file and deleting it. */ return httpDownloadFile(path.c_str(), url.c_str(), force_https, DownloadFileTask::HttpProgressCallback, this); } diff --git a/include/dump_options_frame.hpp b/include/dump_options_frame.hpp index 0c0edf7..ec0941d 100644 --- a/include/dump_options_frame.hpp +++ b/include/dump_options_frame.hpp @@ -34,7 +34,7 @@ namespace nxdt::views { private: RootView *root_view = nullptr; - std::string raw_filename{}, extension{}; + std::string storage_prefix{}, base_output_path{}, raw_filename{}, extension{}; brls::List *list = nullptr; brls::InputListItem *filename = nullptr; @@ -45,24 +45,23 @@ namespace nxdt::views bool finalized = false; - void Initialize(std::string& title, brls::Image *icon); + void Initialize(const std::string& title, brls::Image *icon); std::string SanitizeUserFileName(void); void UpdateOutputStorages(const nxdt::tasks::UmsDeviceVector& ums_devices); + void UpdateStoragePrefix(u32 selected); protected: - DumpOptionsFrame(RootView *root_view, std::string& title, std::string& raw_filename, std::string extension); - DumpOptionsFrame(RootView *root_view, std::string& title, brls::Image *icon, std::string& raw_filename, std::string extension); + DumpOptionsFrame(RootView *root_view, const std::string& title, const std::string& base_output_path, const std::string& raw_filename, const std::string& extension); + DumpOptionsFrame(RootView *root_view, const std::string& title, brls::Image *icon, const std::string& base_output_path, const std::string& raw_filename, const std::string& extension); ~DumpOptionsFrame(); bool onCancel(void) override final; void addView(brls::View *view, bool fill = false); - std::string GetFileName(void); - - std::string GetOutputStoragePrefix(void); + const std::string GetOutputFilePath(void); ALWAYS_INLINE brls::GenericEvent::Subscription RegisterButtonListener(brls::GenericEvent::Callback cb) { diff --git a/include/gamecard_image_dump_options_frame.hpp b/include/gamecard_image_dump_options_frame.hpp index a9b50f6..8ab4ab7 100644 --- a/include/gamecard_image_dump_options_frame.hpp +++ b/include/gamecard_image_dump_options_frame.hpp @@ -38,7 +38,7 @@ namespace nxdt::views brls::SelectListItem *checksum_lookup_method = nullptr; public: - GameCardImageDumpOptionsFrame(RootView *root_view, std::string title, std::string raw_filename); + GameCardImageDumpOptionsFrame(RootView *root_view, std::string raw_filename); }; } diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index 2e33fee..ce10398 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -87,19 +87,6 @@ static const u32 g_sizeSuffixesCount = MAX_ELEMENTS(g_sizeSuffixes); static const char g_illegalFileSystemChars[] = "\\/:*?\"<>|"; static const size_t g_illegalFileSystemCharsLength = (MAX_ELEMENTS(g_illegalFileSystemChars) - 1); -static const char *g_outputDirs[] = { - HBMENU_BASE_PATH, - APP_BASE_PATH, - GAMECARD_PATH, - HFS_PATH, - NSP_PATH, - TICKET_PATH, - NCA_PATH, - NCA_FS_PATH -}; - -static const size_t g_outputDirsCount = MAX_ELEMENTS(g_outputDirs); - static bool g_appUpdated = false; static const SplConfigItem SplConfigItem_ExosphereApiVersion = (SplConfigItem)65000; @@ -205,10 +192,6 @@ bool utilsInitializeResources(void) LOG_MSG_INFO("Running under %s %s unit in %s mode.", g_isDevUnit ? "development" : "retail", utilsIsMarikoUnit() ? "Mariko" : "Erista", utilsIsAppletMode() ? "applet" : "title override"); - /* Create output directories (SD card only). */ - /* TODO: remove the APP_TITLE check whenever we're ready for a release. */ - if (!strcasecmp(APP_TITLE, "nxdumptool")) utilsCreateOutputDirectories(NULL); - if (g_appLaunchPath) { LOG_MSG_INFO("Launch path: \"%s\".", g_appLaunchPath); @@ -217,6 +200,7 @@ bool utilsInitializeResources(void) /* TODO: uncomment this block whenever we are ready for a release. */ /*if (strcmp(g_appLaunchPath, NRO_PATH) != 0) { + utilsCreateDirectoryTree(NRO_PATH, false); remove(NRO_PATH); rename(g_appLaunchPath, NRO_PATH); @@ -786,24 +770,6 @@ bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_fr return true; } -void utilsCreateOutputDirectories(const char *device) -{ - size_t device_len = 0; - char path[FS_MAX_PATH] = {0}; - - if (device && (!(device_len = strlen(device)) || device[device_len - 1] != ':')) - { - LOG_MSG_ERROR("Invalid parameters!"); - return; - } - - for(size_t i = 0; i < g_outputDirsCount; i++) - { - sprintf(path, "%s%s", (device ? device : DEVOPTAB_SDMC_DEVICE), g_outputDirs[i]); - mkdir(path, 0744); - } -} - bool utilsCheckIfFileExists(const char *path) { if (!path || !*path) return false; diff --git a/source/dump_options_frame.cpp b/source/dump_options_frame.cpp index b573c9c..01583eb 100644 --- a/source/dump_options_frame.cpp +++ b/source/dump_options_frame.cpp @@ -26,7 +26,8 @@ using namespace i18n::literals; /* For _i18n. */ namespace nxdt::views { - DumpOptionsFrame::DumpOptionsFrame(RootView *root_view, std::string& title, std::string& raw_filename, std::string extension) : brls::ThumbnailFrame(), root_view(root_view), raw_filename(raw_filename), extension(extension) + DumpOptionsFrame::DumpOptionsFrame(RootView *root_view, const std::string& title, const std::string& base_output_path, const std::string& raw_filename, const std::string& extension) : + brls::ThumbnailFrame(), root_view(root_view), base_output_path(base_output_path), raw_filename(raw_filename), extension(extension) { /* Generate icon using the default image. */ brls::Image *icon = new brls::Image(); @@ -37,7 +38,8 @@ namespace nxdt::views this->Initialize(title, icon); } - DumpOptionsFrame::DumpOptionsFrame(RootView *root_view, std::string& title, brls::Image *icon, std::string& raw_filename, std::string extension) : brls::ThumbnailFrame(), root_view(root_view), raw_filename(raw_filename), extension(extension) + DumpOptionsFrame::DumpOptionsFrame(RootView *root_view, const std::string& title, brls::Image *icon, const std::string& base_output_path, const std::string& raw_filename, const std::string& extension) : + brls::ThumbnailFrame(), root_view(root_view), base_output_path(base_output_path), raw_filename(raw_filename), extension(extension) { /* Initialize the rest of the elements. */ this->Initialize(title, icon); @@ -52,7 +54,7 @@ namespace nxdt::views this->root_view->UnregisterUmsTaskListener(this->ums_task_sub); } - void DumpOptionsFrame::Initialize(std::string& title, brls::Image *icon) + void DumpOptionsFrame::Initialize(const std::string& title, brls::Image *icon) { /* Set UI properties. */ this->setTitle(title); @@ -86,11 +88,17 @@ namespace nxdt::views /* Sanitize output filename for the selected storage. */ this->filename->setValue(this->SanitizeUserFileName()); + + /* Update the storage prefix. */ + this->UpdateStoragePrefix(static_cast(selected)); }); - /* Manually update output storages vector. */ + /* Manually update the output storages vector. */ this->UpdateOutputStorages(this->root_view->GetUmsDevices()); + /* Manually update the storage prefix. */ + this->UpdateStoragePrefix(this->output_storage->getSelectedValue()); + this->list->addView(this->output_storage); /* Subscribe to the UMS device event. */ @@ -180,6 +188,25 @@ namespace nxdt::views } } + void DumpOptionsFrame::UpdateStoragePrefix(u32 selected) + { + switch(selected) + { + case ConfigOutputStorage_SdCard: + this->storage_prefix = DEVOPTAB_SDMC_DEVICE "/"; + break; + case ConfigOutputStorage_UsbHost: + this->storage_prefix = "/"; + break; + default: + { + const nxdt::tasks::UmsDeviceVector& ums_devices = this->root_view->GetUmsDevices(); + this->storage_prefix = std::string(ums_devices.at(selected - ConfigOutputStorage_Count).first->name); + break; + } + } + } + bool DumpOptionsFrame::onCancel(void) { /* Pop view. */ @@ -192,33 +219,35 @@ namespace nxdt::views this->list->addView(view, fill); } - std::string DumpOptionsFrame::GetFileName(void) + const std::string DumpOptionsFrame::GetOutputFilePath(void) { - return this->filename->getValue(); - } + std::string output = this->storage_prefix; + u32 selected = this->output_storage->getSelectedValue(); + char *sanitized_path = nullptr; - std::string DumpOptionsFrame::GetOutputStoragePrefix(void) - { - std::string prefix{}; - - u8 selected = static_cast(this->output_storage ? this->output_storage->getSelectedValue() : configGetInteger("output_storage")); - - switch(selected) + if (selected == ConfigOutputStorage_SdCard || selected >= ConfigOutputStorage_Count) { - case ConfigOutputStorage_SdCard: - prefix = DEVOPTAB_SDMC_DEVICE "/"; - break; - case ConfigOutputStorage_UsbHost: - prefix = "/"; - break; - default: - { - const nxdt::tasks::UmsDeviceVector& ums_devices = this->root_view->GetUmsDevices(); - prefix = std::string(ums_devices.at(selected - ConfigOutputStorage_Count).first->name); - break; - } + /* Remove the trailing path separator (if available) and append the application's base path if we're dealing with an SD card or a UMS device. */ + if (output.back() == '/') output.pop_back(); + output += APP_BASE_PATH; } - return prefix; + /* Append a path separator, if needed. */ + if (output.back() != '/' && this->base_output_path.front() != '/') output.push_back('/'); + + /* Append the base output path string. */ + output += this->base_output_path; + + /* Generate the sanitized file path. */ + sanitized_path = utilsGeneratePath(output.c_str(), this->filename->getValue().c_str(), this->extension.c_str()); + if (!sanitized_path) throw fmt::format("Failed to generate sanitized file path."); + + /* Update output. */ + output = std::string(sanitized_path); + + /* Free sanitized path. */ + free(sanitized_path); + + return output; } } diff --git a/source/gamecard_image_dump_options_frame.cpp b/source/gamecard_image_dump_options_frame.cpp index d13f548..ec7ed2b 100644 --- a/source/gamecard_image_dump_options_frame.cpp +++ b/source/gamecard_image_dump_options_frame.cpp @@ -26,7 +26,8 @@ using namespace i18n::literals; /* For _i18n. */ namespace nxdt::views { - GameCardImageDumpOptionsFrame::GameCardImageDumpOptionsFrame(RootView *root_view, std::string title, std::string raw_filename) : DumpOptionsFrame(root_view, title, raw_filename, ".xci") + GameCardImageDumpOptionsFrame::GameCardImageDumpOptionsFrame(RootView *root_view, std::string raw_filename) : + DumpOptionsFrame(root_view, "gamecard_tab/list/dump_card_image/label"_i18n, std::string(GAMECARD_SUBDIR), raw_filename, std::string(".xci")) { /* Prepend KeyArea data. */ this->prepend_key_area = new brls::ToggleListItem("dump_options/prepend_key_area/label"_i18n, configGetBoolean("gamecard/prepend_key_area"), "dump_options/prepend_key_area/description"_i18n, @@ -116,10 +117,11 @@ namespace nxdt::views /* Register dump button callback. */ this->RegisterButtonListener([this](brls::View *view) { /* Retrieve configuration values set by the user. */ - //bool prepend_key_area = this->prepend_key_area_item->getToggleState(); - //bool keep_certificate = this->keep_certificate_item->getToggleState(); + //bool prepend_key_area_val = this->prepend_key_area->getToggleState(); + //bool keep_certificate_val = this->keep_certificate->getToggleState(); bool trim_dump_val = this->trim_dump->getToggleState(); - //bool calculate_checksum = this->calculate_checksum_item->getToggleState(); + //bool calculate_checksum_val = this->calculate_checksum->getToggleState(); + //int checksum_lookup_method_val = static_cast(this->checksum_lookup_method->getSelectedValue()); /* Get gamecard size. */ u64 gc_size = 0; @@ -132,6 +134,11 @@ namespace nxdt::views /* Display update frame. */ //brls::Application::pushView(new OptionsTabUpdateApplicationFrame(), brls::ViewAnimation::SLIDE_LEFT, false); brls::Application::notify(fmt::format("0x{:X}", gc_size)); + + + + + LOG_MSG_DEBUG("Output file path: %s", this->GetOutputFilePath().c_str()); }); } } diff --git a/source/gamecard_tab.cpp b/source/gamecard_tab.cpp index afffbdd..99c2e8a 100644 --- a/source/gamecard_tab.cpp +++ b/source/gamecard_tab.cpp @@ -257,7 +257,7 @@ namespace nxdt::views dump_card_image->getClickEvent()->subscribe([this](brls::View *view) { std::string& raw_filename = (configGetInteger("naming_convention") == TitleNamingConvention_Full ? raw_filename_full : raw_filename_id_only); - brls::Application::pushView(new GameCardImageDumpOptionsFrame(this->root_view, "gamecard_tab/list/dump_card_image/label"_i18n, raw_filename), brls::ViewAnimation::SLIDE_LEFT); + brls::Application::pushView(new GameCardImageDumpOptionsFrame(this->root_view, raw_filename), brls::ViewAnimation::SLIDE_LEFT); }); this->list->addView(dump_card_image);