mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-12-22 16:42:11 +00:00
Implemented OptionsTabUpdateFileDialog and OptionsTabUpdateFileDialogContent classes.
Other changes include: * utils: utilsGenerateFormattedSizeString() now takes an input double instead of size_t. * DownloadTask: calculated speed is now expressed in bytes per second, so now it's up to the caller to convert it to other units. * DownloadTask: set download size and percentage if the download size isn't known and we're dealing with the final chunk. * http: slightly improved CURL error info formatting. * OptionsTab: fully implemented NSWDB XML update option.
This commit is contained in:
parent
f80573e5c4
commit
dbbc5c7ebd
11 changed files with 253 additions and 32 deletions
|
@ -109,7 +109,7 @@ void utilsTrimString(char *str);
|
|||
void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src, size_t src_size, bool uppercase);
|
||||
|
||||
/// Formats the provided 'size' value to a human-readable size string and stores it in 'dst'.
|
||||
void utilsGenerateFormattedSizeString(u64 size, char *dst, size_t dst_size);
|
||||
void utilsGenerateFormattedSizeString(double size, char *dst, size_t dst_size);
|
||||
|
||||
/// Saves the total size and free space available from the filesystem pointed to by the input path (e.g. "sdmc:/") to 'out_total' and 'out_free', respectively.
|
||||
/// Either 'out_total' or 'out_free' can be NULL, but at least one of them must be a valid pointer.
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace nxdt::tasks
|
|||
int percentage; ///< Progress percentage.
|
||||
|
||||
/// Fields set by DownloadTask::onProgressUpdate().
|
||||
double speed; ///< Download speed expressed in KiB/s.
|
||||
double speed; ///< Download speed expressed in bytes per second.
|
||||
std::string eta; ///< Formatted ETA string.
|
||||
} DownloadTaskProgress;
|
||||
|
||||
|
@ -205,6 +205,8 @@ namespace nxdt::tasks
|
|||
template<typename Result, typename... Params>
|
||||
void DownloadTask<Result, Params...>::onProgressUpdate(const DownloadTaskProgress& 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;
|
||||
|
@ -215,27 +217,35 @@ namespace nxdt::tasks
|
|||
std::chrono::duration<double> 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) || this->getStatus() == AsyncTaskStatus::RUNNING)) return;
|
||||
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<double>(progress.current - prev_current);
|
||||
|
||||
/* Calculate download speed in kibibytes per second (KiB/s). */
|
||||
double speed = ((diff_current / diff_time_conv) / 1024.0);
|
||||
|
||||
/* Calculate remaining data size in kibibytes (KiB) and ETA if we know the download size. */
|
||||
double eta = 0.0;
|
||||
|
||||
if (progress.size)
|
||||
{
|
||||
double remaining = (static_cast<double>(progress.size - progress.current) / 1024.0);
|
||||
eta = (remaining / speed);
|
||||
}
|
||||
/* Calculate download speed in bytes per second. */
|
||||
double speed = (diff_current / diff_time_conv);
|
||||
|
||||
/* Fill struct. */
|
||||
DownloadTaskProgress new_progress = progress;
|
||||
new_progress.speed = speed;
|
||||
new_progress.eta = (progress.size ? fmt::format("{:02}H{:02}M{:02}S", std::fmod(eta, 86400.0) / 3600.0, std::fmod(eta, 3600.0) / 60.0, std::fmod(eta, 60.0)) : "");
|
||||
|
||||
if (progress.size)
|
||||
{
|
||||
/* Calculate remaining data size and ETA if we know the download size. */
|
||||
double remaining = static_cast<double>(progress.size - progress.current);
|
||||
double eta = (remaining / speed);
|
||||
new_progress.eta = fmt::format("{:02}H{:02}M{:02}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;
|
||||
|
|
|
@ -26,8 +26,47 @@
|
|||
|
||||
#include <borealis.hpp>
|
||||
|
||||
#include "download_task.hpp"
|
||||
|
||||
namespace nxdt::views
|
||||
{
|
||||
/* Used as the content view for OptionsTabUpdateFileDialog. */
|
||||
class OptionsTabUpdateFileDialogContent: public brls::View
|
||||
{
|
||||
private:
|
||||
brls::ProgressDisplay *progress_display = nullptr;
|
||||
brls::Label *size_label = nullptr, *speed_eta_label = nullptr;
|
||||
|
||||
std::string GetFormattedSizeString(size_t size);
|
||||
std::string GetFormattedSizeString(double size);
|
||||
|
||||
protected:
|
||||
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:
|
||||
OptionsTabUpdateFileDialogContent(void);
|
||||
~OptionsTabUpdateFileDialogContent(void);
|
||||
|
||||
void SetProgress(const nxdt::tasks::DownloadTaskProgress& progress);
|
||||
|
||||
void willAppear(bool resetState = false) override;
|
||||
void willDisappear(bool resetState = false) override;
|
||||
};
|
||||
|
||||
/* Update file dialog. */
|
||||
class OptionsTabUpdateFileDialog: public brls::Dialog
|
||||
{
|
||||
private:
|
||||
nxdt::tasks::DownloadFileTask download_task;
|
||||
std::string success_str;
|
||||
|
||||
public:
|
||||
OptionsTabUpdateFileDialog(std::string path, std::string url, bool force_https, std::string success_str);
|
||||
|
||||
bool onCancel(void) override;
|
||||
};
|
||||
|
||||
class OptionsTab: public brls::List
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ef8e8e96302064e52171204b75cd8f0145adc1ad
|
||||
Subproject commit 1e2134a2ef140a7bca3fb799aba294dda1db1075
|
|
@ -25,8 +25,15 @@
|
|||
"description": "Checks if an update is available in nxdumptool's GitHub repository. Requires Internet connectivity."
|
||||
},
|
||||
|
||||
"update_dialog": {
|
||||
"cancel": "Cancel",
|
||||
"close": "Close"
|
||||
},
|
||||
|
||||
"notifications": {
|
||||
"is_nso": "The application is running as an NSO. Unable to update.",
|
||||
"already_updated": "The application has already been updated. Please reload."
|
||||
"already_updated": "The application has already been updated. Please reload.",
|
||||
"update_failed": "Update failed! For more information, please check the logfile.",
|
||||
"nswdb_xml_updated": "NSWDB XML successfully updated!"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ bool httpPerformGetRequest(const char *url, bool force_https, size_t *outsize, H
|
|||
long http_code = 0;
|
||||
curl_off_t download_size = 0, content_length = 0;
|
||||
char curl_err_buf[CURL_ERROR_SIZE] = {0};
|
||||
const char *error_str = NULL;
|
||||
|
||||
/* Start CURL session. */
|
||||
curl = curl_easy_init();
|
||||
|
@ -167,10 +168,12 @@ bool httpPerformGetRequest(const char *url, bool force_https, size_t *outsize, H
|
|||
if (curl_err_buf[curl_err_buf_len - 1] == '\n') curl_err_buf[--curl_err_buf_len] = '\0';
|
||||
if (curl_err_buf[curl_err_buf_len - 1] == '\r') curl_err_buf[--curl_err_buf_len] = '\0';
|
||||
|
||||
LOG_MSG("%s", curl_err_buf);
|
||||
error_str = curl_err_buf;
|
||||
} else {
|
||||
LOG_MSG("%s", curl_easy_strerror(res));
|
||||
error_str = curl_easy_strerror(res);
|
||||
}
|
||||
|
||||
if (error_str) LOG_MSG("CURL error info: \"%s\".", error_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -572,18 +572,18 @@ void utilsGenerateHexStringFromData(char *dst, size_t dst_size, const void *src,
|
|||
dst[j] = '\0';
|
||||
}
|
||||
|
||||
void utilsGenerateFormattedSizeString(u64 size, char *dst, size_t dst_size)
|
||||
void utilsGenerateFormattedSizeString(double size, char *dst, size_t dst_size)
|
||||
{
|
||||
if (!dst || dst_size < 2) return;
|
||||
|
||||
double converted_size = (double)size;
|
||||
size = fabs(size);
|
||||
|
||||
for(u32 i = 0; i < g_sizeSuffixesCount; i++)
|
||||
{
|
||||
if (converted_size >= pow(1024.0, i + 1) && (i + 1) < g_sizeSuffixesCount) continue;
|
||||
if (size >= pow(1024.0, i + 1) && (i + 1) < g_sizeSuffixesCount) continue;
|
||||
|
||||
converted_size /= pow(1024.0, i);
|
||||
snprintf(dst, dst_size, "%.*f %s", (converted_size >= 100.0 ? 0 : (converted_size >= 10.0 ? 1 : 2)), converted_size, g_sizeSuffixes[i]);
|
||||
size /= pow(1024.0, i);
|
||||
snprintf(dst, dst_size, "%.2F %s", size, g_sizeSuffixes[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1760,7 +1760,7 @@ static bool titleGenerateTitleInfoEntriesForTitleStorage(TitleStorage *title_sto
|
|||
cur_title_info->storage_id = storage_id;
|
||||
memcpy(&(cur_title_info->meta_key), cur_meta_key, sizeof(NcmContentMetaKey));
|
||||
cur_title_info->version.value = cur_title_info->meta_key.version;
|
||||
utilsGenerateFormattedSizeString(cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
|
||||
utilsGenerateFormattedSizeString((double)cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
|
||||
|
||||
/* Retrieve application metadata. */
|
||||
u64 app_id = (cur_title_info->meta_key.type <= NcmContentMetaType_Application ? cur_title_info->meta_key.id : \
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace nxdt::views
|
|||
|
||||
void ErrorFrame::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
|
||||
{
|
||||
this->label->setWidth(roundf((float)this->width * 0.90f));
|
||||
this->label->setWidth(roundf(static_cast<float>(this->width) * 0.90f));
|
||||
this->label->invalidate(true);
|
||||
|
||||
this->label->setBoundaries(
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace nxdt::views
|
|||
char strbuf[0x40] = {0};
|
||||
|
||||
func(&size);
|
||||
utilsGenerateFormattedSizeString(size, strbuf, sizeof(strbuf));
|
||||
utilsGenerateFormattedSizeString(static_cast<double>(size), strbuf, sizeof(strbuf));
|
||||
|
||||
return std::string(strbuf);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,164 @@ using namespace brls::i18n::literals; /* For _i18n. */
|
|||
|
||||
namespace nxdt::views
|
||||
{
|
||||
OptionsTabUpdateFileDialogContent::OptionsTabUpdateFileDialogContent(void)
|
||||
{
|
||||
this->progress_display = new brls::ProgressDisplay();
|
||||
this->progress_display->setParent(this);
|
||||
|
||||
this->size_label = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
|
||||
this->size_label->setVerticalAlign(NVG_ALIGN_BOTTOM);
|
||||
this->size_label->setParent(this);
|
||||
|
||||
this->speed_eta_label = new brls::Label(brls::LabelStyle::MEDIUM, "", false);
|
||||
this->speed_eta_label->setVerticalAlign(NVG_ALIGN_TOP);
|
||||
this->speed_eta_label->setParent(this);
|
||||
}
|
||||
|
||||
OptionsTabUpdateFileDialogContent::~OptionsTabUpdateFileDialogContent(void)
|
||||
{
|
||||
delete this->progress_display;
|
||||
delete this->size_label;
|
||||
delete this->speed_eta_label;
|
||||
}
|
||||
|
||||
void OptionsTabUpdateFileDialogContent::SetProgress(const nxdt::tasks::DownloadTaskProgress& progress)
|
||||
{
|
||||
/* Update progress percentage. */
|
||||
this->progress_display->setProgress(progress.size ? progress.percentage : 0, 100);
|
||||
|
||||
/* Update size string. */
|
||||
this->size_label->setText(fmt::format("{} / {}", this->GetFormattedSizeString(progress.current), progress.size ? this->GetFormattedSizeString(progress.size) : "?"));
|
||||
|
||||
/* Update speed / ETA string. */
|
||||
if (progress.eta != "")
|
||||
{
|
||||
this->speed_eta_label->setText(fmt::format("{}/s - ETA: {}", this->GetFormattedSizeString(progress.speed), progress.eta));
|
||||
} else {
|
||||
this->speed_eta_label->setText(fmt::format("{}/s", this->GetFormattedSizeString(progress.speed)));
|
||||
}
|
||||
|
||||
this->invalidate();
|
||||
}
|
||||
|
||||
void OptionsTabUpdateFileDialogContent::willAppear(bool resetState)
|
||||
{
|
||||
this->progress_display->willAppear(resetState);
|
||||
}
|
||||
|
||||
void OptionsTabUpdateFileDialogContent::willDisappear(bool resetState)
|
||||
{
|
||||
this->progress_display->willDisappear(resetState);
|
||||
}
|
||||
|
||||
void OptionsTabUpdateFileDialogContent::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
|
||||
{
|
||||
/* Progress display. */
|
||||
this->progress_display->frame(ctx);
|
||||
|
||||
/* Size label. */
|
||||
this->size_label->frame(ctx);
|
||||
|
||||
/* Speed / ETA label. */
|
||||
this->speed_eta_label->frame(ctx);
|
||||
}
|
||||
|
||||
void OptionsTabUpdateFileDialogContent::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
|
||||
{
|
||||
unsigned elem_width = roundf(static_cast<float>(this->width) * 0.90f);
|
||||
|
||||
/* Progress display. */
|
||||
this->progress_display->setBoundaries(
|
||||
this->x + (this->width - elem_width) / 2,
|
||||
this->y + (this->height - style->CrashFrame.buttonHeight) / 2,
|
||||
elem_width,
|
||||
style->CrashFrame.buttonHeight);
|
||||
|
||||
this->progress_display->invalidate(true);
|
||||
|
||||
/* Size label. */
|
||||
this->size_label->setWidth(elem_width);
|
||||
this->size_label->invalidate(true);
|
||||
|
||||
this->size_label->setBoundaries(
|
||||
this->x + (this->width - this->size_label->getWidth()) / 2,
|
||||
this->progress_display->getY() - this->progress_display->getHeight() / 8,
|
||||
this->size_label->getWidth(),
|
||||
this->size_label->getHeight());
|
||||
|
||||
/* Speed / ETA label. */
|
||||
this->speed_eta_label->setWidth(elem_width);
|
||||
this->speed_eta_label->invalidate(true);
|
||||
|
||||
this->speed_eta_label->setBoundaries(
|
||||
this->x + (this->width - this->speed_eta_label->getWidth()) / 2,
|
||||
this->progress_display->getY() + this->progress_display->getHeight() + this->progress_display->getHeight() / 8,
|
||||
this->speed_eta_label->getWidth(),
|
||||
this->speed_eta_label->getHeight());
|
||||
}
|
||||
|
||||
std::string OptionsTabUpdateFileDialogContent::GetFormattedSizeString(size_t size)
|
||||
{
|
||||
char strbuf[0x40] = {0};
|
||||
utilsGenerateFormattedSizeString(static_cast<double>(size), strbuf, sizeof(strbuf));
|
||||
return std::string(strbuf);
|
||||
}
|
||||
|
||||
std::string OptionsTabUpdateFileDialogContent::GetFormattedSizeString(double size)
|
||||
{
|
||||
char strbuf[0x40] = {0};
|
||||
utilsGenerateFormattedSizeString(size, strbuf, sizeof(strbuf));
|
||||
return std::string(strbuf);
|
||||
}
|
||||
|
||||
OptionsTabUpdateFileDialog::OptionsTabUpdateFileDialog(std::string path, std::string url, bool force_https, std::string success_str) : brls::Dialog(), success_str(success_str)
|
||||
{
|
||||
/* Set content view. */
|
||||
OptionsTabUpdateFileDialogContent *content = new OptionsTabUpdateFileDialogContent();
|
||||
this->setContentView(content);
|
||||
|
||||
/* Add cancel button. */
|
||||
this->addButton("options_tab/update_dialog/cancel"_i18n, [this](brls::View* view) {
|
||||
this->onCancel();
|
||||
});
|
||||
|
||||
/* Disable cancelling with B button. */
|
||||
this->setCancelable(false);
|
||||
|
||||
/* Subscribe to the download task. */
|
||||
this->download_task.RegisterListener([this, content](const nxdt::tasks::DownloadTaskProgress& progress) {
|
||||
/* Update progress. */
|
||||
content->SetProgress(progress);
|
||||
|
||||
/* Check if the download task has finished. */
|
||||
if (this->download_task.isFinished())
|
||||
{
|
||||
/* Stop spinner. */
|
||||
content->willDisappear();
|
||||
|
||||
/* Update button label. */
|
||||
this->setButtonText(0, "options_tab/update_dialog/close"_i18n);
|
||||
|
||||
/* Display notification. */
|
||||
brls::Application::notify(this->download_task.get() ? this->success_str : "options_tab/notifications/update_failed"_i18n);
|
||||
}
|
||||
});
|
||||
|
||||
/* Start download task. */
|
||||
this->download_task.execute(path, url, force_https);
|
||||
}
|
||||
|
||||
bool OptionsTabUpdateFileDialog::onCancel(void)
|
||||
{
|
||||
/* Cancel download task. */
|
||||
this->download_task.cancel();
|
||||
|
||||
/* Close dialog. */
|
||||
this->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OptionsTab::OptionsTab(void) : brls::List()
|
||||
{
|
||||
/* Set custom spacing. */
|
||||
|
@ -42,6 +200,7 @@ namespace nxdt::views
|
|||
brls::ToggleListItem *overclock = new brls::ToggleListItem("options_tab/overclock/label"_i18n, configGetBoolean("overclock"), \
|
||||
"options_tab/overclock/description"_i18n, "options_tab/overclock/value_enabled"_i18n, \
|
||||
"options_tab/overclock/value_disabled"_i18n);
|
||||
|
||||
overclock->getClickEvent()->subscribe([](brls::View* view) {
|
||||
brls::ToggleListItem *item = static_cast<brls::ToggleListItem*>(view);
|
||||
|
||||
|
@ -56,6 +215,7 @@ namespace nxdt::views
|
|||
|
||||
brls::Logger::debug("Overclock setting changed by user.");
|
||||
});
|
||||
|
||||
this->addView(overclock);
|
||||
|
||||
/* Naming convention. */
|
||||
|
@ -64,6 +224,7 @@ namespace nxdt::views
|
|||
"options_tab/naming_convention/value_01"_i18n
|
||||
}, static_cast<unsigned>(configGetInteger("naming_convention")),
|
||||
"options_tab/naming_convention/description"_i18n);
|
||||
|
||||
naming_convention->getValueSelectedEvent()->subscribe([](int selected){
|
||||
/* Make sure the current value isn't out of bounds. */
|
||||
if (selected < 0 || selected > static_cast<int>(TitleNamingConvention_Count)) return;
|
||||
|
@ -73,22 +234,22 @@ namespace nxdt::views
|
|||
|
||||
brls::Logger::debug("Naming convention setting changed by user.");
|
||||
});
|
||||
|
||||
this->addView(naming_convention);
|
||||
|
||||
/* Update NSWDB XML. */
|
||||
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) {
|
||||
brls::Dialog *dialog = new brls::Dialog("this is a test");
|
||||
dialog->setCancelable(false);
|
||||
dialog->addButton("cancel?", [dialog](brls::View *view) {
|
||||
dialog->close();
|
||||
});
|
||||
OptionsTabUpdateFileDialog *dialog = new OptionsTabUpdateFileDialog(NSWDB_XML_PATH, NSWDB_XML_URL, false, "options_tab/notifications/nswdb_xml_updated"_i18n);
|
||||
dialog->open(false);
|
||||
});
|
||||
|
||||
this->addView(update_nswdb_xml);
|
||||
|
||||
/* Update application. */
|
||||
brls::ListItem *update_app = new brls::ListItem("options_tab/update_app/label"_i18n, "options_tab/update_app/description"_i18n);
|
||||
|
||||
update_app->getClickEvent()->subscribe([this](brls::View* view) {
|
||||
if (envIsNso())
|
||||
{
|
||||
|
@ -108,6 +269,7 @@ namespace nxdt::views
|
|||
|
||||
brls::Application::pushView(staged_frame);*/
|
||||
});
|
||||
|
||||
this->addView(update_app);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue