diff --git a/include/async_task.hpp b/include/async_task.hpp index 909e7d6..ab6b361 100644 --- a/include/async_task.hpp +++ b/include/async_task.hpp @@ -84,7 +84,7 @@ namespace nxdt::tasks /* Update status. */ this->m_status = AsyncTaskStatus::FINISHED; - /* Call appropiate post-execution function. */ + /* Run appropiate post-execution callback. */ if (this->isCancelled()) { this->onCancelled(this->m_result); @@ -193,7 +193,7 @@ namespace nxdt::tasks /* Update task status. */ this->m_status = AsyncTaskStatus::RUNNING; - /* Run onPreExecute() callback. */ + /* Run pre-execution callback. */ this->onPreExecute(); /* Start asynchronous task on a new thread. */ diff --git a/include/eta_progress_display.hpp b/include/data_transfer_progress_display.hpp similarity index 58% rename from include/eta_progress_display.hpp rename to include/data_transfer_progress_display.hpp index 388f77f..59564ed 100644 --- a/include/eta_progress_display.hpp +++ b/include/data_transfer_progress_display.hpp @@ -1,5 +1,5 @@ /* - * eta_progress_display.hpp + * data_transfer_progress_display.hpp * * Copyright (c) 2020-2024, DarkMatterCore . * @@ -21,24 +21,15 @@ #pragma once -#ifndef __ETA_PROGRESS_DISPLAY_HPP__ -#define __ETA_PROGRESS_DISPLAY_HPP__ +#ifndef __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__ +#define __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__ -#include +#include "data_transfer_task.hpp" namespace nxdt::views { - /* Used to hold progress info. */ - typedef struct { - size_t size; ///< Total process size. - size_t current; ///< Number of bytes processed thus far. - int percentage; ///< Progress percentage. - double speed; ///< Current speed expressed in bytes per second. - std::string eta; ///< Formatted ETA string. - } EtaProgressInfo; - - /* Used to display the progress of a running task. Shows a progress bar, a spinner, a percentage value, the process speed and an ETA value. */ - class EtaProgressDisplay: public brls::View + /* Used to display the progress of an ongoing data transfer task. Shows a progress bar, a spinner, a percentage value, the process speed and an ETA value. */ + class DataTransferProgressDisplay: public brls::View { private: brls::ProgressDisplay *progress_display = nullptr; @@ -47,22 +38,18 @@ namespace nxdt::views std::string GetFormattedSizeString(double size); protected: - /* Set class as non-copyable and non-moveable. */ - NON_COPYABLE(EtaProgressDisplay); - NON_MOVEABLE(EtaProgressDisplay); - void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override; void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override; public: - EtaProgressDisplay(void); - ~EtaProgressDisplay(void); + DataTransferProgressDisplay(void); + ~DataTransferProgressDisplay(void); - void setProgress(const EtaProgressInfo& progress); + void setProgress(const nxdt::tasks::DataTransferProgress& progress); void willAppear(bool resetState = false) override; void willDisappear(bool resetState = false) override; }; } -#endif /* __ETA_PROGRESS_DISPLAY_HPP__ */ +#endif /* __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__ */ diff --git a/include/data_transfer_task.hpp b/include/data_transfer_task.hpp new file mode 100644 index 0000000..018bdc3 --- /dev/null +++ b/include/data_transfer_task.hpp @@ -0,0 +1,233 @@ +/* + * data_transfer_task.hpp + * + * Copyright (c) 2020-2024, DarkMatterCore . + * + * 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 . + */ + +#pragma once + +#ifndef __DATA_TRANSFER_TASK_HPP__ +#define __DATA_TRANSFER_TASK_HPP__ + +#include + +#include "core/nxdt_utils.h" +#include "async_task.hpp" + +namespace nxdt::tasks +{ + /* Used to hold data transfer progress info. */ + typedef struct { + size_t total_size; ///< Total size for the data transfer process. + size_t xfer_size; ///< Number of bytes transferred thus far. + int percentage; ///< Progress percentage. + double speed; ///< Current speed expressed in bytes per second. + std::string eta; ///< Formatted ETA string. + } DataTransferProgress; + + /* Custom event type used to push data transfer progress updates. */ + typedef brls::Event DataTransferProgressEvent; + + /* Class template to asynchronously transfer data on a background thread. */ + /* Automatically allocates and registers a RepeatingTask on its own, which is started along with the actual task when AsyncTask::execute() is called. */ + /* This internal RepeatingTask is guaranteed to work on the UI thread, and it is also automatically unregistered on object destruction. */ + /* Progress updates are pushed through a DataTransferProgressEvent. Make sure to register all event listeners before executing the task. */ + template + class DataTransferTask: public AsyncTask + { + private: + /* Handles task progress updates on the calling thread. */ + class Handler: public brls::RepeatingTask + { + private: + bool finished = false; + DataTransferTask* task = nullptr; + + protected: + void run(retro_time_t current_time) override final + { + brls::RepeatingTask::run(current_time); + if (this->task && !this->finished) this->finished = this->task->loopCallback(); + } + + public: + Handler(retro_time_t interval, DataTransferTask* task) : brls::RepeatingTask(interval), task(task) { } + + ALWAYS_INLINE bool IsFinished(void) + { + return this->finished; + } + }; + + typedef std::chrono::time_point SteadyTimePoint; + static constexpr auto &CurrentSteadyTimePoint = std::chrono::steady_clock::now; + + DataTransferProgressEvent progress_event{}; + Handler *task_handler = nullptr; + + SteadyTimePoint start_time{}, prev_time{}, end_time{}; + size_t prev_xfer_size = 0; + + protected: + /* Set class as non-copyable and non-moveable. */ + NON_COPYABLE(DataTransferTask); + NON_MOVEABLE(DataTransferTask); + + /* Make the background function overridable. */ + virtual Result doInBackground(const Params&... params) override = 0; + + /* Runs on the calling thread. */ + void onCancelled(const Result& result) override final + { + NX_IGNORE_ARG(result); + + /* Set end time. */ + this->end_time = CurrentSteadyTimePoint(); + + /* Pause task handler. */ + this->task_handler->pause(); + + /* Unset long running process state. */ + utilsSetLongRunningProcessState(false); + } + + /* Runs on the calling thread. */ + void onPostExecute(const Result& result) override final + { + NX_IGNORE_ARG(result); + + /* Set end time. */ + this->end_time = CurrentSteadyTimePoint(); + + /* Fire task handler immediately to get the last result from AsyncTask::loopCallback(), then pause it. */ + this->task_handler->fireNow(); + this->task_handler->pause(); + + /* Update progress one last time. */ + this->onProgressUpdate(this->getProgress()); + + /* Unset long running process state. */ + utilsSetLongRunningProcessState(false); + } + + /* Runs on the calling thread. */ + void onPreExecute(void) override final + { + /* Set long running process state. */ + utilsSetLongRunningProcessState(true); + + /* Start task handler. */ + this->task_handler->start(); + + /* Set start time. */ + this->start_time = this->prev_time = CurrentSteadyTimePoint(); + } + + /* Runs on the calling thread. */ + void onProgressUpdate(const DataTransferProgress& progress) override final + { + AsyncTaskStatus status = this->getStatus(); + + /* Return immediately if there has been no progress at all, or if it the task has been cancelled. */ + bool proceed = (progress.xfer_size > prev_xfer_size || (progress.xfer_size == prev_xfer_size && (!progress.total_size || progress.xfer_size >= progress.total_size))); + if (!proceed || this->isCancelled()) return; + + /* Calculate time difference between the last progress update and the current one. */ + /* Return immediately if it's less than 1 second, but only if this isn't the last chunk; or if we don't know the total size and the task is still running . */ + SteadyTimePoint cur_time = std::chrono::steady_clock::now(); + double diff_time = std::chrono::duration(cur_time - this->prev_time).count(); + if (diff_time < 1.0 && ((progress.total_size && progress.xfer_size < progress.total_size) || status == AsyncTaskStatus::RUNNING)) return; + + /* Calculate transferred data size difference between the last progress update and the current one. */ + double diff_xfer_size = static_cast(progress.xfer_size - prev_xfer_size); + + /* Calculate transfer speed in bytes per second. */ + double speed = (diff_xfer_size / diff_time); + + /* Fill struct. */ + DataTransferProgress new_progress = progress; + new_progress.speed = speed; + + if (progress.total_size) + { + /* Calculate remaining data size and ETA if we know the total size. */ + double remaining = static_cast(progress.total_size - progress.xfer_size); + double eta = (remaining / speed); + new_progress.eta = fmt::format("{:02.0F}H{:02.0F}M{:02.0F}S", std::fmod(eta, 86400.0) / 3600.0, std::fmod(eta, 3600.0) / 60.0, std::fmod(eta, 60.0)); + } else { + /* No total size means no ETA calculation, sadly. */ + new_progress.eta = ""; + } + + /* Set total size if we don't know it and if this is the final chunk. */ + if (!new_progress.total_size && status == AsyncTaskStatus::FINISHED) + { + new_progress.total_size = new_progress.xfer_size; + new_progress.percentage = 100; + } + + /* Update class variables. */ + this->prev_time = cur_time; + this->prev_xfer_size = progress.xfer_size; + + /* Send updated progress to all listeners. */ + this->progress_event.fire(new_progress); + } + + public: + DataTransferTask(void) + { + /* Create task handler. */ + this->task_handler = new Handler(DATA_TRANSFER_TASK_INTERVAL, this); + } + + ~DataTransferTask(void) + { + /* Stop task handler. Borealis' task manager will take care of deleting it. */ + this->task_handler->stop(); + + /* Unregister all event listeners. */ + this->progress_event.unsubscribeAll(); + } + + /* Returns the last result from AsyncTask::loopCallback(). Runs on the calling thread. */ + ALWAYS_INLINE bool IsFinished(void) + { + return this->task_handler->IsFinished(); + } + + /* Returns the task duration expressed in seconds. */ + /* If the task hasn't finished yet, it returns the number of seconds that have passed since the task was started. */ + ALWAYS_INLINE double GetDuration(void) + { + return std::chrono::duration(this->IsFinished() ? (this->end_time - this->start_time) : (CurrentSteadyTimePoint() - this->start_time)).count(); + } + + ALWAYS_INLINE DataTransferProgressEvent::Subscription RegisterListener(DataTransferProgressEvent::Callback cb) + { + return this->progress_event.subscribe(cb); + } + + ALWAYS_INLINE void UnregisterListener(DataTransferProgressEvent::Subscription subscription) + { + this->progress_event.unsubscribe(subscription); + } + }; +} + +#endif /* __DATA_TRANSFER_TASK_HPP__ */ diff --git a/include/defines.h b/include/defines.h index f8b07f3..01b946f 100644 --- a/include/defines.h +++ b/include/defines.h @@ -104,7 +104,13 @@ #define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:" -#define DOWNLOAD_TASK_INTERVAL 100 /* 100 milliseconds. */ +/// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. +/// Reference: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits. +/// Most modern filesystems use a 255-byte limit instead of 255-character/codepoint limit, so that's what we're gonna use. +#define FS_MAX_FILENAME_LENGTH 255 +#define SDMC_MAX_FILENAME_LENGTH 128 /* Arbitrarily set, I'm tired of FS sysmodule shenanigans. */ + +#define DATA_TRANSFER_TASK_INTERVAL 100 /* 100 milliseconds. */ #define HTTP_USER_AGENT APP_TITLE "/" APP_VERSION " (Nintendo Switch)" #define HTTP_CONNECT_TIMEOUT 10L /* 10 seconds. */ diff --git a/include/download_task.hpp b/include/download_task.hpp index 0d4ad02..78a1e03 100644 --- a/include/download_task.hpp +++ b/include/download_task.hpp @@ -24,254 +24,83 @@ #ifndef __DOWNLOAD_TASK_HPP__ #define __DOWNLOAD_TASK_HPP__ -#include - -#include "core/nxdt_utils.h" -#include "async_task.hpp" -#include "eta_progress_display.hpp" +#include "data_transfer_task.hpp" namespace nxdt::tasks { - /* Custom event type used to push download progress updates. */ - typedef brls::Event DownloadProgressEvent; - /* Used to hold a buffer + size pair with downloaded data. */ typedef std::pair DownloadDataResult; /* Class template to asynchronously download data on a background thread. */ - /* Automatically allocates and registers a RepeatingTask on its own, which is started along with the actual task when execute() is called. */ - /* This internal RepeatingTask is guaranteed to work on the UI thread, and it is also automatically unregistered on object destruction. */ - /* Progress updates are pushed through a DownloadProgressEvent. Make sure to register all event listeners before executing the task. */ + /* Uses both AsyncTask and DataTransferTask class templates. */ template - class DownloadTask: public AsyncTask + class DownloadTask: public DataTransferTask { - public: - /* Handles task progress updates on the calling thread. */ - class DownloadTaskHandler: public brls::RepeatingTask - { - private: - bool finished = false; - DownloadTask* task = nullptr; - - protected: - void run(retro_time_t current_time) override final; - - public: - DownloadTaskHandler(retro_time_t interval, DownloadTask* task); - - ALWAYS_INLINE bool isFinished(void) - { - return this->finished; - } - }; - - private: - DownloadProgressEvent progress_event; - DownloadTaskHandler *task_handler = nullptr; - std::chrono::time_point start_time{}, prev_time{}; - size_t prev_current = 0; - protected: - /* Make the background function overridable. */ - virtual Result doInBackground(const Params&... params) override = 0; - - /* These functions run on the calling thread. */ - void onCancelled(const Result& result) override final; - void onPostExecute(const Result& result) override final; - void onPreExecute(void) override final; - void onProgressUpdate(const nxdt::views::EtaProgressInfo& progress) override final; + /* Set class as non-copyable and non-moveable. */ + NON_COPYABLE(DownloadTask); + NON_MOVEABLE(DownloadTask); public: - DownloadTask(void); - ~DownloadTask(void); + DownloadTask(void) = default; - /* Runs on the asynchronous task thread. Required by CURL. */ + /* Runs on the asynchronous task thread. Required by cURL. */ /* Make sure to pass it to either httpDownloadFile() or httpDownloadData() with 'this' as the user pointer. */ - static int HttpProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); - - /* Returns the last result from loopCallback(). Runs on the calling thread. */ - ALWAYS_INLINE bool isFinished(void) + static int HttpProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - return this->task_handler->isFinished(); - } + NX_IGNORE_ARG(ultotal); + NX_IGNORE_ARG(ulnow); - ALWAYS_INLINE DownloadProgressEvent::Subscription RegisterListener(DownloadProgressEvent::Callback cb) - { - return this->progress_event.subscribe(cb); - } + DataTransferProgress progress{}; + DownloadTask* task = static_cast*>(clientp); - ALWAYS_INLINE void UnregisterListener(DownloadProgressEvent::Subscription subscription) - { - this->progress_event.unsubscribe(subscription); + /* Don't proceed if we're dealing with an invalid task pointer, or if the task has been cancelled. */ + if (!task || task->isCancelled()) return 1; + + /* Fill struct. */ + progress.total_size = static_cast(dltotal); + progress.xfer_size = static_cast(dlnow); + progress.percentage = (progress.total_size ? static_cast((progress.xfer_size * 100) / progress.total_size) : 0); + + /* Push progress onto the class. */ + task->publishProgress(progress); + + return 0; } }; - template - DownloadTask::DownloadTaskHandler::DownloadTaskHandler(retro_time_t interval, DownloadTask* task) : brls::RepeatingTask(interval), task(task) - { - /* Do nothing. */ - } - - template - void DownloadTask::DownloadTaskHandler::run(retro_time_t current_time) - { - brls::RepeatingTask::run(current_time); - if (this->task && !this->finished) this->finished = this->task->loopCallback(); - } - - template - DownloadTask::DownloadTask(void) - { - /* Create task handler. */ - this->task_handler = new DownloadTaskHandler(DOWNLOAD_TASK_INTERVAL, this); - } - - template - DownloadTask::~DownloadTask(void) - { - /* Stop task handler. Borealis' task manager will take care of deleting it. */ - this->task_handler->stop(); - - /* Unregister all event listeners. */ - this->progress_event.unsubscribeAll(); - } - - template - int DownloadTask::HttpProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) - { - NX_IGNORE_ARG(ultotal); - NX_IGNORE_ARG(ulnow); - - nxdt::views::EtaProgressInfo progress = {0}; - DownloadTask* task = static_cast*>(clientp); - - /* Don't proceed if we're dealing with an invalid task pointer, or if the task has been cancelled. */ - if (!task || task->isCancelled()) return 1; - - /* Fill struct. */ - progress.size = static_cast(dltotal); - progress.current = static_cast(dlnow); - progress.percentage = (progress.size ? static_cast((progress.current * 100) / progress.size) : 0); - - /* Push progress onto the class. */ - task->publishProgress(progress); - - return 0; - } - - template - void DownloadTask::onCancelled(const Result& result) - { - NX_IGNORE_ARG(result); - - /* Pause task handler. */ - this->task_handler->pause(); - - /* Unset long running process state. */ - utilsSetLongRunningProcessState(false); - } - - template - void DownloadTask::onPostExecute(const Result& result) - { - NX_IGNORE_ARG(result); - - /* Fire task handler immediately to get the last result from loopCallback(), then pause it. */ - this->task_handler->fireNow(); - this->task_handler->pause(); - - /* Update progress one last time. */ - this->onProgressUpdate(this->getProgress()); - - /* Unset long running process state. */ - utilsSetLongRunningProcessState(false); - } - - template - void DownloadTask::onPreExecute(void) - { - /* Set long running process state. */ - utilsSetLongRunningProcessState(true); - - /* Start task handler. */ - this->task_handler->start(); - - /* Set start time. */ - this->start_time = this->prev_time = std::chrono::steady_clock::now(); - } - - template - void DownloadTask::onProgressUpdate(const nxdt::views::EtaProgressInfo& progress) - { - AsyncTaskStatus status = this->getStatus(); - - /* Return immediately if there has been no progress at all, or if it the task has been cancelled. */ - bool proceed = (progress.current > prev_current || (progress.current == prev_current && (!progress.size || progress.current >= progress.size))); - if (!proceed || this->isCancelled()) return; - - /* Calculate time difference between the last progress update and the current one. */ - /* Return immediately if it's less than 1 second, but only if this isn't the last chunk (or if the task is still running, if we don't know the download size). */ - std::chrono::time_point cur_time = std::chrono::steady_clock::now(); - std::chrono::duration diff_time = (cur_time - this->prev_time); - - double diff_time_conv = diff_time.count(); - if (diff_time_conv < 1.0 && ((progress.size && progress.current < progress.size) || status == AsyncTaskStatus::RUNNING)) return; - - /* Calculate transferred data size difference between the last progress update and the current one. */ - double diff_current = static_cast(progress.current - prev_current); - - /* Calculate download speed in bytes per second. */ - double speed = (diff_current / diff_time_conv); - - /* Fill struct. */ - nxdt::views::EtaProgressInfo new_progress = progress; - new_progress.speed = speed; - - if (progress.size) - { - /* Calculate remaining data size and ETA if we know the download size. */ - double remaining = static_cast(progress.size - progress.current); - double eta = (remaining / speed); - new_progress.eta = fmt::format("{:02.0F}H{:02.0F}M{:02.0F}S", std::fmod(eta, 86400.0) / 3600.0, std::fmod(eta, 3600.0) / 60.0, std::fmod(eta, 60.0)); - } else { - /* No download size means no ETA calculation, sadly. */ - new_progress.eta = ""; - } - - /* Set download size if we don't know it and if this is the final chunk. */ - if (!new_progress.size && status == AsyncTaskStatus::FINISHED) - { - new_progress.size = new_progress.current; - new_progress.percentage = 100; - } - - /* Update class variables. */ - this->prev_time = cur_time; - this->prev_current = progress.current; - - /* Send updated progress to all listeners. */ - this->progress_event.fire(new_progress); - } - /* Asynchronous task to download a file using an output path and a URL. */ class DownloadFileTask: public DownloadTask { protected: + /* Set class as non-copyable and non-moveable. */ + NON_COPYABLE(DownloadFileTask); + NON_MOVEABLE(DownloadFileTask); + /* 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. */ return httpDownloadFile(path.c_str(), url.c_str(), force_https, DownloadFileTask::HttpProgressCallback, this); } + + public: + DownloadFileTask(void) = default; }; /* Asynchronous task to store downloaded data into a dynamically allocated buffer using a URL. */ + /* The buffer returned by std::pair::first() must be manually freed by the calling function using free(). */ class DownloadDataTask: public DownloadTask { protected: + /* Set class as non-copyable and non-moveable. */ + NON_COPYABLE(DownloadDataTask); + NON_MOVEABLE(DownloadDataTask); + + /* Runs in the background thread. */ DownloadDataResult doInBackground(const std::string& url, const bool& force_https) override final { - char *buf = NULL; + char *buf = nullptr; size_t buf_size = 0; /* If the process fails or if it's cancelled, httpDownloadData() will take care of freeing up the allocated memory and return NULL. */ @@ -279,6 +108,9 @@ namespace nxdt::tasks return std::make_pair(buf, buf_size); } + + public: + DownloadDataTask(void) = default; }; } diff --git a/include/dump_options_frame.hpp b/include/dump_options_frame.hpp index 2665867..c988b06 100644 --- a/include/dump_options_frame.hpp +++ b/include/dump_options_frame.hpp @@ -43,26 +43,25 @@ namespace nxdt::views RootView *root_view = nullptr; nxdt::tasks::UmsEvent::Subscription ums_task_sub; - char *raw_filename = NULL; - const char *extension = NULL; + std::string raw_filename = ""; + std::string extension = ""; brls::List *list = nullptr; - brls::InputListItem *filename_input = nullptr; - brls::SelectListItem *output_storage = nullptr; - brls::ToggleListItem *prepend_key_area = nullptr; - brls::ToggleListItem *keep_certificate = nullptr; - brls::ToggleListItem *trim_dump = nullptr; - brls::ToggleListItem *calculate_checksum = nullptr; - brls::SelectListItem *checksum_lookup_method = nullptr; + brls::InputListItem *filename_item = nullptr; + brls::SelectListItem *output_storage_item = nullptr; + brls::ToggleListItem *prepend_key_area_item = nullptr; + brls::ToggleListItem *keep_certificate_item = nullptr; + brls::ToggleListItem *trim_dump_item = nullptr; + brls::ToggleListItem *calculate_checksum_item = nullptr; + brls::SelectListItem *checksum_lookup_method_item = nullptr; - std::string RegenerateFileName(void) + std::string SanitizeFileName(void) { - if (!this->raw_filename) return "dummy"; + char *raw_filename_dup = nullptr; - char *raw_filename_dup = strdup(this->raw_filename); - if (!raw_filename_dup) return "dummy"; + if (raw_filename.empty() || !(raw_filename_dup = strdup(this->raw_filename.c_str()))) return "dummy"; - u8 selected = static_cast(this->output_storage ? this->output_storage->getSelectedValue() : configGetInteger("output_storage")); + u8 selected = static_cast(this->output_storage_item ? this->output_storage_item->getSelectedValue() : configGetInteger("output_storage")); utilsReplaceIllegalCharacters(raw_filename_dup, selected == ConfigOutputStorage_SdCard); std::string output = std::string(raw_filename_dup); @@ -71,37 +70,55 @@ namespace nxdt::views return output; } - void UpdateRawFileName(void) - { - if (this->raw_filename) free(this->raw_filename); - this->raw_filename = strdup(this->filename_input->getValue().c_str()); - } - void UpdateStorages(const nxdt::tasks::UmsDeviceVector* ums_devices) { - if (!this->output_storage) return; + if (!this->output_storage_item) return; - std::vector *storages = this->output_storage->getValues(); + std::vector *storages = this->output_storage_item->getValues(); storages->clear(); - storages->push_back("dump_options/output_storage/value_00"_i18n); - storages->push_back("dump_options/output_storage/value_01"_i18n); + size_t elem_count = (ConfigOutputStorage_Count + ums_devices->size()); + u32 selected = this->output_storage_item->getSelectedValue(); - for(const UsbHsFsDevice& cur_ums_device : *ums_devices) + for(size_t i = 0; i < elem_count; i++) { - std::string device_str = (std::string(cur_ums_device.name) + ", "); - if (cur_ums_device.product_name[0]) device_str += (std::string(cur_ums_device.product_name) + ", "); - device_str += fmt::format("LUN {}, FS #{}, {}", cur_ums_device.lun, cur_ums_device.fs_idx, LIBUSBHSFS_FS_TYPE_STR(cur_ums_device.fs_type)); - storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_02", device_str)); + if (i == 1) + { + storages->push_back("dump_options/output_storage/value_01"_i18n); + continue; + } + + u64 total_sz = 0, free_sz = 0; + char total_sz_str[64] = {0}, free_sz_str[64] = {0}; + + const UsbHsFsDevice *cur_ums_device = (i >= ConfigOutputStorage_Count ? (ums_devices->data() + (i - ConfigOutputStorage_Count)) : nullptr); + + sprintf(total_sz_str, "%s/", cur_ums_device ? cur_ums_device->name : DEVOPTAB_SDMC_DEVICE); + utilsGetFileSystemStatsByPath(total_sz_str, &total_sz, &free_sz); + utilsGenerateFormattedSizeString(total_sz, total_sz_str, sizeof(total_sz_str)); + utilsGenerateFormattedSizeString(free_sz, free_sz_str, sizeof(free_sz_str)); + + if (cur_ums_device) + { + std::string ums_extra_info = (cur_ums_device->product_name[0] ? (std::string(cur_ums_device->product_name) + ", ") : ""); + ums_extra_info += fmt::format("LUN {}, FS #{}, {}", cur_ums_device->lun, cur_ums_device->fs_idx, LIBUSBHSFS_FS_TYPE_STR(cur_ums_device->fs_type)); + storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_02", static_cast(strlen(cur_ums_device->name + 3) - 1), cur_ums_device->name + 3, free_sz_str, total_sz_str, ums_extra_info)); + } else { + storages->push_back(brls::i18n::getStr("dump_options/output_storage/value_00", free_sz_str, total_sz_str)); + } } - if (this->output_storage->getSelectedValue() > ConfigOutputStorage_UsbHost) + if (selected > ConfigOutputStorage_UsbHost) { /* Set the SD card as the current output storage. */ - this->output_storage->setSelectedValue(ConfigOutputStorage_SdCard); + this->output_storage_item->setSelectedValue(ConfigOutputStorage_SdCard); - /* Regenerate filename. */ - this->output_storage->getValueSelectedEvent()->fire(ConfigOutputStorage_SdCard); + /* Manually trigger selection event. */ + /* This will take care of both updating the JSON configuration and saniziting the filename provided by the user. */ + this->output_storage_item->getValueSelectedEvent()->fire(ConfigOutputStorage_SdCard); + } else { + /* Set the current output storage once more. This will make sure the device string gets updated. */ + this->output_storage_item->setSelectedValue(selected); } } @@ -115,7 +132,7 @@ namespace nxdt::views } public: - DumpOptionsFrame(RootView *root_view, std::string title, brls::Image *icon, char *raw_filename, const char *extension) : brls::ThumbnailFrame(), root_view(root_view), raw_filename(raw_filename), extension(extension) + 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) { /* Set UI properties. */ this->setTitle(title); @@ -132,14 +149,14 @@ namespace nxdt::views - this->filename_input = new brls::InputListItem("dump_options/filename/label"_i18n, this->RegenerateFileName(), "", "dump_options/filename/description"_i18n, 255); + this->filename_item = new brls::InputListItem("dump_options/filename/label"_i18n, this->SanitizeFileName(), "", "dump_options/filename/description"_i18n, FS_MAX_FILENAME_LENGTH); - this->filename_input->getClickEvent()->subscribe([this](brls::View *view) { - this->UpdateRawFileName(); - this->filename_input->setValue(this->RegenerateFileName()); + this->filename_item->getClickEvent()->subscribe([this](brls::View *view) { + this->raw_filename = this->filename_item->getValue(); + this->filename_item->setValue(this->SanitizeFileName()); }); - this->list->addView(this->filename_input); + this->list->addView(this->filename_item); @@ -148,28 +165,25 @@ namespace nxdt::views - this->output_storage = new brls::SelectListItem("dump_options/output_storage/label"_i18n, { - "dump_options/output_storage/value_00"_i18n, - "dump_options/output_storage/value_01"_i18n - }, configGetInteger("output_storage"), - brls::i18n::getStr("dump_options/output_storage/description", GITHUB_REPOSITORY_URL)); + this->output_storage_item = new brls::SelectListItem("dump_options/output_storage/label"_i18n, { "dummy0", "dummy1" }, configGetInteger("output_storage"), + brls::i18n::getStr("dump_options/output_storage/description", GITHUB_REPOSITORY_URL)); /* Subscribe to SelectListItem's value selected event. */ - this->output_storage->getValueSelectedEvent()->subscribe([this](int selected) { + this->output_storage_item->getValueSelectedEvent()->subscribe([this](int selected) { /* Make sure the current value isn't out of bounds. */ if (selected < ConfigOutputStorage_SdCard || selected >= static_cast(this->root_view->GetUmsDevices()->size() + ConfigOutputStorage_Count)) return; /* Update configuration. */ if (selected == ConfigOutputStorage_SdCard || selected == ConfigOutputStorage_UsbHost) configSetInteger("output_storage", selected); - /* Update output filename. */ - this->filename_input->setValue(this->RegenerateFileName()); + /* Sanitize output filename for the selected storage. */ + this->filename_item->setValue(this->SanitizeFileName()); }); - /* Update output storages vector. */ + /* Manually update output storages vector. */ this->UpdateStorages(this->root_view->GetUmsDevices()); - this->list->addView(this->output_storage); + this->list->addView(this->output_storage_item); @@ -190,11 +204,11 @@ namespace nxdt::views - 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, "generic/value_enabled"_i18n, \ - "generic/value_disabled"_i18n); + this->prepend_key_area_item = new brls::ToggleListItem("dump_options/prepend_key_area/label"_i18n, configGetBoolean("gamecard/prepend_key_area"), + "dump_options/prepend_key_area/description"_i18n, "generic/value_enabled"_i18n, + "generic/value_disabled"_i18n); - this->prepend_key_area->getClickEvent()->subscribe([](brls::View* view) { + this->prepend_key_area_item->getClickEvent()->subscribe([](brls::View* view) { /* Get current value. */ brls::ToggleListItem *item = static_cast(view); bool value = item->getToggleState(); @@ -205,7 +219,7 @@ namespace nxdt::views LOG_MSG_DEBUG("Prepend Key Area setting changed by user."); }); - this->list->addView(this->prepend_key_area); + this->list->addView(this->prepend_key_area_item); @@ -213,11 +227,11 @@ namespace nxdt::views - this->keep_certificate = new brls::ToggleListItem("dump_options/keep_certificate/label"_i18n, configGetBoolean("gamecard/keep_certificate"), \ - "dump_options/keep_certificate/description"_i18n, "generic/value_enabled"_i18n, \ - "generic/value_disabled"_i18n); + this->keep_certificate_item = new brls::ToggleListItem("dump_options/keep_certificate/label"_i18n, configGetBoolean("gamecard/keep_certificate"), + "dump_options/keep_certificate/description"_i18n, "generic/value_enabled"_i18n, + "generic/value_disabled"_i18n); - this->keep_certificate->getClickEvent()->subscribe([](brls::View* view) { + this->keep_certificate_item->getClickEvent()->subscribe([](brls::View* view) { /* Get current value. */ brls::ToggleListItem *item = static_cast(view); bool value = item->getToggleState(); @@ -228,7 +242,7 @@ namespace nxdt::views LOG_MSG_DEBUG("Keep certificate setting changed by user."); }); - this->list->addView(this->keep_certificate); + this->list->addView(this->keep_certificate_item); @@ -238,11 +252,11 @@ namespace nxdt::views - this->trim_dump = new brls::ToggleListItem("dump_options/trim_dump/label"_i18n, configGetBoolean("gamecard/trim_dump"), \ - "dump_options/trim_dump/description"_i18n, "generic/value_enabled"_i18n, \ - "generic/value_disabled"_i18n); + this->trim_dump_item = new brls::ToggleListItem("dump_options/trim_dump/label"_i18n, configGetBoolean("gamecard/trim_dump"), + "dump_options/trim_dump/description"_i18n, "generic/value_enabled"_i18n, + "generic/value_disabled"_i18n); - this->trim_dump->getClickEvent()->subscribe([](brls::View* view) { + this->trim_dump_item->getClickEvent()->subscribe([](brls::View* view) { /* Get current value. */ brls::ToggleListItem *item = static_cast(view); bool value = item->getToggleState(); @@ -253,7 +267,7 @@ namespace nxdt::views LOG_MSG_DEBUG("Trim dump setting changed by user."); }); - this->list->addView(this->trim_dump); + this->list->addView(this->trim_dump_item); @@ -263,11 +277,11 @@ namespace nxdt::views - this->calculate_checksum = new brls::ToggleListItem("dump_options/calculate_checksum/label"_i18n, configGetBoolean("gamecard/calculate_checksum"), \ - "dump_options/calculate_checksum/description"_i18n, "generic/value_enabled"_i18n, \ - "generic/value_disabled"_i18n); + this->calculate_checksum_item = new brls::ToggleListItem("dump_options/calculate_checksum/label"_i18n, configGetBoolean("gamecard/calculate_checksum"), + "dump_options/calculate_checksum/description"_i18n, "generic/value_enabled"_i18n, + "generic/value_disabled"_i18n); - this->calculate_checksum->getClickEvent()->subscribe([](brls::View* view) { + this->calculate_checksum_item->getClickEvent()->subscribe([](brls::View* view) { /* Get current value. */ brls::ToggleListItem *item = static_cast(view); bool value = item->getToggleState(); @@ -278,7 +292,7 @@ namespace nxdt::views LOG_MSG_DEBUG("Calculate checksum setting changed by user."); }); - this->list->addView(this->calculate_checksum); + this->list->addView(this->calculate_checksum_item); @@ -287,16 +301,16 @@ namespace nxdt::views - this->checksum_lookup_method = new brls::SelectListItem("dump_options/checksum_lookup_method/label"_i18n, { - "dump_options/checksum_lookup_method/value_00"_i18n, - "NSWDB", - "No-Intro" - }, configGetInteger("gamecard/checksum_lookup_method"), - brls::i18n::getStr("dump_options/checksum_lookup_method/description", - "dump_options/calculate_checksum/label"_i18n, "NSWDB", NSWDB_XML_NAME, "No-Intro")); + this->checksum_lookup_method_item = new brls::SelectListItem("dump_options/checksum_lookup_method/label"_i18n, { + "dump_options/checksum_lookup_method/value_00"_i18n, + "NSWDB", + "No-Intro" + }, configGetInteger("gamecard/checksum_lookup_method"), + brls::i18n::getStr("dump_options/checksum_lookup_method/description", + "dump_options/calculate_checksum/label"_i18n, "NSWDB", NSWDB_XML_NAME, "No-Intro")); /* Subscribe to SelectListItem's value selected event. */ - this->checksum_lookup_method->getValueSelectedEvent()->subscribe([this](int selected) { + this->checksum_lookup_method_item->getValueSelectedEvent()->subscribe([this](int selected) { /* Make sure the current value isn't out of bounds. */ if (selected < ConfigChecksumLookupMethod_None || selected >= ConfigChecksumLookupMethod_Count) return; @@ -304,7 +318,7 @@ namespace nxdt::views configSetInteger("gamecard/checksum_lookup_method", selected); }); - this->list->addView(this->checksum_lookup_method); + this->list->addView(this->checksum_lookup_method_item); @@ -314,8 +328,28 @@ namespace nxdt::views brls::Button *button = this->getSidebar()->getButton(); button->setLabel("dump_options/start_dump"_i18n); - button->getClickEvent()->subscribe([](brls::View *view) { - brls::Application::notify("test"); + button->getClickEvent()->subscribe([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 trim_dump = this->trim_dump_item->getToggleState(); + //bool calculate_checksum = this->calculate_checksum_item->getToggleState(); + + /* Get gamecard size. */ + u64 gc_size = 0; + if ((!trim_dump && !gamecardGetTotalSize(&gc_size)) || (trim_dump && !gamecardGetTrimmedSize(&gc_size)) || !gc_size) + { + brls::Application::notify("fail"); + } + + + + + + + /* Display update frame. */ + //brls::Application::pushView(new OptionsTabUpdateApplicationFrame(), brls::ViewAnimation::SLIDE_LEFT, false); + brls::Application::notify(fmt::format("0x{:X}", gc_size)); }); @@ -331,8 +365,6 @@ namespace nxdt::views { /* Unregister task listener. */ this->root_view->UnregisterUmsTaskListener(this->ums_task_sub); - - if (this->raw_filename) free(this->raw_filename); } }; } diff --git a/include/options_tab.hpp b/include/options_tab.hpp index 3094afa..e5c718e 100644 --- a/include/options_tab.hpp +++ b/include/options_tab.hpp @@ -24,10 +24,8 @@ #ifndef __OPTIONS_TAB_HPP__ #define __OPTIONS_TAB_HPP__ -#include - #include "root_view.hpp" -#include "eta_progress_display.hpp" +#include "data_transfer_progress_display.hpp" namespace nxdt::views { @@ -35,6 +33,7 @@ namespace nxdt::views class OptionsTabUpdateFileDialog: public brls::Dialog { private: + DataTransferProgressDisplay *update_progress = nullptr; nxdt::tasks::DownloadFileTask download_task; std::string success_str; @@ -51,9 +50,9 @@ namespace nxdt::views size_t json_buf_size = 0; UtilsGitHubReleaseJsonData json_data = {0}; - brls::Label *wait_lbl = nullptr; /// First stage. - brls::List *changelog_list = nullptr; /// Second stage. - EtaProgressDisplay *update_progress = nullptr; /// Third stage. + brls::Label *wait_lbl = nullptr; /// First stage. + brls::List *changelog_list = nullptr; /// Second stage. + DataTransferProgressDisplay *update_progress = nullptr; /// Third stage. nxdt::tasks::DownloadFileTask nro_task; diff --git a/include/tasks.hpp b/include/tasks.hpp index 76aa9ce..ddb0ccb 100644 --- a/include/tasks.hpp +++ b/include/tasks.hpp @@ -26,7 +26,7 @@ #include -#include "defines.h" +#include "core/nxdt_includes.h" #include "core/gamecard.h" #include "core/title.h" #include "core/ums.h" diff --git a/romfs/i18n/en-US/dump_options.json b/romfs/i18n/en-US/dump_options.json index f7ed0b8..7c16662 100644 --- a/romfs/i18n/en-US/dump_options.json +++ b/romfs/i18n/en-US/dump_options.json @@ -6,10 +6,10 @@ "output_storage": { "label": "Output storage", - "description": "Storage where the dumped data will be written to. Changing it will automatically update the output filename to better suit the output filesystem limitations.\nUsing a connected USB host requires a libusb-based driver, as well as the Python host script. For more information, please visit \"{}\".", - "value_00": "SD card", + "description": "Storage where the dumped data will be written to. Changing it will automatically update the output filename to better suit the output filesystem limitations.\nUsing a connected USB host requires a libusb-based driver, as well as the Python host script. For more information, please visit \"{0}\".", + "value_00": "SD card ({0} free / {1} total)", "value_01": "USB host", - "value_02": "USB Mass Storage ({})" + "value_02": "USB Mass Storage {1:.{0}} ({2} free / {3} total) ({4})" }, "prepend_key_area": { diff --git a/romfs/i18n/en-US/gamecard_tab.json b/romfs/i18n/en-US/gamecard_tab.json index 74af191..9b0321a 100644 --- a/romfs/i18n/en-US/gamecard_tab.json +++ b/romfs/i18n/en-US/gamecard_tab.json @@ -39,7 +39,7 @@ "dump_initial_data": { "label": "Dump InitialData area", - "description": "The InitialData area holds cryptographic information used by the Lotus ASIC to communicate with the gamecard.\nIt can't be dumped through normal means -- it's not part of the storage areas from gamecard images." + "description": "The InitialData area holds cryptographic information used by the Lotus ASIC to communicate with the gamecard.\nIt can't be dumped through normal means — it's not part of the storage areas from gamecard images." }, "dump_certificate": { @@ -49,12 +49,12 @@ "dump_card_id_set": { "label": "Dump card ID set", - "description": "The card ID set is composed of three 32-bit integers that hold information such as the gamecard's memory type and manufacturer.\nIt can't be dumped through normal means -- it's not part of the storage areas from gamecard images." + "description": "The card ID set is composed of three 32-bit integers that hold information such as the gamecard's memory type and manufacturer.\nIt can't be dumped through normal means — it's not part of the storage areas from gamecard images." }, "dump_card_uid": { "label": "Dump card UID", - "description": "The card UID is a 64-byte long area that serves as a unique identifier for the gamecard. It's mostly used in security contexts.\nIt can't be dumped through normal means -- it's not part of the storage areas from gamecard images." + "description": "The card UID is a 64-byte long area that serves as a unique identifier for the gamecard. It's mostly used in security contexts.\nIt can't be dumped through normal means — it's not part of the storage areas from gamecard images." }, "dump_header": { diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index fa3f911..2e33fee 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -33,12 +33,6 @@ #include "nxdt_devoptab.h" #include "fatfs/ff.h" -/// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits. -/// Reference: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits. -/// Most modern filesystems use a 255-byte limit instead of 255-character/codepoint limit, so that's what we're gonna use. -#define FS_MAX_FILENAME_LENGTH 255 -#define SDMC_MAX_FILENAME_LENGTH 128 /* Arbitrarily set, I'm tired of FS sysmodule shenanigans. */ - /* Type definitions. */ /* Reference: https://github.com/Atmosphere-NX/Atmosphere/blob/master/exosphere/program/source/smc/secmon_smc_info.hpp. */ diff --git a/source/eta_progress_display.cpp b/source/data_transfer_progress_display.cpp similarity index 79% rename from source/eta_progress_display.cpp rename to source/data_transfer_progress_display.cpp index 33932fe..6170e4e 100644 --- a/source/eta_progress_display.cpp +++ b/source/data_transfer_progress_display.cpp @@ -1,5 +1,5 @@ /* - * eta_progress_display.cpp + * data_transfer_progress_display.cpp * * Copyright (c) 2020-2024, DarkMatterCore . * @@ -19,15 +19,11 @@ * along with this program. If not, see . */ -#include -#include - -namespace i18n = brls::i18n; /* For getStr(). */ -using namespace i18n::literals; /* For _i18n. */ +#include namespace nxdt::views { - EtaProgressDisplay::EtaProgressDisplay(void) + DataTransferProgressDisplay::DataTransferProgressDisplay(void) { this->progress_display = new brls::ProgressDisplay(); this->progress_display->setParent(this); @@ -41,14 +37,14 @@ namespace nxdt::views this->speed_eta_lbl->setParent(this); } - EtaProgressDisplay::~EtaProgressDisplay(void) + DataTransferProgressDisplay::~DataTransferProgressDisplay(void) { delete this->progress_display; delete this->size_lbl; delete this->speed_eta_lbl; } - void EtaProgressDisplay::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) + void DataTransferProgressDisplay::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) { /* Progress display. */ this->progress_display->frame(ctx); @@ -60,7 +56,7 @@ namespace nxdt::views this->speed_eta_lbl->frame(ctx); } - void EtaProgressDisplay::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) + void DataTransferProgressDisplay::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) { unsigned elem_width = roundf(static_cast(this->width) * 0.90f); @@ -94,17 +90,17 @@ namespace nxdt::views this->speed_eta_lbl->getHeight()); } - void EtaProgressDisplay::setProgress(const EtaProgressInfo& progress) + void DataTransferProgressDisplay::setProgress(const nxdt::tasks::DataTransferProgress& progress) { /* Update progress percentage. */ this->progress_display->setProgress(progress.percentage, 100); /* Update size string. */ - this->size_lbl->setText(fmt::format("{} / {}", this->GetFormattedSizeString(static_cast(progress.current)), \ - progress.size ? this->GetFormattedSizeString(static_cast(progress.size)) : "?")); + this->size_lbl->setText(fmt::format("{} / {}", this->GetFormattedSizeString(static_cast(progress.xfer_size)), \ + progress.total_size ? this->GetFormattedSizeString(static_cast(progress.total_size)) : "?")); /* Update speed / ETA string. */ - if (progress.eta.length()) + if (!progress.eta.empty()) { this->speed_eta_lbl->setText(fmt::format("{}/s - ETA: {}", this->GetFormattedSizeString(progress.speed), progress.eta)); } else { @@ -114,17 +110,17 @@ namespace nxdt::views this->invalidate(); } - void EtaProgressDisplay::willAppear(bool resetState) + void DataTransferProgressDisplay::willAppear(bool resetState) { this->progress_display->willAppear(resetState); } - void EtaProgressDisplay::willDisappear(bool resetState) + void DataTransferProgressDisplay::willDisappear(bool resetState) { this->progress_display->willDisappear(resetState); } - std::string EtaProgressDisplay::GetFormattedSizeString(double size) + std::string DataTransferProgressDisplay::GetFormattedSizeString(double size) { char strbuf[0x40] = {0}; utilsGenerateFormattedSizeString(size, strbuf, sizeof(strbuf)); diff --git a/source/gamecard_tab.cpp b/source/gamecard_tab.cpp index acd61f6..3366b53 100644 --- a/source/gamecard_tab.cpp +++ b/source/gamecard_tab.cpp @@ -252,7 +252,9 @@ namespace nxdt::views icon->setImage(BOREALIS_ASSET("icon/" APP_TITLE ".jpg")); icon->setScaleType(brls::ImageScaleType::SCALE); - brls::Application::pushView(new DumpOptionsFrame(this->root_view, "gamecard_tab/list/dump_card_image/label"_i18n, icon, raw_filename, ".xci"), brls::ViewAnimation::SLIDE_LEFT); + brls::Application::pushView(new DumpOptionsFrame(this->root_view, "gamecard_tab/list/dump_card_image/label"_i18n, icon, std::string(raw_filename), ".xci"), brls::ViewAnimation::SLIDE_LEFT); + + free(raw_filename); }); this->list->addView(dump_card_image); diff --git a/source/options_tab.cpp b/source/options_tab.cpp index 3954a45..16023e7 100644 --- a/source/options_tab.cpp +++ b/source/options_tab.cpp @@ -19,9 +19,7 @@ * along with this program. If not, see . */ -#include #include -#include #include #include #include @@ -34,11 +32,11 @@ namespace nxdt::views OptionsTabUpdateFileDialog::OptionsTabUpdateFileDialog(std::string path, std::string url, bool force_https, std::string success_str) : brls::Dialog(), success_str(success_str) { /* Set content view. */ - EtaProgressDisplay *update_progress = new EtaProgressDisplay(); - this->setContentView(update_progress); + this->update_progress = new DataTransferProgressDisplay(); + this->setContentView(this->update_progress); /* Add cancel button. */ - this->addButton("options_tab/update_dialog/cancel"_i18n, [this](brls::View* view) { + this->addButton("options_tab/update_dialog/cancel"_i18n, [&](brls::View* view) { /* Cancel download task. */ this->download_task.cancel(); @@ -50,15 +48,15 @@ namespace nxdt::views this->setCancelable(false); /* Subscribe to the download task. */ - this->download_task.RegisterListener([this, update_progress](const EtaProgressInfo& progress) { + this->download_task.RegisterListener([&](const nxdt::tasks::DataTransferProgress& progress) { /* Update progress. */ - update_progress->setProgress(progress); + this->update_progress->setProgress(progress); /* Check if the download task has finished. */ - if (this->download_task.isFinished()) + if (this->download_task.IsFinished()) { /* Stop spinner. */ - update_progress->willDisappear(); + this->update_progress->willDisappear(); /* Update button label. */ this->setButtonText(0, "options_tab/update_dialog/close"_i18n); @@ -90,15 +88,14 @@ namespace nxdt::views this->addStage(this->changelog_list); /* Add third stage. */ - this->update_progress = new EtaProgressDisplay(); + this->update_progress = new DataTransferProgressDisplay(); this->addStage(this->update_progress); /* Subscribe to the JSON task. */ - this->json_task.RegisterListener([this](const EtaProgressInfo& progress) { + this->json_task.RegisterListener([&](const nxdt::tasks::DataTransferProgress& progress) { /* Return immediately if the JSON task hasn't finished. */ - if (!this->json_task.isFinished()) return; + if (!this->json_task.IsFinished()) return; - bool pop_view = false; std::string notification = ""; /* Retrieve task result. */ @@ -115,9 +112,6 @@ namespace nxdt::views /* Display changelog. */ this->DisplayChangelog(); } else { - /* Update flag. */ - pop_view = true; - /* Set notification string. */ notification = "options_tab/notifications/up_to_date"_i18n; } @@ -125,15 +119,12 @@ namespace nxdt::views /* Log downloaded data. */ LOG_DATA_ERROR(this->json_buf, this->json_buf_size, "Failed to parse GitHub release JSON. Downloaded data:"); - /* Update flag. */ - pop_view = true; - /* Set notification string. */ notification = "options_tab/notifications/github_json_failed"_i18n; } /* Pop view (if needed). */ - if (pop_view) + if (!notification.empty()) { /* Display notification. */ brls::Application::notify(notification); @@ -252,12 +243,12 @@ namespace nxdt::views this->updateActionHint(brls::Key::B, "options_tab/update_dialog/cancel"_i18n); /* Subscribe to the NRO task. */ - this->nro_task.RegisterListener([this](const EtaProgressInfo& progress) { + this->nro_task.RegisterListener([&](const nxdt::tasks::DataTransferProgress& progress) { /* Update progress. */ this->update_progress->setProgress(progress); /* Check if the download task has finished. */ - if (this->nro_task.isFinished()) + if (this->nro_task.IsFinished()) { /* Get NRO task result and immediately set application updated state if the task succeeded. */ bool ret = this->nro_task.get();