1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-08 11:51:48 +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:
Pablo Curiel 2021-07-29 03:50:17 -04:00
parent f80573e5c4
commit dbbc5c7ebd
11 changed files with 253 additions and 32 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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!"
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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 : \

View file

@ -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(

View file

@ -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);
}

View file

@ -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);
}