From b2a70db9f657d5abbdaec39bf6332d90077b7c0d Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 12:55:56 -0700 Subject: [PATCH 01/53] add resource downloading table - minimal implementation --- .../CommonFramework/GlobalSettingsPanel.cpp | 1 + .../CommonFramework/GlobalSettingsPanel.h | 2 + .../CommonFramework/ResourceDownloadTable.cpp | 44 ++++++++++++++++++ .../CommonFramework/ResourceDownloadTable.h | 46 +++++++++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 5 files changed, 95 insertions(+) create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h diff --git a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp index 0305055e1e..ac74a8a3c1 100644 --- a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp +++ b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.cpp @@ -248,6 +248,7 @@ GlobalSettings::GlobalSettings() PA_ADD_OPTION(TEMP_FOLDER); PA_ADD_OPTION(THEME); PA_ADD_OPTION(USE_PADDLE_OCR); + PA_ADD_OPTION(RESOURCE_DOWNLOAD_TABLE); PA_ADD_OPTION(WINDOW_SIZE); PA_ADD_OPTION(LOG_WINDOW_SIZE); PA_ADD_OPTION(LOG_WINDOW_STARTUP); diff --git a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h index 52a6e30946..a389a1591c 100644 --- a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h +++ b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h @@ -17,6 +17,7 @@ #include "Common/Cpp/Options/StringOption.h" #include "CommonFramework/Panels/SettingsPanel.h" #include "CommonFramework/Panels/PanelTools.h" +#include "CommonFramework/ResourceDownloadTable.h" //#include //using std::cout; @@ -124,6 +125,7 @@ class GlobalSettings : public BatchOption, private ConfigOption::Listener, priva Pimpl THEME; BooleanCheckBoxOption USE_PADDLE_OCR; + ResourceDownloadTable RESOURCE_DOWNLOAD_TABLE; Pimpl WINDOW_SIZE; Pimpl LOG_WINDOW_SIZE; BooleanCheckBoxOption LOG_WINDOW_STARTUP; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp new file mode 100644 index 0000000000..7d835827d1 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp @@ -0,0 +1,44 @@ +/* Resource Download Table + * + * From: https://github.com/PokemonAutomation/ + * + */ + + +#include "ResourceDownloadTable.h" + +namespace PokemonAutomation{ + + +ResourceDownloadRow::ResourceDownloadRow( + std::string&& resource_name, + size_t file_size +) + : StaticTableRow(resource_name) + , m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) + , m_file_size(file_size) + , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) +{ + PA_ADD_STATIC(m_resource_name); + PA_ADD_OPTION(m_file_size_label); +} + +ResourceDownloadTable::ResourceDownloadTable() + : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) +{ + add_row(std::make_unique("PaddleOCR", 1000000)); + + finish_construction(); +} +std::vector ResourceDownloadTable::make_header() const{ + std::vector ret{ + "Resource", + "Size" + }; + return ret; +} + + + + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h new file mode 100644 index 0000000000..e909f4d1d5 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h @@ -0,0 +1,46 @@ +/* Resource Download Table + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_ResourceDownloadTable_H +#define PokemonAutomation_ResourceDownloadTable_H + +#include "CommonFramework/Options/LabelCellOption.h" +#include "Common/Cpp/Options/StaticTableOption.h" + +namespace PokemonAutomation{ + + + +class ResourceDownloadRow : public StaticTableRow{ + +public: + // ~ResourceDownloadRow(); + ResourceDownloadRow( + std::string&& resource_name, + size_t file_size + ); + + LabelCellOption m_resource_name; + size_t m_file_size; + LabelCellOption m_file_size_label; + + +}; + + +class ResourceDownloadTable : public StaticTableOption{ +public: + ResourceDownloadTable(); + + virtual std::vector make_header() const override; + +}; + + + + +} +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index f0a89d4520..b17153a414 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -472,6 +472,8 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/PersistentSettings.h Source/CommonFramework/ProgramSession.cpp Source/CommonFramework/ProgramSession.h + Source/CommonFramework/ResourceDownloadTable.cpp + Source/CommonFramework/ResourceDownloadTable.h Source/CommonFramework/ProgramStats/StatsDatabase.cpp Source/CommonFramework/ProgramStats/StatsDatabase.h Source/CommonFramework/ProgramStats/StatsTracking.cpp From 7a51707636c6e331c1548e315b20cfb144ea6e9e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 13:19:49 -0700 Subject: [PATCH 02/53] move ResourceDownloadTable.h to ResourceDownload folder --- .../{ => ResourceDownload}/ResourceDownloadTable.cpp | 9 ++++++++- .../{ => ResourceDownload}/ResourceDownloadTable.h | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) rename SerialPrograms/Source/CommonFramework/{ => ResourceDownload}/ResourceDownloadTable.cpp (75%) rename SerialPrograms/Source/CommonFramework/{ => ResourceDownload}/ResourceDownloadTable.h (74%) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp similarity index 75% rename from SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp rename to SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 7d835827d1..e74ba84ac4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -9,6 +9,11 @@ namespace PokemonAutomation{ +DownloadButton::DownloadButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , option(p_row) +{} + ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, @@ -18,9 +23,11 @@ ResourceDownloadRow::ResourceDownloadRow( , m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) + , m_download_button(*this) { PA_ADD_STATIC(m_resource_name); - PA_ADD_OPTION(m_file_size_label); + PA_ADD_STATIC(m_file_size_label); + // PA_ADD_STATIC(m_download_button); } ResourceDownloadTable::ResourceDownloadTable() diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h similarity index 74% rename from SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h rename to SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index e909f4d1d5..ad88d9d7f4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -13,6 +13,13 @@ namespace PokemonAutomation{ +class ResourceDownloadRow; +class DownloadButton : public ConfigOptionImpl{ +public: + DownloadButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& option; +}; class ResourceDownloadRow : public StaticTableRow{ @@ -26,6 +33,7 @@ class ResourceDownloadRow : public StaticTableRow{ LabelCellOption m_resource_name; size_t m_file_size; LabelCellOption m_file_size_label; + DownloadButton m_download_button; }; From 306eb13c57e45d4ce3ff28b77a9da48a28d1ff5e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 14:05:19 -0700 Subject: [PATCH 03/53] add DownloadButton and DownloadButtonWidget --- .../CommonFramework/GlobalSettingsPanel.h | 2 +- .../ResourceDownloadTable.cpp | 9 ++-- .../ResourceDownload/ResourceDownloadTable.h | 6 +-- .../ResourceDownloadWidget.cpp | 44 +++++++++++++++++++ .../ResourceDownload/ResourceDownloadWidget.h | 27 ++++++++++++ .../Source/StaticRegistrationQt.cpp | 6 +++ SerialPrograms/cmake/SourceFiles.cmake | 8 ++-- 7 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h diff --git a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h index a389a1591c..938f354707 100644 --- a/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h +++ b/SerialPrograms/Source/CommonFramework/GlobalSettingsPanel.h @@ -17,7 +17,7 @@ #include "Common/Cpp/Options/StringOption.h" #include "CommonFramework/Panels/SettingsPanel.h" #include "CommonFramework/Panels/PanelTools.h" -#include "CommonFramework/ResourceDownloadTable.h" +#include "CommonFramework/ResourceDownload/ResourceDownloadTable.h" //#include //using std::cout; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index e74ba84ac4..d6be378ab5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -9,8 +9,8 @@ namespace PokemonAutomation{ -DownloadButton::DownloadButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) +ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , option(p_row) {} @@ -27,7 +27,7 @@ ResourceDownloadRow::ResourceDownloadRow( { PA_ADD_STATIC(m_resource_name); PA_ADD_STATIC(m_file_size_label); - // PA_ADD_STATIC(m_download_button); + PA_ADD_STATIC(m_download_button); } ResourceDownloadTable::ResourceDownloadTable() @@ -40,7 +40,8 @@ ResourceDownloadTable::ResourceDownloadTable() std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ "Resource", - "Size" + "Size", + "", }; return ret; } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index ad88d9d7f4..66f0413bb2 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -14,9 +14,9 @@ namespace PokemonAutomation{ class ResourceDownloadRow; -class DownloadButton : public ConfigOptionImpl{ +class ResourceDownloadButton : public ConfigOptionImpl{ public: - DownloadButton(ResourceDownloadRow& p_row); + ResourceDownloadButton(ResourceDownloadRow& p_row); ResourceDownloadRow& option; }; @@ -33,7 +33,7 @@ class ResourceDownloadRow : public StaticTableRow{ LabelCellOption m_resource_name; size_t m_file_size; LabelCellOption m_file_size_label; - DownloadButton m_download_button; + ResourceDownloadButton m_download_button; }; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp new file mode 100644 index 0000000000..0774d8cf19 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -0,0 +1,44 @@ +/* Resource Download Widget + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include +#include "CommonFramework/Logging/Logger.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "ResourceDownloadWidget.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + + +template class RegisterConfigWidget; + + +DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) + : ConfigWidget(value) +{ + QPushButton* button = new QPushButton(&parent); + m_widget = button; + + QFont font; + font.setBold(true); + button->setFont(font); + button->setText("Download"); + + button->connect( + button, &QPushButton::clicked, + button, [&](bool){ + cout << "Clicked Download Button" << endl; + } + ); +} + + + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h new file mode 100644 index 0000000000..af013e05bd --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -0,0 +1,27 @@ +/* Resource Download Widget + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_ResourceDownloadWidget_H +#define PokemonAutomation_ResourceDownloadWidget_H + +#include "Common/Qt/Options/ConfigWidget.h" +#include "ResourceDownloadTable.h" + +namespace PokemonAutomation{ + + +class DownloadButtonWidget : public ConfigWidget{ +public: + using ParentOption = ResourceDownloadButton; + +public: + DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value); +}; + + + +} +#endif diff --git a/SerialPrograms/Source/StaticRegistrationQt.cpp b/SerialPrograms/Source/StaticRegistrationQt.cpp index 47d1fa0d9e..fec70852ea 100644 --- a/SerialPrograms/Source/StaticRegistrationQt.cpp +++ b/SerialPrograms/Source/StaticRegistrationQt.cpp @@ -36,6 +36,9 @@ #include "CommonFramework/Options/QtWidget/LabelCellWidget.h" #include "CommonFramework/Notifications/EventNotificationWidget.h" +// Resource Download +#include "CommonFramework/ResourceDownload/ResourceDownloadWidget.h" + // Integrations #include "Integrations/DiscordIntegrationSettingsWidget.h" @@ -99,6 +102,9 @@ void register_all_statics(){ RegisterConfigWidget(); RegisterConfigWidget(); + // Resource Download + RegisterConfigWidget(); + // Integrations RegisterConfigWidget(); diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index b17153a414..125e99e017 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -471,9 +471,7 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/PersistentSettings.cpp Source/CommonFramework/PersistentSettings.h Source/CommonFramework/ProgramSession.cpp - Source/CommonFramework/ProgramSession.h - Source/CommonFramework/ResourceDownloadTable.cpp - Source/CommonFramework/ResourceDownloadTable.h + Source/CommonFramework/ProgramSession.h Source/CommonFramework/ProgramStats/StatsDatabase.cpp Source/CommonFramework/ProgramStats/StatsDatabase.h Source/CommonFramework/ProgramStats/StatsTracking.cpp @@ -489,6 +487,10 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Recording/StreamHistoryTracker_SaveFrames.h Source/CommonFramework/Recording/StreamRecorder.cpp Source/CommonFramework/Recording/StreamRecorder.h + Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp + Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h + Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp + Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h Source/CommonFramework/Startup/NewVersionCheck.cpp Source/CommonFramework/Startup/NewVersionCheck.h Source/CommonFramework/Startup/SetupSettings.cpp From 8378b7f255ce27e71713bed6da35db7f4948ad4b Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 14:48:27 -0700 Subject: [PATCH 04/53] add Delete Button --- .../ResourceDownloadTable.cpp | 10 ++++++++- .../ResourceDownload/ResourceDownloadTable.h | 8 +++++++ .../ResourceDownloadWidget.cpp | 22 +++++++++++++++++++ .../ResourceDownload/ResourceDownloadWidget.h | 9 ++++++++ .../Source/StaticRegistrationQt.cpp | 1 + 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index d6be378ab5..abfd494451 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -12,7 +12,12 @@ namespace PokemonAutomation{ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , option(p_row) -{} +{} + +ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , option(p_row) +{} ResourceDownloadRow::ResourceDownloadRow( @@ -24,10 +29,12 @@ ResourceDownloadRow::ResourceDownloadRow( , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) , m_download_button(*this) + , m_delete_button(*this) { PA_ADD_STATIC(m_resource_name); PA_ADD_STATIC(m_file_size_label); PA_ADD_STATIC(m_download_button); + PA_ADD_STATIC(m_delete_button); } ResourceDownloadTable::ResourceDownloadTable() @@ -42,6 +49,7 @@ std::vector ResourceDownloadTable::make_header() const{ "Resource", "Size", "", + "", }; return ret; } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 66f0413bb2..f47dedb704 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -21,6 +21,13 @@ class ResourceDownloadButton : public ConfigOptionImpl{ ResourceDownloadRow& option; }; +class ResourceDeleteButton : public ConfigOptionImpl{ +public: + ResourceDeleteButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& option; +}; + class ResourceDownloadRow : public StaticTableRow{ public: @@ -34,6 +41,7 @@ class ResourceDownloadRow : public StaticTableRow{ size_t m_file_size; LabelCellOption m_file_size_label; ResourceDownloadButton m_download_button; + ResourceDeleteButton m_delete_button; }; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 0774d8cf19..a894423ce3 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -39,6 +39,28 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt ); } +template class RegisterConfigWidget; + + +DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) + : ConfigWidget(value) +{ + QPushButton* button = new QPushButton(&parent); + m_widget = button; + + QFont font; + font.setBold(true); + button->setFont(font); + button->setText("Delete"); + + button->connect( + button, &QPushButton::clicked, + button, [&](bool){ + cout << "Clicked Delete Button" << endl; + } + ); +} + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index af013e05bd..69c9971a12 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -22,6 +22,15 @@ class DownloadButtonWidget : public ConfigWidget{ }; +class DeleteButtonWidget : public ConfigWidget{ +public: + using ParentOption = ResourceDeleteButton; + +public: + DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); +}; + + } #endif diff --git a/SerialPrograms/Source/StaticRegistrationQt.cpp b/SerialPrograms/Source/StaticRegistrationQt.cpp index fec70852ea..625c70325c 100644 --- a/SerialPrograms/Source/StaticRegistrationQt.cpp +++ b/SerialPrograms/Source/StaticRegistrationQt.cpp @@ -104,6 +104,7 @@ void register_all_statics(){ // Resource Download RegisterConfigWidget(); + RegisterConfigWidget(); // Integrations RegisterConfigWidget(); From 64a5534efa036c361a39627cb79299ee605d9af1 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 17:26:33 -0700 Subject: [PATCH 05/53] add_rows_from_resource_list_json --- .../ResourceDownloadTable.cpp | 28 +++++++++++++++++-- .../ResourceDownload/ResourceDownloadTable.h | 3 ++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index abfd494451..594dead1bc 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -4,7 +4,9 @@ * */ - +#include "CommonFramework/Globals.h" +#include "Common/Cpp/Json/JsonArray.h" +#include "Common/Cpp/Json/JsonObject.h" #include "ResourceDownloadTable.h" namespace PokemonAutomation{ @@ -40,7 +42,7 @@ ResourceDownloadRow::ResourceDownloadRow( ResourceDownloadTable::ResourceDownloadTable() : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) { - add_row(std::make_unique("PaddleOCR", 1000000)); + add_rows_from_resource_list_json(); finish_construction(); } @@ -55,6 +57,28 @@ std::vector ResourceDownloadTable::make_header() const{ } +void ResourceDownloadTable::add_rows_from_resource_list_json(){ + JsonValue json = load_json_file(RESOURCE_PATH() + "ResourceList.json"); + + try{ + const JsonObject& obj = json.to_object_throw(); + const JsonArray& resource_list = obj.get_array_throw("resourceList"); + for (const JsonValue& resource_val : resource_list){ + const JsonObject& resource_obj = resource_val.to_object_throw(); + + std::string resource_name = resource_obj.get_string_throw("resourceName"); + int64_t decompressed_bytes = resource_obj.get_integer_throw("DecompressedBytes"); + + add_row(std::make_unique(std::move(resource_name), decompressed_bytes)); + } + + }catch (ParseException& e){ + throw ParseException(e.message() + "\nJSON parsing error. Given JSON file doesn't match the expected format."); + } + +} + + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index f47dedb704..85f42a15d5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -53,6 +53,9 @@ class ResourceDownloadTable : public StaticTableOption{ virtual std::vector make_header() const override; + void add_rows_from_resource_list_json(); + + }; From ed2a9766f4d0cef2e2b087a46b33fab6434f0668 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 23:13:14 -0700 Subject: [PATCH 06/53] refactor so that we keep a handle on m_resources and m_resource_rows. --- .../ResourceDownloadTable.cpp | 57 +++++++++++++++++-- .../ResourceDownload/ResourceDownloadTable.h | 26 ++++++++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 594dead1bc..71565107f4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -24,16 +24,20 @@ ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, + bool is_downloaded, size_t file_size ) : StaticTableRow(resource_name) , m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) + , m_is_downloaded(is_downloaded) + , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Y" : "N") , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) , m_download_button(*this) , m_delete_button(*this) { PA_ADD_STATIC(m_resource_name); + PA_ADD_STATIC(m_is_downloaded_label); PA_ADD_STATIC(m_file_size_label); PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); @@ -41,23 +45,35 @@ ResourceDownloadRow::ResourceDownloadRow( ResourceDownloadTable::ResourceDownloadTable() : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) + , m_resources(deserialize_resource_list_json()) + , m_resource_rows(get_resource_download_rows()) { - add_rows_from_resource_list_json(); + add_resource_download_rows(); finish_construction(); } std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ "Resource", - "Size", + "Downloaded?", + "Size (MB)", "", "", }; return ret; } +ResourceType get_resource_type_from_string(std::string type){ + if (type == "ZippedFolder"){ + return ResourceType::ZIP_FILE; + }else{ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "VideoFPS: Unknown enum."); + } + +} -void ResourceDownloadTable::add_rows_from_resource_list_json(){ +std::vector ResourceDownloadTable::deserialize_resource_list_json(){ + std::vector resources; JsonValue json = load_json_file(RESOURCE_PATH() + "ResourceList.json"); try{ @@ -67,15 +83,46 @@ void ResourceDownloadTable::add_rows_from_resource_list_json(){ const JsonObject& resource_obj = resource_val.to_object_throw(); std::string resource_name = resource_obj.get_string_throw("resourceName"); - int64_t decompressed_bytes = resource_obj.get_integer_throw("DecompressedBytes"); + ResourceType resource_type = get_resource_type_from_string(resource_obj.get_string_throw("Type")); + size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); + size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); + std::string url = resource_obj.get_string_throw("URL"); + + DownloadedResource resource = { + resource_name, + resource_type, + compressed_bytes, + decompressed_bytes, + url + }; + + resources.emplace_back(std::move(resource)); - add_row(std::make_unique(std::move(resource_name), decompressed_bytes)); } }catch (ParseException& e){ throw ParseException(e.message() + "\nJSON parsing error. Given JSON file doesn't match the expected format."); } + return resources; +} + +std::vector> ResourceDownloadTable::get_resource_download_rows(){ + std::vector> resource_rows; + for (const DownloadedResource& resource : m_resources){ + bool is_downloaded = false; + std::string resource_name = resource.resource_name; + resource_rows.emplace_back(std::make_unique(std::move(resource_name), is_downloaded, resource.size_decompressed_bytes)); + } + + return resource_rows; +} + + +void ResourceDownloadTable::add_resource_download_rows(){ + for (auto& row_ptr : m_resource_rows){ + add_row(row_ptr.get()); + } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 85f42a15d5..42973d0324 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -34,10 +34,13 @@ class ResourceDownloadRow : public StaticTableRow{ // ~ResourceDownloadRow(); ResourceDownloadRow( std::string&& resource_name, + bool is_downloaded, size_t file_size ); LabelCellOption m_resource_name; + bool m_is_downloaded; + LabelCellOption m_is_downloaded_label; size_t m_file_size; LabelCellOption m_file_size_label; ResourceDownloadButton m_download_button; @@ -47,19 +50,40 @@ class ResourceDownloadRow : public StaticTableRow{ }; +enum class ResourceType{ + ZIP_FILE, +}; +struct DownloadedResource{ + std::string resource_name; + ResourceType resource_type; + size_t size_compressed_bytes; + size_t size_decompressed_bytes; + std::string url; +}; + class ResourceDownloadTable : public StaticTableOption{ public: ResourceDownloadTable(); virtual std::vector make_header() const override; - void add_rows_from_resource_list_json(); + std::vector deserialize_resource_list_json(); + std::vector> get_resource_download_rows(); + void add_resource_download_rows(); + +private: + std::vector m_resources; + + // we need to keep a handle on each Row, so that we can edit m_is_downloaded_label later on. + std::vector> m_resource_rows; }; + + } #endif From b98f2aabf56e191bde5c329d2296df0940c01443 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 16 Mar 2026 21:11:30 -0700 Subject: [PATCH 07/53] check if resource is downloaded --- .../ResourceDownload/ResourceDownloadTable.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 71565107f4..16f40dd0a5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -7,8 +7,11 @@ #include "CommonFramework/Globals.h" #include "Common/Cpp/Json/JsonArray.h" #include "Common/Cpp/Json/JsonObject.h" +#include "Common/Cpp/Filesystem.h" #include "ResourceDownloadTable.h" +#include + namespace PokemonAutomation{ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) @@ -110,8 +113,11 @@ std::vector ResourceDownloadTable::deserialize_resource_list std::vector> ResourceDownloadTable::get_resource_download_rows(){ std::vector> resource_rows; for (const DownloadedResource& resource : m_resources){ - bool is_downloaded = false; std::string resource_name = resource.resource_name; + + Filesystem::Path filepath{RUNTIME_BASE_PATH() + "DownloadedResources/" + resource_name}; + bool is_downloaded = std::filesystem::is_directory(filepath); + resource_rows.emplace_back(std::make_unique(std::move(resource_name), is_downloaded, resource.size_decompressed_bytes)); } From d9cab91c8f9aa0e421767ce67e992e221142feb2 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 16 Mar 2026 21:39:20 -0700 Subject: [PATCH 08/53] check if downloaded. add "Version" column --- .../ResourceDownloadTable.cpp | 38 +++++++++++++++---- .../ResourceDownload/ResourceDownloadTable.h | 25 ++++++++++-- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 16f40dd0a5..d5c860b6fe 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -27,25 +27,45 @@ ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, + size_t file_size, bool is_downloaded, - size_t file_size + ResourceVersion version ) : StaticTableRow(resource_name) , m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) - , m_is_downloaded(is_downloaded) - , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Y" : "N") , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) + , m_is_downloaded(is_downloaded) + , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") + , m_version(version) + , m_version_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version)) , m_download_button(*this) , m_delete_button(*this) { PA_ADD_STATIC(m_resource_name); - PA_ADD_STATIC(m_is_downloaded_label); PA_ADD_STATIC(m_file_size_label); + PA_ADD_STATIC(m_is_downloaded_label); + PA_ADD_STATIC(m_version_label); + PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); } +std::string ResourceDownloadRow::resource_version_to_string(ResourceVersion version){ + switch(version){ + case ResourceVersion::CURRENT: + return "Current"; + case ResourceVersion::OUTDATED: + return "Outdated"; + case ResourceVersion::NOT_APPLICABLE: + return "--"; + case ResourceVersion::BLANK: + return ""; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "resource_version_to_string: Unknown enum."); + } +} + ResourceDownloadTable::ResourceDownloadTable() : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) , m_resources(deserialize_resource_list_json()) @@ -58,8 +78,9 @@ ResourceDownloadTable::ResourceDownloadTable() std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ "Resource", - "Downloaded?", "Size (MB)", + "Downloaded", + "Version", "", "", }; @@ -70,7 +91,7 @@ ResourceType get_resource_type_from_string(std::string type){ if (type == "ZippedFolder"){ return ResourceType::ZIP_FILE; }else{ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "VideoFPS: Unknown enum."); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_resource_type_from_string: Unknown enum."); } } @@ -114,11 +135,12 @@ std::vector> ResourceDownloadTable::get_res std::vector> resource_rows; for (const DownloadedResource& resource : m_resources){ std::string resource_name = resource.resource_name; - + ResourceVersion version = ResourceVersion::BLANK; + Filesystem::Path filepath{RUNTIME_BASE_PATH() + "DownloadedResources/" + resource_name}; bool is_downloaded = std::filesystem::is_directory(filepath); - resource_rows.emplace_back(std::make_unique(std::move(resource_name), is_downloaded, resource.size_decompressed_bytes)); + resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, version)); } return resource_rows; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 42973d0324..6b937c6e5b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -28,21 +28,39 @@ class ResourceDeleteButton : public ConfigOptionImpl{ ResourceDownloadRow& option; }; +enum class ResourceVersion{ + CURRENT, + OUTDATED, + NOT_APPLICABLE, + BLANK, +}; + class ResourceDownloadRow : public StaticTableRow{ public: // ~ResourceDownloadRow(); ResourceDownloadRow( std::string&& resource_name, + size_t file_size, bool is_downloaded, - size_t file_size + ResourceVersion version ); +private: + std::string resource_version_to_string(ResourceVersion version); + +private: LabelCellOption m_resource_name; - bool m_is_downloaded; - LabelCellOption m_is_downloaded_label; + size_t m_file_size; LabelCellOption m_file_size_label; + + bool m_is_downloaded; + LabelCellOption m_is_downloaded_label; + + ResourceVersion m_version; + LabelCellOption m_version_label; + ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; @@ -67,6 +85,7 @@ class ResourceDownloadTable : public StaticTableOption{ virtual std::vector make_header() const override; +private: std::vector deserialize_resource_list_json(); std::vector> get_resource_download_rows(); void add_resource_download_rows(); From 56dcfd23000a806aba2fb2d3be47d8174858dbcb Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 15:37:44 -0700 Subject: [PATCH 09/53] add DOWNLOADED_RESOURCE_PATH() --- SerialPrograms/Source/CommonFramework/Globals.cpp | 4 ++++ SerialPrograms/Source/CommonFramework/Globals.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index f916b2f6eb..439d492553 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -220,6 +220,10 @@ const std::string& RESOURCE_PATH(){ static std::string path = get_resource_path(); return path; } +const std::string& DOWNLOADED_RESOURCE_PATH(){ + static std::string path = RUNTIME_BASE_PATH() + "DownloadedResources/"; + return path; +} const std::string& TRAINING_PATH(){ static std::string path = get_training_path(); return path; diff --git a/SerialPrograms/Source/CommonFramework/Globals.h b/SerialPrograms/Source/CommonFramework/Globals.h index 55ce1a91ce..754b585e14 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.h +++ b/SerialPrograms/Source/CommonFramework/Globals.h @@ -76,6 +76,10 @@ const std::string& USER_FILE_PATH(); // Resource folder path. Resources include JSON files, images, sound files and others required by // various automation programs. const std::string& RESOURCE_PATH(); + +// Folder path that holds Downloaded resources +const std::string& DOWNLOADED_RESOURCE_PATH(); + // Hold ML training data. const std::string& TRAINING_PATH(); From b18a9a9031261af4fda51c8d813136eeb1bc5c70 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 15:48:57 -0700 Subject: [PATCH 10/53] initial implementation of updating "Version" column on a separate thread. --- .../Options/LabelCellOption.cpp | 16 +++++++++- .../CommonFramework/Options/LabelCellOption.h | 2 ++ .../Options/QtWidget/LabelCellWidget.cpp | 13 ++++++++- .../Options/QtWidget/LabelCellWidget.h | 5 +++- .../ResourceDownloadTable.cpp | 29 ++++++++++++++++++- .../ResourceDownload/ResourceDownloadTable.h | 8 ++++- 6 files changed, 68 insertions(+), 5 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.cpp b/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.cpp index 66e6eb7416..3308b81f0a 100644 --- a/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.cpp +++ b/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.cpp @@ -6,6 +6,7 @@ #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Json/JsonValue.h" +#include "Common/Cpp/Concurrency/SpinLock.h" #include "LabelCellOption.h" //#include @@ -17,7 +18,7 @@ namespace PokemonAutomation{ struct LabelCellOption::Data{ -// mutable SpinLock m_lock; + mutable SpinLock m_lock; std::string m_text; // ImageRGB32 m_icon_owner; ImageViewRGB32 m_icon; @@ -80,6 +81,7 @@ LabelCellOption::LabelCellOption( // : m_data(CONSTRUCT_TOKEN, std::move(text), std::move(icon)) //{} const std::string& LabelCellOption::text() const{ + ReadSpinLock lg(m_data->m_lock); return m_data->m_text; } const ImageViewRGB32& LabelCellOption::icon() const{ @@ -94,6 +96,18 @@ JsonValue LabelCellOption::to_json() const{ return JsonValue(); } +void LabelCellOption::set_text(std::string x){ + // sanitize(x); + { + WriteSpinLock lg(m_data->m_lock); + if (m_data->m_text == x){ + return; + } + m_data->m_text = std::move(x); + } + report_value_changed(this); +} + diff --git a/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.h b/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.h index f7afe0b27e..300964d6d3 100644 --- a/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.h +++ b/SerialPrograms/Source/CommonFramework/Options/LabelCellOption.h @@ -45,6 +45,8 @@ class LabelCellOption : public ConfigOptionImpl{ const ImageViewRGB32& icon() const; Resolution resolution() const; + void set_text(std::string x); + virtual void load_json(const JsonValue& json) override; virtual JsonValue to_json() const override; diff --git a/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.cpp b/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.cpp index 4964284466..d43eca8839 100644 --- a/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.cpp @@ -24,7 +24,7 @@ LabelCellWidget::~LabelCellWidget(){ LabelCellWidget::LabelCellWidget(QWidget& parent, LabelCellOption& value) : QWidget(&parent) , ConfigWidget(value, *this) -// , m_value(value) + , m_value(value) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -49,6 +49,17 @@ LabelCellWidget::LabelCellWidget(QWidget& parent, LabelCellOption& value) layout->addWidget(m_text, 1); // text->setTextInteractionFlags(Qt::TextBrowserInteraction); // m_text->setOpenExternalLinks(true); + + m_value.add_listener(*this); +} + +void LabelCellWidget::update_value(){ + m_text->setText(QString::fromStdString(m_value.text())); +} +void LabelCellWidget::on_config_value_changed(void* object){ + QMetaObject::invokeMethod(m_text, [this]{ + update_value(); + }, Qt::QueuedConnection); } diff --git a/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.h b/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.h index 77dda8709b..14442fda62 100644 --- a/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.h +++ b/SerialPrograms/Source/CommonFramework/Options/QtWidget/LabelCellWidget.h @@ -25,8 +25,11 @@ class LabelCellWidget : public QWidget, public ConfigWidget{ ~LabelCellWidget(); LabelCellWidget(QWidget& parent, LabelCellOption& value); + virtual void update_value() override; + virtual void on_config_value_changed(void* object) override; + private: -// LabelCellOption& m_value; + LabelCellOption& m_value; QLabel* m_icon = nullptr; QLabel* m_text; }; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index d5c860b6fe..07d2c3b102 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -5,12 +5,18 @@ */ #include "CommonFramework/Globals.h" +#include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Json/JsonArray.h" #include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Filesystem.h" #include "ResourceDownloadTable.h" #include +#include + +#include +using std::cout; +using std::endl; namespace PokemonAutomation{ @@ -66,6 +72,10 @@ std::string ResourceDownloadRow::resource_version_to_string(ResourceVersion vers } } +ResourceDownloadTable::~ResourceDownloadTable(){ + m_worker.wait_and_ignore_exceptions(); +} + ResourceDownloadTable::ResourceDownloadTable() : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) , m_resources(deserialize_resource_list_json()) @@ -74,6 +84,13 @@ ResourceDownloadTable::ResourceDownloadTable() add_resource_download_rows(); finish_construction(); + + m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + // cout << "hello1" << endl; + check_all_resource_versions(); + } + ); } std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ @@ -137,7 +154,7 @@ std::vector> ResourceDownloadTable::get_res std::string resource_name = resource.resource_name; ResourceVersion version = ResourceVersion::BLANK; - Filesystem::Path filepath{RUNTIME_BASE_PATH() + "DownloadedResources/" + resource_name}; + Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name}; bool is_downloaded = std::filesystem::is_directory(filepath); resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, version)); @@ -153,6 +170,16 @@ void ResourceDownloadTable::add_resource_download_rows(){ } } +void ResourceDownloadTable::check_all_resource_versions(){ + + // test code + std::this_thread::sleep_for(std::chrono::seconds(5)); + + for (auto& row_ptr : m_resource_rows){ + row_ptr->m_version_label.set_text("Hi"); + } + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 6b937c6e5b..ebd1801ec8 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -9,6 +9,7 @@ #include "CommonFramework/Options/LabelCellOption.h" #include "Common/Cpp/Options/StaticTableOption.h" +#include "Common/Cpp/Concurrency/AsyncTask.h" namespace PokemonAutomation{ @@ -49,7 +50,7 @@ class ResourceDownloadRow : public StaticTableRow{ private: std::string resource_version_to_string(ResourceVersion version); -private: +public: LabelCellOption m_resource_name; size_t m_file_size; @@ -81,6 +82,7 @@ struct DownloadedResource{ class ResourceDownloadTable : public StaticTableOption{ public: + ~ResourceDownloadTable(); ResourceDownloadTable(); virtual std::vector make_header() const override; @@ -90,6 +92,8 @@ class ResourceDownloadTable : public StaticTableOption{ std::vector> get_resource_download_rows(); void add_resource_download_rows(); + void check_all_resource_versions(); + private: std::vector m_resources; @@ -97,6 +101,8 @@ class ResourceDownloadTable : public StaticTableOption{ // we need to keep a handle on each Row, so that we can edit m_is_downloaded_label later on. std::vector> m_resource_rows; + AsyncTask m_worker; + }; From 05158311a27135273e3ed27d56fed63523b40282 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 22:16:05 -0700 Subject: [PATCH 11/53] initialize worker thread outside of constructor --- .../ResourceDownload/ResourceDownloadTable.cpp | 17 ++++++++++------- .../ResourceDownload/ResourceDownloadTable.h | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 07d2c3b102..235c594789 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -84,13 +84,6 @@ ResourceDownloadTable::ResourceDownloadTable() add_resource_download_rows(); finish_construction(); - - m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - // cout << "hello1" << endl; - check_all_resource_versions(); - } - ); } std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ @@ -104,6 +97,16 @@ std::vector ResourceDownloadTable::make_header() const{ return ret; } +UiWrapper ResourceDownloadTable::make_UiComponent(void* params) { + m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + check_all_resource_versions(); + } + ); + + return ConfigOptionImpl::make_UiComponent(params); +} + ResourceType get_resource_type_from_string(std::string type){ if (type == "ZippedFolder"){ return ResourceType::ZIP_FILE; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index ebd1801ec8..6851c8acf1 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -86,6 +86,7 @@ class ResourceDownloadTable : public StaticTableOption{ ResourceDownloadTable(); virtual std::vector make_header() const override; + virtual UiWrapper make_UiComponent(void* params) override; private: std::vector deserialize_resource_list_json(); From b03ca41a425c5d793a1a92fbdeb4152c4ca86cc6 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 22:45:04 -0700 Subject: [PATCH 12/53] convert member fields to PIMPL --- .../ResourceDownloadTable.cpp | 51 ++++++++++++++----- .../ResourceDownload/ResourceDownloadTable.h | 21 +++----- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 235c594789..796d0b777e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -6,6 +6,8 @@ #include "CommonFramework/Globals.h" #include "CommonFramework/Tools/GlobalThreadPools.h" +#include "CommonFramework/Options/LabelCellOption.h" +#include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Json/JsonArray.h" #include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Filesystem.h" @@ -30,7 +32,38 @@ ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) , option(p_row) {} +struct ResourceDownloadRow::Data{ + Data( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + ResourceVersion version + ) + : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) + , m_file_size(file_size) + , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) + , m_is_downloaded(is_downloaded) + , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") + , m_version(version) + , m_version_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version)) + {} + LabelCellOption m_resource_name; + + size_t m_file_size; + LabelCellOption m_file_size_label; + + bool m_is_downloaded; + LabelCellOption m_is_downloaded_label; + + ResourceVersion m_version; + LabelCellOption m_version_label; + + +}; + + +ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, size_t file_size, @@ -38,20 +71,14 @@ ResourceDownloadRow::ResourceDownloadRow( ResourceVersion version ) : StaticTableRow(resource_name) - , m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) - , m_file_size(file_size) - , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) - , m_is_downloaded(is_downloaded) - , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") - , m_version(version) - , m_version_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version)) + , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version) , m_download_button(*this) , m_delete_button(*this) { - PA_ADD_STATIC(m_resource_name); - PA_ADD_STATIC(m_file_size_label); - PA_ADD_STATIC(m_is_downloaded_label); - PA_ADD_STATIC(m_version_label); + PA_ADD_STATIC(m_data->m_resource_name); + PA_ADD_STATIC(m_data->m_file_size_label); + PA_ADD_STATIC(m_data->m_is_downloaded_label); + PA_ADD_STATIC(m_data->m_version_label); PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); @@ -179,7 +206,7 @@ void ResourceDownloadTable::check_all_resource_versions(){ std::this_thread::sleep_for(std::chrono::seconds(5)); for (auto& row_ptr : m_resource_rows){ - row_ptr->m_version_label.set_text("Hi"); + row_ptr->m_data->m_version_label.set_text("Hi"); } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 6851c8acf1..c5f33d96f2 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -7,9 +7,9 @@ #ifndef PokemonAutomation_ResourceDownloadTable_H #define PokemonAutomation_ResourceDownloadTable_H -#include "CommonFramework/Options/LabelCellOption.h" -#include "Common/Cpp/Options/StaticTableOption.h" #include "Common/Cpp/Concurrency/AsyncTask.h" +#include "Common/Cpp/Containers/Pimpl.h" +#include "Common/Cpp/Options/StaticTableOption.h" namespace PokemonAutomation{ @@ -39,7 +39,7 @@ enum class ResourceVersion{ class ResourceDownloadRow : public StaticTableRow{ public: - // ~ResourceDownloadRow(); + ~ResourceDownloadRow(); ResourceDownloadRow( std::string&& resource_name, size_t file_size, @@ -48,24 +48,15 @@ class ResourceDownloadRow : public StaticTableRow{ ); private: - std::string resource_version_to_string(ResourceVersion version); + static std::string resource_version_to_string(ResourceVersion version); public: - LabelCellOption m_resource_name; - - size_t m_file_size; - LabelCellOption m_file_size_label; - - bool m_is_downloaded; - LabelCellOption m_is_downloaded_label; - - ResourceVersion m_version; - LabelCellOption m_version_label; + struct Data; + Pimpl m_data; ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; - }; From 4541a37fd047e872090ddf678ebbabb50e898561 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 18 Mar 2026 21:51:25 -0700 Subject: [PATCH 13/53] add version number to the JSON --- .../ResourceDownloadTable.cpp | 84 +++++++++++++------ .../ResourceDownload/ResourceDownloadTable.h | 24 +++--- 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 796d0b777e..7be2ae8752 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -5,7 +5,10 @@ */ #include "CommonFramework/Globals.h" +#include "CommonFramework/Logging/Logger.h" #include "CommonFramework/Tools/GlobalThreadPools.h" +#include "CommonFramework/Tools/FileDownloader.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/Options/LabelCellOption.h" #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Json/JsonArray.h" @@ -24,12 +27,12 @@ namespace PokemonAutomation{ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , option(p_row) + , row(p_row) {} ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , option(p_row) + , row(p_row) {} struct ResourceDownloadRow::Data{ @@ -37,15 +40,16 @@ struct ResourceDownloadRow::Data{ std::string&& resource_name, size_t file_size, bool is_downloaded, - ResourceVersion version + size_t version_num ) : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) , m_is_downloaded(is_downloaded) , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") - , m_version(version) - , m_version_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version)) + , m_version_num(version_num) + , m_version_status(ResourceVersionStatus::BLANK) + , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(m_version_status)) {} LabelCellOption m_resource_name; @@ -56,8 +60,9 @@ struct ResourceDownloadRow::Data{ bool m_is_downloaded; LabelCellOption m_is_downloaded_label; - ResourceVersion m_version; - LabelCellOption m_version_label; + size_t m_version_num; + ResourceVersionStatus m_version_status; + LabelCellOption m_version_status_label; }; @@ -68,31 +73,31 @@ ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, size_t file_size, bool is_downloaded, - ResourceVersion version + size_t version_num ) : StaticTableRow(resource_name) - , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version) + , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num) , m_download_button(*this) , m_delete_button(*this) { PA_ADD_STATIC(m_data->m_resource_name); PA_ADD_STATIC(m_data->m_file_size_label); PA_ADD_STATIC(m_data->m_is_downloaded_label); - PA_ADD_STATIC(m_data->m_version_label); + PA_ADD_STATIC(m_data->m_version_status_label); PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); } -std::string ResourceDownloadRow::resource_version_to_string(ResourceVersion version){ +std::string ResourceDownloadRow::resource_version_to_string(ResourceVersionStatus version){ switch(version){ - case ResourceVersion::CURRENT: + case ResourceVersionStatus::CURRENT: return "Current"; - case ResourceVersion::OUTDATED: + case ResourceVersionStatus::OUTDATED: return "Outdated"; - case ResourceVersion::NOT_APPLICABLE: + case ResourceVersionStatus::NOT_APPLICABLE: return "--"; - case ResourceVersion::BLANK: + case ResourceVersionStatus::BLANK: return ""; default: throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "resource_version_to_string: Unknown enum."); @@ -105,7 +110,7 @@ ResourceDownloadTable::~ResourceDownloadTable(){ ResourceDownloadTable::ResourceDownloadTable() : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) - , m_resources(deserialize_resource_list_json()) + , m_resources(deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json"))) , m_resource_rows(get_resource_download_rows()) { add_resource_download_rows(); @@ -138,14 +143,13 @@ ResourceType get_resource_type_from_string(std::string type){ if (type == "ZippedFolder"){ return ResourceType::ZIP_FILE; }else{ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_resource_type_from_string: Unknown enum."); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_resource_type_from_string: Unknown string."); } } -std::vector ResourceDownloadTable::deserialize_resource_list_json(){ - std::vector resources; - JsonValue json = load_json_file(RESOURCE_PATH() + "ResourceList.json"); +std::vector ResourceDownloadTable::deserialize_resource_list_json(const JsonValue& json){ + std::vector resources; try{ const JsonObject& obj = json.to_object_throw(); @@ -154,6 +158,7 @@ std::vector ResourceDownloadTable::deserialize_resource_list const JsonObject& resource_obj = resource_val.to_object_throw(); std::string resource_name = resource_obj.get_string_throw("resourceName"); + size_t version_num = resource_obj.get_integer_throw("version"); ResourceType resource_type = get_resource_type_from_string(resource_obj.get_string_throw("Type")); size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); @@ -161,6 +166,7 @@ std::vector ResourceDownloadTable::deserialize_resource_list DownloadedResource resource = { resource_name, + version_num, resource_type, compressed_bytes, decompressed_bytes, @@ -182,12 +188,13 @@ std::vector> ResourceDownloadTable::get_res std::vector> resource_rows; for (const DownloadedResource& resource : m_resources){ std::string resource_name = resource.resource_name; - ResourceVersion version = ResourceVersion::BLANK; + size_t version_num = resource.version_num; - Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name}; + Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num)}; bool is_downloaded = std::filesystem::is_directory(filepath); + cout << DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num) << endl; - resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, version)); + resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, version_num)); } return resource_rows; @@ -200,13 +207,42 @@ void ResourceDownloadTable::add_resource_download_rows(){ } } +const JsonObject& fetch_resource_download_list_json_from_remote(){ + Logger& logger = global_logger_tagged(); + JsonValue json; + try{ + json = FileDownloader::download_json_file( + logger, + "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" + ); + }catch (OperationFailedException&){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); + } + const JsonObject* obj = json.to_object(); + if (obj == nullptr){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Invalid JSON."); + } + + return *obj; +} + +const JsonObject& remote_resource_download_list_json(){ + static const JsonObject& json = fetch_resource_download_list_json_from_remote(); + + return json; +} + void ResourceDownloadTable::check_all_resource_versions(){ + const JsonObject& json_obj = remote_resource_download_list_json(); + json_obj.get_string_throw("hi"); + + // const JsonArray& resource_list = json_obj.get_array_throw("resourceList"); // test code std::this_thread::sleep_for(std::chrono::seconds(5)); for (auto& row_ptr : m_resource_rows){ - row_ptr->m_data->m_version_label.set_text("Hi"); + row_ptr->m_data->m_version_status_label.set_text("Hi"); } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index c5f33d96f2..d731a07ac0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -13,27 +13,28 @@ namespace PokemonAutomation{ - +class JsonValue; class ResourceDownloadRow; class ResourceDownloadButton : public ConfigOptionImpl{ public: ResourceDownloadButton(ResourceDownloadRow& p_row); - ResourceDownloadRow& option; + ResourceDownloadRow& row; }; class ResourceDeleteButton : public ConfigOptionImpl{ public: ResourceDeleteButton(ResourceDownloadRow& p_row); - ResourceDownloadRow& option; + ResourceDownloadRow& row; }; -enum class ResourceVersion{ +enum class ResourceVersionStatus{ CURRENT, - OUTDATED, - NOT_APPLICABLE, - BLANK, + OUTDATED, // still used, but newer version available + RETIRED, // no longer used + NOT_APPLICABLE, // resource not downloaded locally, so can't get its version + BLANK, // not yet fetched version info from remote }; class ResourceDownloadRow : public StaticTableRow{ @@ -44,11 +45,11 @@ class ResourceDownloadRow : public StaticTableRow{ std::string&& resource_name, size_t file_size, bool is_downloaded, - ResourceVersion version + size_t version_num ); private: - static std::string resource_version_to_string(ResourceVersion version); + static std::string resource_version_to_string(ResourceVersionStatus version); public: struct Data; @@ -65,6 +66,7 @@ enum class ResourceType{ }; struct DownloadedResource{ std::string resource_name; + size_t version_num; ResourceType resource_type; size_t size_compressed_bytes; size_t size_decompressed_bytes; @@ -79,8 +81,8 @@ class ResourceDownloadTable : public StaticTableOption{ virtual std::vector make_header() const override; virtual UiWrapper make_UiComponent(void* params) override; -private: - std::vector deserialize_resource_list_json(); +private: + std::vector deserialize_resource_list_json(const JsonValue& json); std::vector> get_resource_download_rows(); void add_resource_download_rows(); From db182f8ae10248b92b7b676cab018e5382e5f2d7 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 19 Mar 2026 15:08:28 -0700 Subject: [PATCH 14/53] update version_num, version_status for each row --- .../ResourceDownloadTable.cpp | 164 +++++++++++------- .../ResourceDownload/ResourceDownloadTable.h | 10 +- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 7be2ae8752..ddd36f2abf 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -18,6 +18,7 @@ #include #include +// #include #include using std::cout; @@ -40,7 +41,8 @@ struct ResourceDownloadRow::Data{ std::string&& resource_name, size_t file_size, bool is_downloaded, - size_t version_num + std::optional version_num, + ResourceVersionStatus version_status ) : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) , m_file_size(file_size) @@ -48,8 +50,8 @@ struct ResourceDownloadRow::Data{ , m_is_downloaded(is_downloaded) , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") , m_version_num(version_num) - , m_version_status(ResourceVersionStatus::BLANK) - , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(m_version_status)) + , m_version_status(version_status) + , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) {} LabelCellOption m_resource_name; @@ -60,7 +62,7 @@ struct ResourceDownloadRow::Data{ bool m_is_downloaded; LabelCellOption m_is_downloaded_label; - size_t m_version_num; + std::optional m_version_num; ResourceVersionStatus m_version_status; LabelCellOption m_version_status_label; @@ -73,10 +75,11 @@ ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, size_t file_size, bool is_downloaded, - size_t version_num + std::optional version_num, + ResourceVersionStatus version_status ) : StaticTableRow(resource_name) - , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num) + , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) , m_download_button(*this) , m_delete_button(*this) { @@ -99,46 +102,13 @@ std::string ResourceDownloadRow::resource_version_to_string(ResourceVersionStatu return "--"; case ResourceVersionStatus::BLANK: return ""; + case ResourceVersionStatus::FUTURE_VERSION: + return "Unsupported future version.
Please update the Computer Control program."; default: throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "resource_version_to_string: Unknown enum."); } } -ResourceDownloadTable::~ResourceDownloadTable(){ - m_worker.wait_and_ignore_exceptions(); -} - -ResourceDownloadTable::ResourceDownloadTable() - : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) - , m_resources(deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json"))) - , m_resource_rows(get_resource_download_rows()) -{ - add_resource_download_rows(); - - finish_construction(); -} -std::vector ResourceDownloadTable::make_header() const{ - std::vector ret{ - "Resource", - "Size (MB)", - "Downloaded", - "Version", - "", - "", - }; - return ret; -} - -UiWrapper ResourceDownloadTable::make_UiComponent(void* params) { - m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - check_all_resource_versions(); - } - ); - - return ConfigOptionImpl::make_UiComponent(params); -} - ResourceType get_resource_type_from_string(std::string type){ if (type == "ZippedFolder"){ return ResourceType::ZIP_FILE; @@ -148,7 +118,7 @@ ResourceType get_resource_type_from_string(std::string type){ } -std::vector ResourceDownloadTable::deserialize_resource_list_json(const JsonValue& json){ +std::vector deserialize_resource_list_json(const JsonValue& json){ std::vector resources; try{ @@ -158,7 +128,7 @@ std::vector ResourceDownloadTable::deserialize_resource_list const JsonObject& resource_obj = resource_val.to_object_throw(); std::string resource_name = resource_obj.get_string_throw("resourceName"); - size_t version_num = resource_obj.get_integer_throw("version"); + std::optional version_num = (uint16_t)resource_obj.get_integer_throw("version"); ResourceType resource_type = get_resource_type_from_string(resource_obj.get_string_throw("Type")); size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); @@ -184,17 +154,91 @@ std::vector ResourceDownloadTable::deserialize_resource_list return resources; } + +std::vector local_resource_download_list(){ + static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); + + return local_resources; +} + +ResourceDownloadTable::~ResourceDownloadTable(){ + m_worker.wait_and_ignore_exceptions(); +} + +ResourceDownloadTable::ResourceDownloadTable() + : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) + , m_resource_rows(get_resource_download_rows()) +{ + add_resource_download_rows(); + + finish_construction(); +} +std::vector ResourceDownloadTable::make_header() const{ + std::vector ret{ + "Resource", + "Size (MB)", + "Downloaded", + "Version", + "", + "", + }; + return ret; +} + +UiWrapper ResourceDownloadTable::make_UiComponent(void* params) { + m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + check_all_resource_versions(); + } + ); + + return ConfigOptionImpl::make_UiComponent(params); +} + + +uint16_t get_resource_version_num(Filesystem::Path folder_path){ + std::string file_name = folder_path.string() + "/version.json"; + const JsonValue& json = load_json_file(file_name); + + const JsonObject& obj = json.to_object_throw(); + uint16_t version_num = (uint16_t)obj.get_integer_throw("version"); + + return version_num; +} + +ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num){ + if (!current_version_num.has_value()){ + return ResourceVersionStatus::NOT_APPLICABLE; + } + + if (current_version_num < expected_version_num){ + return ResourceVersionStatus::OUTDATED; + }else if (current_version_num == expected_version_num){ + return ResourceVersionStatus::CURRENT; + }else{ // current > expected + return ResourceVersionStatus::FUTURE_VERSION; + } +} + std::vector> ResourceDownloadTable::get_resource_download_rows(){ std::vector> resource_rows; - for (const DownloadedResource& resource : m_resources){ + for (const DownloadedResource& resource : local_resource_download_list()){ std::string resource_name = resource.resource_name; - size_t version_num = resource.version_num; + uint16_t expected_version_num = resource.version_num.value(); + std::optional current_version_num; // default nullopt + ResourceVersionStatus version_status = ResourceVersionStatus::BLANK; - Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num)}; + Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name}; bool is_downloaded = std::filesystem::is_directory(filepath); - cout << DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num) << endl; + if (is_downloaded){ + current_version_num = get_resource_version_num(filepath); + } + + version_status = get_version_status(expected_version_num, current_version_num); + + // cout << DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num) << endl; - resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, version_num)); + resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, current_version_num, version_status)); } return resource_rows; @@ -207,7 +251,7 @@ void ResourceDownloadTable::add_resource_download_rows(){ } } -const JsonObject& fetch_resource_download_list_json_from_remote(){ +JsonValue fetch_resource_download_list_json_from_remote(){ Logger& logger = global_logger_tagged(); JsonValue json; try{ @@ -218,23 +262,27 @@ const JsonObject& fetch_resource_download_list_json_from_remote(){ }catch (OperationFailedException&){ throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); } - const JsonObject* obj = json.to_object(); - if (obj == nullptr){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Invalid JSON."); - } - return *obj; + return json; } -const JsonObject& remote_resource_download_list_json(){ - static const JsonObject& json = fetch_resource_download_list_json_from_remote(); +const JsonValue& remote_resource_download_list_json(){ + static const JsonValue json = fetch_resource_download_list_json_from_remote(); return json; } +std::vector remote_resource_download_list(){ + static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); + + return remote_resources; +} + + void ResourceDownloadTable::check_all_resource_versions(){ - const JsonObject& json_obj = remote_resource_download_list_json(); - json_obj.get_string_throw("hi"); + std::vector remote_resources = remote_resource_download_list(); + + // const JsonArray& resource_list = json_obj.get_array_throw("resourceList"); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index d731a07ac0..2c67256453 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -10,6 +10,7 @@ #include "Common/Cpp/Concurrency/AsyncTask.h" #include "Common/Cpp/Containers/Pimpl.h" #include "Common/Cpp/Options/StaticTableOption.h" +#include namespace PokemonAutomation{ @@ -35,6 +36,7 @@ enum class ResourceVersionStatus{ RETIRED, // no longer used NOT_APPLICABLE, // resource not downloaded locally, so can't get its version BLANK, // not yet fetched version info from remote + FUTURE_VERSION, // current version number is greater than the expected version number }; class ResourceDownloadRow : public StaticTableRow{ @@ -45,7 +47,8 @@ class ResourceDownloadRow : public StaticTableRow{ std::string&& resource_name, size_t file_size, bool is_downloaded, - size_t version_num + std::optional version_num, + ResourceVersionStatus version_status ); private: @@ -66,7 +69,7 @@ enum class ResourceType{ }; struct DownloadedResource{ std::string resource_name; - size_t version_num; + std::optional version_num; ResourceType resource_type; size_t size_compressed_bytes; size_t size_decompressed_bytes; @@ -82,7 +85,6 @@ class ResourceDownloadTable : public StaticTableOption{ virtual UiWrapper make_UiComponent(void* params) override; private: - std::vector deserialize_resource_list_json(const JsonValue& json); std::vector> get_resource_download_rows(); void add_resource_download_rows(); @@ -90,8 +92,6 @@ class ResourceDownloadTable : public StaticTableOption{ private: - std::vector m_resources; - // we need to keep a handle on each Row, so that we can edit m_is_downloaded_label later on. std::vector> m_resource_rows; From 6e0bc14cf358982f9193abf63c9bc9037710d54e Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 19 Mar 2026 21:27:44 -0700 Subject: [PATCH 15/53] refactor Row class into its own file --- .../ResourceDownload/ResourceDownloadRow.cpp | 106 +++++++++ .../ResourceDownload/ResourceDownloadRow.h | 64 +++++ .../ResourceDownloadTable.cpp | 221 ++++++------------ .../ResourceDownload/ResourceDownloadTable.h | 58 +---- SerialPrograms/cmake/SourceFiles.cmake | 2 + 5 files changed, 244 insertions(+), 207 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp new file mode 100644 index 0000000000..650ee58d07 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -0,0 +1,106 @@ +/* Resource Download Row + * + * From: https://github.com/PokemonAutomation/ + * + */ + + + +#include "Common/Cpp/Containers/Pimpl.tpp" +#include "Common/Cpp/Exceptions.h" +#include "CommonFramework/Options/LabelCellOption.h" +#include "ResourceDownloadRow.h" + + +// #include +// using std::cout; +// using std::endl; + +namespace PokemonAutomation{ + + +ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + +ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + +std::string resource_version_to_string(ResourceVersionStatus version){ + switch(version){ + case ResourceVersionStatus::CURRENT: + return "Current"; + case ResourceVersionStatus::OUTDATED: + return "Outdated"; + case ResourceVersionStatus::NOT_APPLICABLE: + return "--"; + // case ResourceVersionStatus::BLANK: + // return ""; + case ResourceVersionStatus::FUTURE_VERSION: + return "Unsupported future version.
Please update the Computer Control program."; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "resource_version_to_string: Unknown enum."); + } +} + +struct ResourceDownloadRow::Data{ + Data( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + std::optional version_num, + ResourceVersionStatus version_status + ) + : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) + , m_file_size(file_size) + , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) + , m_is_downloaded(is_downloaded) + , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") + , m_version_num(version_num) + , m_version_status(version_status) + , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) + {} + + LabelCellOption m_resource_name; + + size_t m_file_size; + LabelCellOption m_file_size_label; + + bool m_is_downloaded; + LabelCellOption m_is_downloaded_label; + + std::optional m_version_num; + ResourceVersionStatus m_version_status; + LabelCellOption m_version_status_label; + + +}; + + +ResourceDownloadRow::~ResourceDownloadRow(){} +ResourceDownloadRow::ResourceDownloadRow( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + std::optional version_num, + ResourceVersionStatus version_status +) + : StaticTableRow(resource_name) + , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) + , m_download_button(*this) + , m_delete_button(*this) +{ + PA_ADD_STATIC(m_data->m_resource_name); + PA_ADD_STATIC(m_data->m_file_size_label); + PA_ADD_STATIC(m_data->m_is_downloaded_label); + PA_ADD_STATIC(m_data->m_version_status_label); + + PA_ADD_STATIC(m_download_button); + PA_ADD_STATIC(m_delete_button); +} + + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h new file mode 100644 index 0000000000..23020ed843 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -0,0 +1,64 @@ +/* Resource Download Row + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_ResourceDownloadRow_H +#define PokemonAutomation_ResourceDownloadRow_H + +#include "Common/Cpp/Containers/Pimpl.h" +#include "Common/Cpp/Options/StaticTableOption.h" +#include + + +namespace PokemonAutomation{ + +class ResourceDownloadRow; + +class ResourceDownloadButton : public ConfigOptionImpl{ +public: + ResourceDownloadButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; +}; + +class ResourceDeleteButton : public ConfigOptionImpl{ +public: + ResourceDeleteButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; +}; + +enum class ResourceVersionStatus{ + CURRENT, + OUTDATED, // still used, but newer version available + FUTURE_VERSION, // current version number is greater than the expected version number + NOT_APPLICABLE, // resource not downloaded locally, so can't get its version + // RETIRED, // no longer used + // BLANK, // not yet fetched version info from remote +}; + +class ResourceDownloadRow : public StaticTableRow{ + +public: + ~ResourceDownloadRow(); + ResourceDownloadRow( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + std::optional version_num, + ResourceVersionStatus version_status + ); + +public: + struct Data; + Pimpl m_data; + + ResourceDownloadButton m_download_button; + ResourceDeleteButton m_delete_button; + +}; + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index ddd36f2abf..b93afc382d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -6,18 +6,16 @@ #include "CommonFramework/Globals.h" #include "CommonFramework/Logging/Logger.h" -#include "CommonFramework/Tools/GlobalThreadPools.h" +// #include "CommonFramework/Tools/GlobalThreadPools.h" #include "CommonFramework/Tools/FileDownloader.h" #include "CommonFramework/Exceptions/OperationFailedException.h" -#include "CommonFramework/Options/LabelCellOption.h" -#include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Json/JsonArray.h" #include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Filesystem.h" #include "ResourceDownloadTable.h" #include -#include +// #include // #include #include @@ -26,88 +24,6 @@ using std::endl; namespace PokemonAutomation{ -ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) -{} - -ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) -{} - -struct ResourceDownloadRow::Data{ - Data( - std::string&& resource_name, - size_t file_size, - bool is_downloaded, - std::optional version_num, - ResourceVersionStatus version_status - ) - : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) - , m_file_size(file_size) - , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) - , m_is_downloaded(is_downloaded) - , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") - , m_version_num(version_num) - , m_version_status(version_status) - , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) - {} - - LabelCellOption m_resource_name; - - size_t m_file_size; - LabelCellOption m_file_size_label; - - bool m_is_downloaded; - LabelCellOption m_is_downloaded_label; - - std::optional m_version_num; - ResourceVersionStatus m_version_status; - LabelCellOption m_version_status_label; - - -}; - - -ResourceDownloadRow::~ResourceDownloadRow(){} -ResourceDownloadRow::ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, - bool is_downloaded, - std::optional version_num, - ResourceVersionStatus version_status -) - : StaticTableRow(resource_name) - , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) - , m_download_button(*this) - , m_delete_button(*this) -{ - PA_ADD_STATIC(m_data->m_resource_name); - PA_ADD_STATIC(m_data->m_file_size_label); - PA_ADD_STATIC(m_data->m_is_downloaded_label); - PA_ADD_STATIC(m_data->m_version_status_label); - - PA_ADD_STATIC(m_download_button); - PA_ADD_STATIC(m_delete_button); -} - -std::string ResourceDownloadRow::resource_version_to_string(ResourceVersionStatus version){ - switch(version){ - case ResourceVersionStatus::CURRENT: - return "Current"; - case ResourceVersionStatus::OUTDATED: - return "Outdated"; - case ResourceVersionStatus::NOT_APPLICABLE: - return "--"; - case ResourceVersionStatus::BLANK: - return ""; - case ResourceVersionStatus::FUTURE_VERSION: - return "Unsupported future version.
Please update the Computer Control program."; - default: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "resource_version_to_string: Unknown enum."); - } -} ResourceType get_resource_type_from_string(std::string type){ if (type == "ZippedFolder"){ @@ -161,41 +77,34 @@ std::vector local_resource_download_list(){ return local_resources; } -ResourceDownloadTable::~ResourceDownloadTable(){ - m_worker.wait_and_ignore_exceptions(); + +JsonValue fetch_resource_download_list_json_from_remote(){ + Logger& logger = global_logger_tagged(); + JsonValue json; + try{ + json = FileDownloader::download_json_file( + logger, + "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" + ); + }catch (OperationFailedException&){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); + } + + return json; } -ResourceDownloadTable::ResourceDownloadTable() - : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) - , m_resource_rows(get_resource_download_rows()) -{ - add_resource_download_rows(); +const JsonValue& remote_resource_download_list_json(){ + static const JsonValue json = fetch_resource_download_list_json_from_remote(); - finish_construction(); -} -std::vector ResourceDownloadTable::make_header() const{ - std::vector ret{ - "Resource", - "Size (MB)", - "Downloaded", - "Version", - "", - "", - }; - return ret; + return json; } -UiWrapper ResourceDownloadTable::make_UiComponent(void* params) { - m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - check_all_resource_versions(); - } - ); +std::vector remote_resource_download_list(){ + static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); - return ConfigOptionImpl::make_UiComponent(params); + return remote_resources; } - uint16_t get_resource_version_num(Filesystem::Path folder_path){ std::string file_name = folder_path.string() + "/version.json"; const JsonValue& json = load_json_file(file_name); @@ -220,13 +129,14 @@ ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::opt } } -std::vector> ResourceDownloadTable::get_resource_download_rows(){ + + +std::vector> get_resource_download_rows(){ std::vector> resource_rows; for (const DownloadedResource& resource : local_resource_download_list()){ std::string resource_name = resource.resource_name; uint16_t expected_version_num = resource.version_num.value(); std::optional current_version_num; // default nullopt - ResourceVersionStatus version_status = ResourceVersionStatus::BLANK; Filesystem::Path filepath{DOWNLOADED_RESOURCE_PATH() + resource_name}; bool is_downloaded = std::filesystem::is_directory(filepath); @@ -234,9 +144,7 @@ std::vector> ResourceDownloadTable::get_res current_version_num = get_resource_version_num(filepath); } - version_status = get_version_status(expected_version_num, current_version_num); - - // cout << DOWNLOADED_RESOURCE_PATH() + resource_name + "-v" + std::to_string(version_num) << endl; + ResourceVersionStatus version_status = get_version_status(expected_version_num, current_version_num); resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, current_version_num, version_status)); } @@ -245,55 +153,66 @@ std::vector> ResourceDownloadTable::get_res } -void ResourceDownloadTable::add_resource_download_rows(){ - for (auto& row_ptr : m_resource_rows){ - add_row(row_ptr.get()); - } -} -JsonValue fetch_resource_download_list_json_from_remote(){ - Logger& logger = global_logger_tagged(); - JsonValue json; - try{ - json = FileDownloader::download_json_file( - logger, - "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" - ); - }catch (OperationFailedException&){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); - } - - return json; + +ResourceDownloadTable::~ResourceDownloadTable(){ + m_worker.wait_and_ignore_exceptions(); } -const JsonValue& remote_resource_download_list_json(){ - static const JsonValue json = fetch_resource_download_list_json_from_remote(); +ResourceDownloadTable::ResourceDownloadTable() + : StaticTableOption("Resource Downloading:
Download resources not included in the initial download of the program.", LockMode::LOCK_WHILE_RUNNING, false) + , m_resource_rows(get_resource_download_rows()) +{ + add_resource_download_rows(); - return json; + finish_construction(); +} +std::vector ResourceDownloadTable::make_header() const{ + std::vector ret{ + "Resource", + "Size (MB)", + "Downloaded", + "Version", + "", + "", + }; + return ret; } -std::vector remote_resource_download_list(){ - static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); +// UiWrapper ResourceDownloadTable::make_UiComponent(void* params) { +// m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( +// [this]{ +// check_all_resource_versions(); +// } +// ); - return remote_resources; +// return ConfigOptionImpl::make_UiComponent(params); +// } + +void ResourceDownloadTable::add_resource_download_rows(){ + for (auto& row_ptr : m_resource_rows){ + add_row(row_ptr.get()); + } } -void ResourceDownloadTable::check_all_resource_versions(){ - std::vector remote_resources = remote_resource_download_list(); +// void ResourceDownloadTable::check_all_resource_versions(){ +// std::vector remote_resources = remote_resource_download_list(); - // const JsonArray& resource_list = json_obj.get_array_throw("resourceList"); - // test code - std::this_thread::sleep_for(std::chrono::seconds(5)); - for (auto& row_ptr : m_resource_rows){ - row_ptr->m_data->m_version_status_label.set_text("Hi"); - } +// // const JsonArray& resource_list = json_obj.get_array_throw("resourceList"); -} +// // test code +// std::this_thread::sleep_for(std::chrono::seconds(5)); + +// for (auto& row_ptr : m_resource_rows){ +// row_ptr->m_data->m_version_status_label.set_text("Hi"); +// } + +// } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 2c67256453..46abd2142d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -8,61 +8,10 @@ #define PokemonAutomation_ResourceDownloadTable_H #include "Common/Cpp/Concurrency/AsyncTask.h" -#include "Common/Cpp/Containers/Pimpl.h" -#include "Common/Cpp/Options/StaticTableOption.h" -#include +#include "ResourceDownloadRow.h" namespace PokemonAutomation{ -class JsonValue; -class ResourceDownloadRow; -class ResourceDownloadButton : public ConfigOptionImpl{ -public: - ResourceDownloadButton(ResourceDownloadRow& p_row); - - ResourceDownloadRow& row; -}; - -class ResourceDeleteButton : public ConfigOptionImpl{ -public: - ResourceDeleteButton(ResourceDownloadRow& p_row); - - ResourceDownloadRow& row; -}; - -enum class ResourceVersionStatus{ - CURRENT, - OUTDATED, // still used, but newer version available - RETIRED, // no longer used - NOT_APPLICABLE, // resource not downloaded locally, so can't get its version - BLANK, // not yet fetched version info from remote - FUTURE_VERSION, // current version number is greater than the expected version number -}; - -class ResourceDownloadRow : public StaticTableRow{ - -public: - ~ResourceDownloadRow(); - ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, - bool is_downloaded, - std::optional version_num, - ResourceVersionStatus version_status - ); - -private: - static std::string resource_version_to_string(ResourceVersionStatus version); - -public: - struct Data; - Pimpl m_data; - - ResourceDownloadButton m_download_button; - ResourceDeleteButton m_delete_button; - -}; - enum class ResourceType{ ZIP_FILE, @@ -82,14 +31,11 @@ class ResourceDownloadTable : public StaticTableOption{ ResourceDownloadTable(); virtual std::vector make_header() const override; - virtual UiWrapper make_UiComponent(void* params) override; + // virtual UiWrapper make_UiComponent(void* params) override; private: - std::vector> get_resource_download_rows(); void add_resource_download_rows(); - void check_all_resource_versions(); - private: // we need to keep a handle on each Row, so that we can edit m_is_downloaded_label later on. diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 125e99e017..d095b93727 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -487,6 +487,8 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Recording/StreamHistoryTracker_SaveFrames.h Source/CommonFramework/Recording/StreamRecorder.cpp Source/CommonFramework/Recording/StreamRecorder.h + Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp + Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp From 5388ba5ef10bea336c21c98e715354aad56207ab Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 20 Mar 2026 13:03:38 -0700 Subject: [PATCH 16/53] clicking Download button disables it until action is done --- .../ResourceDownload/ResourceDownloadRow.cpp | 26 +++++++++-- .../ResourceDownload/ResourceDownloadRow.h | 24 +++++++++- .../ResourceDownloadWidget.cpp | 45 ++++++++++++++++--- .../ResourceDownload/ResourceDownloadWidget.h | 9 +++- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 650ee58d07..1389bfb487 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -11,19 +11,39 @@ #include "CommonFramework/Options/LabelCellOption.h" #include "ResourceDownloadRow.h" +#include -// #include -// using std::cout; -// using std::endl; +#include +using std::cout; +using std::endl; namespace PokemonAutomation{ +ResourceDownloadButton::~ResourceDownloadButton(){ + m_worker.wait_and_ignore_exceptions(); +} + ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) + , m_enabled(true) {} + +void ResourceDownloadButton::run_download(){ + m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + std::this_thread::sleep_for(std::chrono::seconds(7)); + cout << "Clicked Download Button" << endl; + + m_enabled = true; + emit download_finished(); + } + ); + +} + ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 23020ed843..7a1c1a32e8 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -7,7 +7,10 @@ #ifndef PokemonAutomation_ResourceDownloadRow_H #define PokemonAutomation_ResourceDownloadRow_H +#include #include "Common/Cpp/Containers/Pimpl.h" +#include "Common/Cpp/Concurrency/AsyncTask.h" +#include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" #include @@ -16,11 +19,30 @@ namespace PokemonAutomation{ class ResourceDownloadRow; -class ResourceDownloadButton : public ConfigOptionImpl{ +class ResourceDownloadButton : public QObject, public ConfigOptionImpl{ + Q_OBJECT public: + ~ResourceDownloadButton(); ResourceDownloadButton(ResourceDownloadRow& p_row); +signals: + void download_finished(); + +public: + void run_download(); + inline bool get_enabled(){ return m_enabled; } + inline void set_enabled(bool enabled){ + m_enabled = enabled; + } + +public: ResourceDownloadRow& row; + +private: + bool m_enabled; // button should be blocked during an active task. m_enabled is false when blocked + AsyncTask m_worker; + + }; class ResourceDeleteButton : public ConfigOptionImpl{ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index a894423ce3..1893c55a9e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -5,8 +5,11 @@ */ #include +#include #include +#include #include "CommonFramework/Logging/Logger.h" + #include "CommonFramework/Notifications/ProgramNotifications.h" #include "ResourceDownloadWidget.h" @@ -19,9 +22,12 @@ namespace PokemonAutomation{ template class RegisterConfigWidget; - +DownloadButtonWidget::~DownloadButtonWidget(){ +} DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) - : ConfigWidget(value) + : QWidget(&parent) + , ConfigWidget(value) + , m_value(value) { QPushButton* button = new QPushButton(&parent); m_widget = button; @@ -29,16 +35,43 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt QFont font; font.setBold(true); button->setFont(font); - button->setText("Download"); - button->connect( + // Button should be disabled when in the middle of downloading + // this status is stored within ResourceDownloadButton::m_enabled + // when the button is clicked, m_enabled is set to false + // when te download is done, m_enabled is set back to true + // the UI is updated to reflect the status of m_enabled, by using update_enabled_status + auto update_enabled_status = [this, button](){ + if (m_value.get_enabled()){ + button->setEnabled(true); + button->setText("Download"); + }else{ + button->setEnabled(false); + button->setText("Downloading..."); + } + }; + + // update the UI based on m_enabled when the button is constructed + update_enabled_status(); + + // when the button is clicked, m_enabled is set to false, the UI is updated, and we run_download() + connect( button, &QPushButton::clicked, - button, [&](bool){ - cout << "Clicked Download Button" << endl; + this, [this, update_enabled_status](){ + m_value.set_enabled(false); + update_enabled_status(); + m_value.run_download(); } ); + + // when the download is finished, update the UI + connect( + &m_value, &ResourceDownloadButton::download_finished, + this, update_enabled_status + ); } + template class RegisterConfigWidget; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 69c9971a12..e0e3993566 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -7,18 +7,25 @@ #ifndef PokemonAutomation_ResourceDownloadWidget_H #define PokemonAutomation_ResourceDownloadWidget_H +#include #include "Common/Qt/Options/ConfigWidget.h" #include "ResourceDownloadTable.h" namespace PokemonAutomation{ +class ResourceDownloadButton; -class DownloadButtonWidget : public ConfigWidget{ +class DownloadButtonWidget : public QWidget, public ConfigWidget{ public: using ParentOption = ResourceDownloadButton; public: + ~DownloadButtonWidget(); DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value); + +private: + ResourceDownloadButton& m_value; + }; From fc76cdab2c9ca8005bef24d6f71a9c48019055ea Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 20 Mar 2026 22:30:52 -0700 Subject: [PATCH 17/53] add download pop-up --- .../ResourceDownload/ResourceDownloadRow.cpp | 25 ++++- .../ResourceDownload/ResourceDownloadRow.h | 5 +- .../ResourceDownloadWidget.cpp | 94 +++++++++++++++---- .../ResourceDownload/ResourceDownloadWidget.h | 9 ++ 4 files changed, 109 insertions(+), 24 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 1389bfb487..550630b48e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -5,7 +5,6 @@ */ - #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Options/LabelCellOption.h" @@ -20,7 +19,8 @@ using std::endl; namespace PokemonAutomation{ ResourceDownloadButton::~ResourceDownloadButton(){ - m_worker.wait_and_ignore_exceptions(); + m_worker1.wait_and_ignore_exceptions(); + m_worker2.wait_and_ignore_exceptions(); } @@ -31,11 +31,28 @@ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) {} +void ResourceDownloadButton::fetch_json(){ + m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + m_enabled = false; + std::this_thread::sleep_for(std::chrono::seconds(3)); + cout << "Fetched json" << endl; + + m_enabled = true; + emit json_fetch_finished(); + } + ); + +} + + void ResourceDownloadButton::run_download(){ - m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( [this]{ + m_enabled = false; std::this_thread::sleep_for(std::chrono::seconds(7)); - cout << "Clicked Download Button" << endl; + cout << "Done Download" << endl; + // show_update_box("Download", "Download", "Do you want to download?"); m_enabled = true; emit download_finished(); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 7a1c1a32e8..181fadb860 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -26,9 +26,11 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl #include #include +#include #include #include "CommonFramework/Logging/Logger.h" @@ -28,50 +29,105 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt : QWidget(&parent) , ConfigWidget(value) , m_value(value) + , m_button(new QPushButton(&parent)) { - QPushButton* button = new QPushButton(&parent); - m_widget = button; + m_widget = m_button; QFont font; font.setBold(true); - button->setFont(font); + m_button->setFont(font); // Button should be disabled when in the middle of downloading // this status is stored within ResourceDownloadButton::m_enabled // when the button is clicked, m_enabled is set to false // when te download is done, m_enabled is set back to true // the UI is updated to reflect the status of m_enabled, by using update_enabled_status - auto update_enabled_status = [this, button](){ - if (m_value.get_enabled()){ - button->setEnabled(true); - button->setText("Download"); - }else{ - button->setEnabled(false); - button->setText("Downloading..."); - } - }; - // update the UI based on m_enabled when the button is constructed + + // update the UI based on m_enabled, when the button is constructed update_enabled_status(); - // when the button is clicked, m_enabled is set to false, the UI is updated, and we run_download() + // when the button is clicked, m_enabled is set to false, + // fetch json connect( - button, &QPushButton::clicked, - this, [this, update_enabled_status](){ + m_button, &QPushButton::clicked, + this, [this](){ m_value.set_enabled(false); update_enabled_status(); - m_value.run_download(); + m_value.fetch_json(); } ); - // when the download is finished, update the UI + // when json has been fetched, open the update box. + // When click Ok in update box, start the download. If click cancel, re-enable the download button + connect( + &m_value, &ResourceDownloadButton::json_fetch_finished, + this, [this](){ + show_download_confirm_box("Download", "Download", "body"); + } + ); + + // when the download is finished, update the UI to re-enable the button connect( &m_value, &ResourceDownloadButton::download_finished, - this, update_enabled_status + this, &DownloadButtonWidget::update_enabled_status ); } +void DownloadButtonWidget::update_enabled_status(){ + if (m_value.get_enabled()){ + m_button->setEnabled(true); + m_button->setText("Download"); + }else{ + m_button->setEnabled(false); + m_button->setText("Downloading..."); + } +} + + +void DownloadButtonWidget::show_download_confirm_box( + const std::string& title, + const std::string& header, + const std::string& message_body +){ + QMessageBox box; + QPushButton* ok = box.addButton(QMessageBox::Ok); + QPushButton* cancel = box.addButton("Cancel", QMessageBox::NoRole); + box.setEscapeButton(cancel); +// cout << "ok = " << ok << endl; +// cout << "skip = " << skip << endl; + + box.setTextFormat(Qt::RichText); + std::string text = header + "
"; + // text += make_text_url(link_url, link_text); + // text += get_changes(node); + + + box.setWindowTitle(QString::fromStdString(title)); + box.setText(QString::fromStdString(text)); + +// box.open(); + + box.exec(); + + QAbstractButton* clicked = box.clickedButton(); +// cout << "clicked = " << clicked << endl; + if (clicked == ok){ + cout << "Clicked Ok to Download" << endl; + + m_value.run_download(); + return; + } + if (clicked == cancel){ + m_value.set_enabled(true); + update_enabled_status(); + return; + } +} + + + template class RegisterConfigWidget; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index e0e3993566..0838da3dda 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -25,6 +25,14 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ private: ResourceDownloadButton& m_value; + QPushButton* m_button; + + void update_enabled_status(); + void show_download_confirm_box( + const std::string& title, + const std::string& header, + const std::string& message_body + ); }; @@ -39,5 +47,6 @@ class DeleteButtonWidget : public ConfigWidget{ + } #endif From be7ee038d6b67d213a5e84ada898bd14775ee394 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 23 Mar 2026 23:15:50 -0700 Subject: [PATCH 18/53] move functions to Helper class to avoid circular dependency. lazy initialize remote_metadata for each Download button. --- .../ResourceDownloadHelpers.cpp | 134 ++++++++++++++++++ .../ResourceDownloadHelpers.h | 49 +++++++ .../ResourceDownload/ResourceDownloadRow.cpp | 128 ++++++++++------- .../ResourceDownload/ResourceDownloadRow.h | 32 +++-- .../ResourceDownloadTable.cpp | 113 +-------------- .../ResourceDownload/ResourceDownloadTable.h | 14 -- .../ResourceDownloadWidget.cpp | 4 +- SerialPrograms/cmake/SourceFiles.cmake | 2 + 8 files changed, 292 insertions(+), 184 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp new file mode 100644 index 0000000000..f1a2b80dda --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -0,0 +1,134 @@ +/* Resource Download Helpers + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Globals.h" +#include "CommonFramework/Logging/Logger.h" +// #include "CommonFramework/Tools/GlobalThreadPools.h" +#include "CommonFramework/Tools/FileDownloader.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "Common/Cpp/Json/JsonArray.h" +#include "Common/Cpp/Json/JsonObject.h" +#include "Common/Cpp/Filesystem.h" +#include "ResourceDownloadHelpers.h" + +#include +// #include +// #include + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + + +ResourceType get_resource_type_from_string(std::string type){ + if (type == "ZippedFolder"){ + return ResourceType::ZIP_FILE; + }else{ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_resource_type_from_string: Unknown string."); + } + +} + +std::vector deserialize_resource_list_json(const JsonValue& json){ + std::vector resources; + + try{ + const JsonObject& obj = json.to_object_throw(); + const JsonArray& resource_list = obj.get_array_throw("resourceList"); + for (const JsonValue& resource_val : resource_list){ + const JsonObject& resource_obj = resource_val.to_object_throw(); + + std::string resource_name = resource_obj.get_string_throw("resourceName"); + std::optional version_num = (uint16_t)resource_obj.get_integer_throw("version"); + ResourceType resource_type = get_resource_type_from_string(resource_obj.get_string_throw("Type")); + size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); + size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); + std::string url = resource_obj.get_string_throw("URL"); + + DownloadedResourceMetadata resource = { + resource_name, + version_num, + resource_type, + compressed_bytes, + decompressed_bytes, + url + }; + + resources.emplace_back(std::move(resource)); + + } + + }catch (ParseException& e){ + throw ParseException(e.message() + "\nJSON parsing error. Given JSON file doesn't match the expected format."); + } + + return resources; +} + + +std::vector local_resource_download_list(){ + static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); + + return local_resources; +} + + +JsonValue fetch_resource_download_list_json_from_remote(){ + Logger& logger = global_logger_tagged(); + JsonValue json; + try{ + json = FileDownloader::download_json_file( + logger, + "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" + ); + }catch (OperationFailedException&){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); + } + + return json; +} + +const JsonValue& remote_resource_download_list_json(){ + static const JsonValue json = fetch_resource_download_list_json_from_remote(); + + return json; +} + +std::vector remote_resource_download_list(){ + static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); + + return remote_resources; +} + +uint16_t get_resource_version_num(Filesystem::Path folder_path){ + std::string file_name = folder_path.string() + "/version.json"; + const JsonValue& json = load_json_file(file_name); + + const JsonObject& obj = json.to_object_throw(); + uint16_t version_num = (uint16_t)obj.get_integer_throw("version"); + + return version_num; +} + +ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num){ + if (!current_version_num.has_value()){ + return ResourceVersionStatus::NOT_APPLICABLE; + } + + if (current_version_num < expected_version_num){ + return ResourceVersionStatus::OUTDATED; + }else if (current_version_num == expected_version_num){ + return ResourceVersionStatus::CURRENT; + }else{ // current > expected + return ResourceVersionStatus::FUTURE_VERSION; + } +} + + + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h new file mode 100644 index 0000000000..06b1e1c30b --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h @@ -0,0 +1,49 @@ +/* Resource Download Helpers + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_ResourceDownloadHelpers_H +#define PokemonAutomation_ResourceDownloadHelpers_H + +#include +#include + + +namespace PokemonAutomation{ + +namespace Filesystem{ + class Path; +} + + +enum class ResourceType{ + ZIP_FILE, +}; + +struct DownloadedResourceMetadata{ + std::string resource_name; + std::optional version_num; + ResourceType resource_type; + size_t size_compressed_bytes; + size_t size_decompressed_bytes; + std::string url; +}; + +enum class ResourceVersionStatus{ + CURRENT, + OUTDATED, // still used, but newer version available + FUTURE_VERSION, // current version number is greater than the expected version number + NOT_APPLICABLE, // resource not downloaded locally, so can't get its version + // RETIRED, // no longer used + // BLANK, // not yet fetched version info from remote +}; + +std::vector local_resource_download_list(); +std::vector remote_resource_download_list(); +uint16_t get_resource_version_num(Filesystem::Path folder_path); +ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num); + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 550630b48e..04e6d35da6 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -8,6 +8,7 @@ #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Options/LabelCellOption.h" +// #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" #include @@ -18,54 +19,6 @@ using std::endl; namespace PokemonAutomation{ -ResourceDownloadButton::~ResourceDownloadButton(){ - m_worker1.wait_and_ignore_exceptions(); - m_worker2.wait_and_ignore_exceptions(); -} - - -ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) - , m_enabled(true) -{} - - -void ResourceDownloadButton::fetch_json(){ - m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - m_enabled = false; - std::this_thread::sleep_for(std::chrono::seconds(3)); - cout << "Fetched json" << endl; - - m_enabled = true; - emit json_fetch_finished(); - } - ); - -} - - -void ResourceDownloadButton::run_download(){ - m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - m_enabled = false; - std::this_thread::sleep_for(std::chrono::seconds(7)); - cout << "Done Download" << endl; - // show_update_box("Download", "Download", "Do you want to download?"); - - m_enabled = true; - emit download_finished(); - } - ); - -} - -ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) -{} - std::string resource_version_to_string(ResourceVersionStatus version){ switch(version){ case ResourceVersionStatus::CURRENT: @@ -117,6 +70,85 @@ struct ResourceDownloadRow::Data{ }; +ResourceDownloadButton::~ResourceDownloadButton(){ + m_worker1.wait_and_ignore_exceptions(); + m_worker2.wait_and_ignore_exceptions(); +} + + +ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) + , m_enabled(true) +{} + + +void ResourceDownloadButton::fetch_remote_metadata(){ + m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + m_enabled = false; + std::this_thread::sleep_for(std::chrono::seconds(3)); + cout << "Fetched json" << endl; + + + + m_enabled = true; + emit metadata_fetch_finished(); + } + ); + +} + +void ResourceDownloadButton::initialize_remote_metadata(){ + DownloadedResourceMetadata corresponding_remote_metadata; + RemoteMetadataStatus status = RemoteMetadataStatus::NOT_AVAILABLE; + std::vector all_remote_metadata = remote_resource_download_list(); + + std::string resource_name = row.m_data->m_resource_name.text(); + + for (DownloadedResourceMetadata remote_metadata : all_remote_metadata){ + if (remote_metadata.resource_name == resource_name){ + corresponding_remote_metadata = remote_metadata; + status = RemoteMetadataStatus::AVAILABLE; + break; + } + } + + RemoteMetadata remote_metadata = {status, corresponding_remote_metadata}; + + m_remote_metadata = std::make_unique(remote_metadata); +} + +ResourceDownloadButton::RemoteMetadata& ResourceDownloadButton::get_remote_metadata(){ + // Only runs once per instance + std::call_once(init_flag, &ResourceDownloadButton::initialize_remote_metadata, this); + return *m_remote_metadata; +} + + + +void ResourceDownloadButton::run_download(){ + m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + m_enabled = false; + std::this_thread::sleep_for(std::chrono::seconds(7)); + cout << "Done Download" << endl; + // show_update_box("Download", "Download", "Do you want to download?"); + + m_enabled = true; + emit download_finished(); + } + ); + +} + +ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + + + ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( std::string&& resource_name, diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 181fadb860..44a8262ea4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -12,7 +12,7 @@ #include "Common/Cpp/Concurrency/AsyncTask.h" #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" -#include +#include "ResourceDownloadHelpers.h" namespace PokemonAutomation{ @@ -26,11 +26,24 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl m_remote_metadata; private: bool m_enabled; // button should be blocked during an active task. m_enabled is false when blocked AsyncTask m_worker1; AsyncTask m_worker2; + + }; @@ -55,14 +72,7 @@ class ResourceDeleteButton : public ConfigOptionImpl{ ResourceDownloadRow& row; }; -enum class ResourceVersionStatus{ - CURRENT, - OUTDATED, // still used, but newer version available - FUTURE_VERSION, // current version number is greater than the expected version number - NOT_APPLICABLE, // resource not downloaded locally, so can't get its version - // RETIRED, // no longer used - // BLANK, // not yet fetched version info from remote -}; + class ResourceDownloadRow : public StaticTableRow{ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index b93afc382d..e542ede8d4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -12,9 +12,10 @@ #include "Common/Cpp/Json/JsonArray.h" #include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Filesystem.h" +#include "ResourceDownloadRow.h" #include "ResourceDownloadTable.h" -#include +// #include // #include // #include @@ -25,115 +26,9 @@ using std::endl; namespace PokemonAutomation{ -ResourceType get_resource_type_from_string(std::string type){ - if (type == "ZippedFolder"){ - return ResourceType::ZIP_FILE; - }else{ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_resource_type_from_string: Unknown string."); - } - -} - -std::vector deserialize_resource_list_json(const JsonValue& json){ - std::vector resources; - - try{ - const JsonObject& obj = json.to_object_throw(); - const JsonArray& resource_list = obj.get_array_throw("resourceList"); - for (const JsonValue& resource_val : resource_list){ - const JsonObject& resource_obj = resource_val.to_object_throw(); - - std::string resource_name = resource_obj.get_string_throw("resourceName"); - std::optional version_num = (uint16_t)resource_obj.get_integer_throw("version"); - ResourceType resource_type = get_resource_type_from_string(resource_obj.get_string_throw("Type")); - size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); - size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); - std::string url = resource_obj.get_string_throw("URL"); - - DownloadedResource resource = { - resource_name, - version_num, - resource_type, - compressed_bytes, - decompressed_bytes, - url - }; - - resources.emplace_back(std::move(resource)); - - } - - }catch (ParseException& e){ - throw ParseException(e.message() + "\nJSON parsing error. Given JSON file doesn't match the expected format."); - } - - return resources; -} - - -std::vector local_resource_download_list(){ - static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); - - return local_resources; -} - - -JsonValue fetch_resource_download_list_json_from_remote(){ - Logger& logger = global_logger_tagged(); - JsonValue json; - try{ - json = FileDownloader::download_json_file( - logger, - "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" - ); - }catch (OperationFailedException&){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); - } - - return json; -} - -const JsonValue& remote_resource_download_list_json(){ - static const JsonValue json = fetch_resource_download_list_json_from_remote(); - - return json; -} - -std::vector remote_resource_download_list(){ - static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); - - return remote_resources; -} - -uint16_t get_resource_version_num(Filesystem::Path folder_path){ - std::string file_name = folder_path.string() + "/version.json"; - const JsonValue& json = load_json_file(file_name); - - const JsonObject& obj = json.to_object_throw(); - uint16_t version_num = (uint16_t)obj.get_integer_throw("version"); - - return version_num; -} - -ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num){ - if (!current_version_num.has_value()){ - return ResourceVersionStatus::NOT_APPLICABLE; - } - - if (current_version_num < expected_version_num){ - return ResourceVersionStatus::OUTDATED; - }else if (current_version_num == expected_version_num){ - return ResourceVersionStatus::CURRENT; - }else{ // current > expected - return ResourceVersionStatus::FUTURE_VERSION; - } -} - - - std::vector> get_resource_download_rows(){ std::vector> resource_rows; - for (const DownloadedResource& resource : local_resource_download_list()){ + for (const DownloadedResourceMetadata& resource : local_resource_download_list()){ std::string resource_name = resource.resource_name; uint16_t expected_version_num = resource.version_num.value(); std::optional current_version_num; // default nullopt @@ -199,7 +94,7 @@ void ResourceDownloadTable::add_resource_download_rows(){ // void ResourceDownloadTable::check_all_resource_versions(){ -// std::vector remote_resources = remote_resource_download_list(); +// std::vector remote_resources = remote_resource_download_list(); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 46abd2142d..0ac5e44e39 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -13,17 +13,6 @@ namespace PokemonAutomation{ -enum class ResourceType{ - ZIP_FILE, -}; -struct DownloadedResource{ - std::string resource_name; - std::optional version_num; - ResourceType resource_type; - size_t size_compressed_bytes; - size_t size_decompressed_bytes; - std::string url; -}; class ResourceDownloadTable : public StaticTableOption{ public: @@ -47,8 +36,5 @@ class ResourceDownloadTable : public StaticTableOption{ - - - } #endif diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 7b62845879..a3761f683e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -54,14 +54,14 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt this, [this](){ m_value.set_enabled(false); update_enabled_status(); - m_value.fetch_json(); + m_value.fetch_remote_metadata(); } ); // when json has been fetched, open the update box. // When click Ok in update box, start the download. If click cancel, re-enable the download button connect( - &m_value, &ResourceDownloadButton::json_fetch_finished, + &m_value, &ResourceDownloadButton::metadata_fetch_finished, this, [this](){ show_download_confirm_box("Download", "Download", "body"); } diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index d095b93727..d420bcf833 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -487,6 +487,8 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Recording/StreamHistoryTracker_SaveFrames.h Source/CommonFramework/Recording/StreamRecorder.cpp Source/CommonFramework/Recording/StreamRecorder.h + Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp + Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp From 4b17f7d1322e8cc2a3d879b9abbba1ca24058f3f Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 23 Mar 2026 23:35:41 -0700 Subject: [PATCH 19/53] cleanup headers --- .../ResourceDownload/ResourceDownloadHelpers.cpp | 2 +- .../ResourceDownload/ResourceDownloadTable.cpp | 12 ++++++------ .../ResourceDownload/ResourceDownloadTable.h | 5 +++-- .../ResourceDownload/ResourceDownloadWidget.h | 5 +++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index f1a2b80dda..e02a940e0d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -14,7 +14,7 @@ #include "Common/Cpp/Filesystem.h" #include "ResourceDownloadHelpers.h" -#include +// #include // #include // #include diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index e542ede8d4..4ff6b20e2d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -5,12 +5,12 @@ */ #include "CommonFramework/Globals.h" -#include "CommonFramework/Logging/Logger.h" +// #include "CommonFramework/Logging/Logger.h" // #include "CommonFramework/Tools/GlobalThreadPools.h" -#include "CommonFramework/Tools/FileDownloader.h" -#include "CommonFramework/Exceptions/OperationFailedException.h" -#include "Common/Cpp/Json/JsonArray.h" -#include "Common/Cpp/Json/JsonObject.h" +// #include "CommonFramework/Tools/FileDownloader.h" +// #include "CommonFramework/Exceptions/OperationFailedException.h" +// #include "Common/Cpp/Json/JsonArray.h" +// #include "Common/Cpp/Json/JsonObject.h" #include "Common/Cpp/Filesystem.h" #include "ResourceDownloadRow.h" #include "ResourceDownloadTable.h" @@ -85,7 +85,7 @@ std::vector ResourceDownloadTable::make_header() const{ // } void ResourceDownloadTable::add_resource_download_rows(){ - for (auto& row_ptr : m_resource_rows){ + for (std::unique_ptr& row_ptr : m_resource_rows){ add_row(row_ptr.get()); } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h index 0ac5e44e39..c54cdef985 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.h @@ -8,11 +8,12 @@ #define PokemonAutomation_ResourceDownloadTable_H #include "Common/Cpp/Concurrency/AsyncTask.h" -#include "ResourceDownloadRow.h" +#include "Common/Cpp/Options/StaticTableOption.h" +// #include "ResourceDownloadRow.h" namespace PokemonAutomation{ - +class ResourceDownloadRow; class ResourceDownloadTable : public StaticTableOption{ public: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 0838da3dda..64665fce3d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -9,11 +9,12 @@ #include #include "Common/Qt/Options/ConfigWidget.h" -#include "ResourceDownloadTable.h" +// #include "ResourceDownloadTable.h" +#include "ResourceDownloadRow.h" namespace PokemonAutomation{ -class ResourceDownloadButton; +// class ResourceDownloadButton; class DownloadButtonWidget : public QWidget, public ConfigWidget{ public: From ac2bc3906734d3012e49ba37ffb8907f00b105a4 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 24 Mar 2026 22:06:49 -0700 Subject: [PATCH 20/53] print to console when throwing ParseException --- Common/Cpp/Exceptions.cpp | 8 ++++++++ Common/Cpp/Exceptions.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Common/Cpp/Exceptions.cpp b/Common/Cpp/Exceptions.cpp index 75d6f2e007..299d43e6c6 100644 --- a/Common/Cpp/Exceptions.cpp +++ b/Common/Cpp/Exceptions.cpp @@ -29,6 +29,14 @@ std::string Exception::to_str() const{ } +ParseException::ParseException(std::string message_string) + : m_message(std::move(message_string)) +{ + std::string str = ParseException::name(); + str += ": " + message(); + std::cerr << str << std::endl; +} + FileException::FileException(Logger* logger, const char* location, std::string message_string, std::string file) diff --git a/Common/Cpp/Exceptions.h b/Common/Cpp/Exceptions.h index 5c27db8b3f..0c6bcb3f29 100644 --- a/Common/Cpp/Exceptions.h +++ b/Common/Cpp/Exceptions.h @@ -77,7 +77,7 @@ class OperationCancelledException : public Exception{ class ParseException : public Exception{ public: ParseException() = default; - ParseException(std::string message) : m_message(std::move(message)) {} + ParseException(std::string message); virtual const char* name() const override{ return "ParseException"; } virtual std::string message() const override{ return m_message; } protected: From ee33049d196669895d7760e7b52b780f01eca55d Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 24 Mar 2026 22:10:59 -0700 Subject: [PATCH 21/53] update exception handling within the thread. add predownload_warning_summary --- .../ResourceDownloadHelpers.cpp | 8 +- .../ResourceDownloadHelpers.h | 4 +- .../ResourceDownload/ResourceDownloadRow.cpp | 107 ++++++++++++++++-- .../ResourceDownload/ResourceDownloadRow.h | 11 +- .../ResourceDownloadWidget.cpp | 28 ++++- .../ResourceDownload/ResourceDownloadWidget.h | 3 +- 6 files changed, 136 insertions(+), 25 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index e02a940e0d..dc7d8dbcc4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -63,15 +63,15 @@ std::vector deserialize_resource_list_json(const Jso } - }catch (ParseException& e){ - throw ParseException(e.message() + "\nJSON parsing error. Given JSON file doesn't match the expected format."); + }catch (ParseException&){ + throw ParseException("JSON parsing error. Given JSON file doesn't match the expected format."); } return resources; } -std::vector local_resource_download_list(){ +const std::vector& local_resource_download_list(){ static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); return local_resources; @@ -99,7 +99,7 @@ const JsonValue& remote_resource_download_list_json(){ return json; } -std::vector remote_resource_download_list(){ +const std::vector& remote_resource_download_list(){ static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); return remote_resources; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h index 06b1e1c30b..15d666558b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h @@ -40,8 +40,8 @@ enum class ResourceVersionStatus{ // BLANK, // not yet fetched version info from remote }; -std::vector local_resource_download_list(); -std::vector remote_resource_download_list(); +const std::vector& local_resource_download_list(); +const std::vector& remote_resource_download_list(); uint16_t get_resource_version_num(Filesystem::Path folder_path); ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 04e6d35da6..990edf8ae5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -4,7 +4,7 @@ * */ - +#include #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Options/LabelCellOption.h" @@ -80,29 +80,95 @@ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) , m_enabled(true) + , m_local_metadata(get_local_metadata()) {} -void ResourceDownloadButton::fetch_remote_metadata(){ +void ResourceDownloadButton::ensure_remote_metadata_loaded(){ m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( [this]{ - m_enabled = false; - std::this_thread::sleep_for(std::chrono::seconds(3)); - cout << "Fetched json" << endl; - + try { + m_enabled = false; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::string predownload_warning; + ResourceDownloadButton::RemoteMetadata& remote_handle = fetch_remote_metadata(); + // cout << "Fetched remote metadata" << endl; + + predownload_warning = predownload_warning_summary(remote_handle); + m_enabled = true; + emit metadata_fetch_finished(predownload_warning); + + }catch(...){ + m_enabled = true; + // cout << "Exception thrown in thread" << endl; + emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); + // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; + // QMessageBox box; + // box.warning(nullptr, "Error:", + // QString::fromStdString("Error: Unknown error. Embedding session failed.")); + return; + } + + } + ); +} - m_enabled = true; - emit metadata_fetch_finished(); +std::string ResourceDownloadButton::predownload_warning_summary(ResourceDownloadButton::RemoteMetadata& remote_handle){ + + std::string predownload_warning; + + switch (remote_handle.status){ + case RemoteMetadataStatus::UNINITIALIZED: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Remote metadata uninitialized."); + case RemoteMetadataStatus::NOT_AVAILABLE: + predownload_warning = "Resource no longer available for download. We recommend updating the Computer Control program."; + break; + case RemoteMetadataStatus::AVAILABLE: + { + uint16_t local_version_num = m_local_metadata.version_num.value(); + + DownloadedResourceMetadata remote_metadata = remote_handle.metadata; + uint16_t remote_version_num = remote_metadata.version_num.value(); + size_t compressed_size = remote_metadata.size_compressed_bytes; + size_t decompressed_size = remote_metadata.size_decompressed_bytes; + + std::string disk_space_requirement = ""; + + if (decompressed_size > 100000){ + disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; + }else{ + disk_space_requirement = "This will require " + std::to_string(decompressed_size) + " bytes of free space"; } - ); + if (local_version_num < remote_version_num){ + predownload_warning = "The resource you are downloading is a more updated version than the program expects. " + "This may or may not cause issues with the programs. " + "We recommend updating the Computer Control program.
" + + disk_space_requirement; + }else if (local_version_num == remote_version_num){ + predownload_warning = "Update available.
" + disk_space_requirement; + }else if (local_version_num > remote_version_num){ + predownload_warning = "The resource you are downloading is a less updated version than the program expects. " + "Please report this as a bug.
" + + disk_space_requirement; + } + } + break; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Unknown enum."); + } + + return predownload_warning; } + void ResourceDownloadButton::initialize_remote_metadata(){ DownloadedResourceMetadata corresponding_remote_metadata; RemoteMetadataStatus status = RemoteMetadataStatus::NOT_AVAILABLE; std::vector all_remote_metadata = remote_resource_download_list(); + + cout << "done remote_resource_download_list" << endl; std::string resource_name = row.m_data->m_resource_name.text(); @@ -119,12 +185,33 @@ void ResourceDownloadButton::initialize_remote_metadata(){ m_remote_metadata = std::make_unique(remote_metadata); } -ResourceDownloadButton::RemoteMetadata& ResourceDownloadButton::get_remote_metadata(){ +ResourceDownloadButton::RemoteMetadata& ResourceDownloadButton::fetch_remote_metadata(){ // Only runs once per instance std::call_once(init_flag, &ResourceDownloadButton::initialize_remote_metadata, this); return *m_remote_metadata; } +DownloadedResourceMetadata ResourceDownloadButton::get_local_metadata(){ + DownloadedResourceMetadata corresponding_local_metadata; + std::vector all_local_metadata = local_resource_download_list(); + + std::string resource_name = row.m_data->m_resource_name.text(); + + bool found = false; + for (DownloadedResourceMetadata local_metadata : all_local_metadata){ + if (local_metadata.resource_name == resource_name){ + corresponding_local_metadata = local_metadata; + found = true; + break; + } + } + + if (!found){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_local_metadata: Corresponding DownloadedResourceMetadata not found in the local JSON file."); + } + + return corresponding_local_metadata; +} void ResourceDownloadButton::run_download(){ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 44a8262ea4..f82ed37e99 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -26,7 +26,8 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl"; + std::string text = message_body; // text += make_text_url(link_url, link_text); // text += get_changes(node); @@ -126,6 +136,14 @@ void DownloadButtonWidget::show_download_confirm_box( } } +void show_error_box(std::string function_name){ + std::cerr << "Error: Exception thrown in thread. From " + function_name + ". Report this as a bug." << std::endl; + QMessageBox box; + box.warning(nullptr, "Error:", + QString::fromStdString("Error: Exception thrown in thread. From " + function_name + ". Report this as a bug.")); + +} + template class RegisterConfigWidget; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 64665fce3d..81fc761bc3 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -31,12 +31,13 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ void update_enabled_status(); void show_download_confirm_box( const std::string& title, - const std::string& header, const std::string& message_body ); }; +void show_error_box(std::string function_name); + class DeleteButtonWidget : public ConfigWidget{ public: From a520d758b8bb595d713c47d1719ff7351c753f2e Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 13:42:23 -0700 Subject: [PATCH 22/53] FileDownloader::download_file_to_disk --- .../ResourceDownload/ResourceDownloadRow.cpp | 44 +++++++++++---- .../ResourceDownload/ResourceDownloadRow.h | 2 +- .../CommonFramework/Tools/FileDownloader.cpp | 56 +++++++++++++++++++ .../CommonFramework/Tools/FileDownloader.h | 3 + 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 990edf8ae5..3c9de767d6 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -5,13 +5,17 @@ */ #include +#include "CommonFramework/Globals.h" #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" +#include "CommonFramework/Logging/Logger.h" +#include "CommonFramework/Tools/FileDownloader.h" #include "CommonFramework/Options/LabelCellOption.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" #include +// #include #include using std::cout; @@ -80,7 +84,7 @@ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) , m_enabled(true) - , m_local_metadata(get_local_metadata()) + , m_local_metadata(initialize_local_metadata()) {} @@ -89,10 +93,11 @@ void ResourceDownloadButton::ensure_remote_metadata_loaded(){ [this]{ try { m_enabled = false; - std::this_thread::sleep_for(std::chrono::seconds(1)); + // std::this_thread::sleep_for(std::chrono::seconds(1)); std::string predownload_warning; ResourceDownloadButton::RemoteMetadata& remote_handle = fetch_remote_metadata(); // cout << "Fetched remote metadata" << endl; + // throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "testing"); predownload_warning = predownload_warning_summary(remote_handle); m_enabled = true; @@ -191,7 +196,7 @@ ResourceDownloadButton::RemoteMetadata& ResourceDownloadButton::fetch_remote_met return *m_remote_metadata; } -DownloadedResourceMetadata ResourceDownloadButton::get_local_metadata(){ +DownloadedResourceMetadata ResourceDownloadButton::initialize_local_metadata(){ DownloadedResourceMetadata corresponding_local_metadata; std::vector all_local_metadata = local_resource_download_list(); @@ -207,7 +212,7 @@ DownloadedResourceMetadata ResourceDownloadButton::get_local_metadata(){ } if (!found){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "get_local_metadata: Corresponding DownloadedResourceMetadata not found in the local JSON file."); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "initialize_local_metadata: Corresponding DownloadedResourceMetadata not found in the local JSON file."); } return corresponding_local_metadata; @@ -217,13 +222,32 @@ DownloadedResourceMetadata ResourceDownloadButton::get_local_metadata(){ void ResourceDownloadButton::run_download(){ m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( [this]{ - m_enabled = false; - std::this_thread::sleep_for(std::chrono::seconds(7)); - cout << "Done Download" << endl; - // show_update_box("Download", "Download", "Do you want to download?"); + try { + m_enabled = false; + // std::this_thread::sleep_for(std::chrono::seconds(7)); + Logger& logger = global_logger_tagged(); + std::string url = m_local_metadata.url; + std::string resource_name = m_local_metadata.resource_name; + FileDownloader::download_file_to_disk( + logger, + url, + DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip" + ); + + cout << "Done Download" << endl; - m_enabled = true; - emit download_finished(); + m_enabled = true; + emit download_finished(); + }catch(...){ + m_enabled = true; + // cout << "Exception thrown in thread" << endl; + emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); + // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; + // QMessageBox box; + // box.warning(nullptr, "Error:", + // QString::fromStdString("Error: Unknown error. Embedding session failed.")); + return; + } } ); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index f82ed37e99..955f4cf5f5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -44,7 +44,7 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl #include #include +#include #include "Common/Cpp/Json/JsonValue.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "FileDownloader.h" @@ -89,6 +90,61 @@ std::string download_file(Logger& logger, const std::string& url){ return std::string(downloaded_data.data(), downloaded_data.size()); } + +void download_file_to_disk(Logger& logger, const std::string& url, const std::string& file_path){ +// cout << "download_file()" << endl; + QNetworkAccessManager network_access_manager; + QEventLoop loop; + + // 1. Initialize QSaveFile + QSaveFile file(QString::fromStdString(file_path)); + if (!file.open(QIODevice::WriteOnly)) { + throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, + "Could not open save file: " + file_path); + } + + QNetworkRequest request(QUrl(QString::fromStdString(url))); + + request.setTransferTimeout(std::chrono::seconds(5)); + + // 2. Start the GET request + QNetworkReply* reply = network_access_manager.get(request); + + // 3. Stream chunks directly to the temporary file + QObject::connect(reply, &QNetworkReply::readyRead, [&file, reply]() { + file.write(reply->readAll()); + }); + + // 4. Handle completion and errors + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + + // Start the loop. local wait mechanism that pauses execution of the function + // while Qt handles the network request. + // the loop stops once we see the signal QNetworkReply::finished. + loop.exec(); + + // // Final check for remaining data + // if (reply->bytesAvailable() > 0) { + // file.write(reply->readAll()); + // } + + // 5. Finalize the transaction + if (reply->error() == QNetworkReply::NoError) { + // This moves the temporary file to the final destination 'file_path' + if (!file.commit()) { + throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, + "Failed to commit file to disk: " + file_path); + } + } else { + QString error_string = reply->errorString(); + // QSaveFile automatically deletes the temp file if commit() isn't called + throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, + "Network Error: " + error_string.toStdString()); + } + + reply->deleteLater(); +} + JsonValue download_json_file(Logger& logger, const std::string& url){ std::string downloaded_data = download_file(logger, url); return parse_json(downloaded_data); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h index 92243a3718..3175b41c13 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h @@ -17,6 +17,9 @@ namespace FileDownloader{ // Throws OperationFailedException if failed to download. std::string download_file(Logger& logger, const std::string& url); +// Throws OperationFailedException if failed to download. +void download_file_to_disk(Logger& logger, const std::string& url, const std::string& file_path); + // Throws OperationFailedException if failed to download. // Returns empty value if invalid JSON. JsonValue download_json_file(Logger& logger, const std::string& url); From eb4af183507c3cf2ccefbe65743248d28c96abb2 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 16:05:42 -0700 Subject: [PATCH 23/53] initial progress bar implementation --- .../ResourceDownload/ResourceDownloadRow.cpp | 31 ++++++++++++++++--- .../ResourceDownload/ResourceDownloadRow.h | 1 + .../ResourceDownloadWidget.cpp | 11 +++++++ .../CommonFramework/Tools/FileDownloader.cpp | 20 +++++++++++- .../CommonFramework/Tools/FileDownloader.h | 10 +++++- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 3c9de767d6..85ce6c70f0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -4,7 +4,7 @@ * */ -#include +// #include #include "CommonFramework/Globals.h" #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" @@ -226,12 +226,35 @@ void ResourceDownloadButton::run_download(){ m_enabled = false; // std::this_thread::sleep_for(std::chrono::seconds(7)); Logger& logger = global_logger_tagged(); - std::string url = m_local_metadata.url; - std::string resource_name = m_local_metadata.resource_name; + ResourceDownloadButton::RemoteMetadata& remote_handle = fetch_remote_metadata(); + if (remote_handle.status != RemoteMetadataStatus::AVAILABLE){ + switch (remote_handle.status){ + case RemoteMetadataStatus::UNINITIALIZED: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_download: Remote metadata uninitialized."); + case RemoteMetadataStatus::NOT_AVAILABLE: + cout << "run_download: Download not available. Cancel download." << endl; + m_enabled = true; + emit download_finished(); + return; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_download: Unknown enum."); + } + } + + // Download is available + + DownloadedResourceMetadata metadata = remote_handle.metadata; + std::string url = metadata.url; + std::string resource_name = metadata.resource_name; + qint64 expected_size = metadata.size_compressed_bytes; FileDownloader::download_file_to_disk( logger, url, - DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip" + DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip", + expected_size, + [this](int percentage_progress){ + download_progress(percentage_progress); + } ); cout << "Done Download" << endl; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 955f4cf5f5..6536b12734 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -27,6 +27,7 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl progress_callback +){ // cout << "download_file()" << endl; QNetworkAccessManager network_access_manager; QEventLoop loop; @@ -110,6 +116,18 @@ void download_file_to_disk(Logger& logger, const std::string& url, const std::st // 2. Start the GET request QNetworkReply* reply = network_access_manager.get(request); + // Progress Bar Logic + QObject::connect(reply, &QNetworkReply::downloadProgress, + [expected_size, progress_callback](qint64 bytesReceived, qint64 bytesTotal) { + + // Use expected_size if the network doesn't provide one + qint64 total = (bytesTotal > 0) ? bytesTotal : expected_size; + + int percentage_progress = static_cast((bytesReceived * 100) / total); + progress_callback(percentage_progress); + + }); + // 3. Stream chunks directly to the temporary file QObject::connect(reply, &QNetworkReply::readyRead, [&file, reply]() { file.write(reply->readAll()); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h index 3175b41c13..a48bcf7569 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h @@ -8,6 +8,8 @@ #define PokemonAutomation_FileDownloader_H #include +#include +#include namespace PokemonAutomation{ class Logger; @@ -18,7 +20,13 @@ namespace FileDownloader{ std::string download_file(Logger& logger, const std::string& url); // Throws OperationFailedException if failed to download. -void download_file_to_disk(Logger& logger, const std::string& url, const std::string& file_path); +void download_file_to_disk( + Logger& logger, + const std::string& url, + const std::string& file_path, + qint64 expected_size, + std::function progress_callback +); // Throws OperationFailedException if failed to download. // Returns empty value if invalid JSON. From c78d4d36d8eaec2e757d1f65ad6852719169e1d0 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 17:41:21 -0700 Subject: [PATCH 24/53] fix Download button --- .../ResourceDownload/ResourceDownloadWidget.cpp | 3 +-- .../ResourceDownload/ResourceDownloadWidget.h | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 506781e9de..c1901309b4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -26,8 +26,7 @@ template class RegisterConfigWidget; DownloadButtonWidget::~DownloadButtonWidget(){ } DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) - : QWidget(&parent) - , ConfigWidget(value) + : ConfigWidget(value) , m_value(value) , m_button(new QPushButton(&parent)) { diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 81fc761bc3..ee3350df1b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -7,7 +7,7 @@ #ifndef PokemonAutomation_ResourceDownloadWidget_H #define PokemonAutomation_ResourceDownloadWidget_H -#include +#include #include "Common/Qt/Options/ConfigWidget.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" @@ -16,7 +16,8 @@ namespace PokemonAutomation{ // class ResourceDownloadButton; -class DownloadButtonWidget : public QWidget, public ConfigWidget{ +class DownloadButtonWidget : public QObject, public ConfigWidget{ + Q_OBJECT public: using ParentOption = ResourceDownloadButton; From 2d5c9489a7e71d76b1148a81bfee3eb828290f4c Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 22:01:36 -0700 Subject: [PATCH 25/53] added static progress bar to UI --- .../ResourceDownload/ResourceDownloadRow.cpp | 16 +++++----- .../ResourceDownload/ResourceDownloadRow.h | 9 ++++++ .../ResourceDownloadTable.cpp | 1 + .../ResourceDownloadWidget.cpp | 30 ++++++++++++++++--- .../ResourceDownload/ResourceDownloadWidget.h | 14 +++++++++ 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 85ce6c70f0..2f32412dff 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -138,13 +138,7 @@ std::string ResourceDownloadButton::predownload_warning_summary(ResourceDownload size_t compressed_size = remote_metadata.size_compressed_bytes; size_t decompressed_size = remote_metadata.size_decompressed_bytes; - std::string disk_space_requirement = ""; - - if (decompressed_size > 100000){ - disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; - }else{ - disk_space_requirement = "This will require " + std::to_string(decompressed_size) + " bytes of free space"; - } + std::string disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; if (local_version_num < remote_version_num){ predownload_warning = "The resource you are downloading is a more updated version than the program expects. " @@ -282,6 +276,12 @@ ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) {} +ResourceProgressBar::ResourceProgressBar(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + + ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( @@ -295,6 +295,7 @@ ResourceDownloadRow::ResourceDownloadRow( , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) , m_download_button(*this) , m_delete_button(*this) + , m_progress_bar(*this) { PA_ADD_STATIC(m_data->m_resource_name); PA_ADD_STATIC(m_data->m_file_size_label); @@ -303,6 +304,7 @@ ResourceDownloadRow::ResourceDownloadRow( PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); + PA_ADD_STATIC(m_progress_bar); } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 6536b12734..50e74de4cb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -46,6 +46,7 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl{ ResourceDownloadRow& row; }; +class ResourceProgressBar : public ConfigOptionImpl{ +public: + ResourceProgressBar(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; +}; + class ResourceDownloadRow : public StaticTableRow{ @@ -98,6 +106,7 @@ class ResourceDownloadRow : public StaticTableRow{ ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; + ResourceProgressBar m_progress_bar; }; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 4ff6b20e2d..30639d2c59 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -70,6 +70,7 @@ std::vector ResourceDownloadTable::make_header() const{ "Version", "", "", + "", }; return ret; } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index c1901309b4..d0b4cb110e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "CommonFramework/Logging/Logger.h" #include "CommonFramework/Notifications/ProgramNotifications.h" @@ -22,14 +23,13 @@ namespace PokemonAutomation{ template class RegisterConfigWidget; - DownloadButtonWidget::~DownloadButtonWidget(){ } DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) : ConfigWidget(value) , m_value(value) - , m_button(new QPushButton(&parent)) { + m_button = new QPushButton(&parent); m_widget = m_button; QFont font; @@ -157,8 +157,6 @@ void show_error_box(std::string function_name){ template class RegisterConfigWidget; - - DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) : ConfigWidget(value) { @@ -179,5 +177,29 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va } +template class RegisterConfigWidget; +ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value) + : QWidget(&parent) + , ConfigWidget(value, *this) +{ + + // 1. Instantiate the widgets + m_status_label = new QLabel("Ready", this); + m_progress_bar = new QProgressBar(this); + + + // 2. Configure the progress bar + m_progress_bar->setRange(0, 100); + m_progress_bar->setValue(0); + m_progress_bar->setTextVisible(true); // Shows % inside the bar + + // 3. Create a horizontal layout to hold them + QHBoxLayout *layout = new QHBoxLayout(); + layout->addWidget(m_status_label); + layout->addWidget(m_progress_bar); + + this->setLayout(layout); +} + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index ee3350df1b..0fa3f38178 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -8,6 +8,8 @@ #define PokemonAutomation_ResourceDownloadWidget_H #include +#include +#include #include "Common/Qt/Options/ConfigWidget.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" @@ -48,6 +50,18 @@ class DeleteButtonWidget : public ConfigWidget{ DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); }; +class ProgressBarWidget : public QWidget, public ConfigWidget{ +public: + using ParentOption = ResourceProgressBar; + +public: + ProgressBarWidget(QWidget& parent, ResourceProgressBar& value); + +private: + QLabel* m_status_label; + QProgressBar* m_progress_bar; +}; + From 654f15f06c731033c5dabce922d4d6beee66bc99 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 12:09:16 -0700 Subject: [PATCH 26/53] refactor. move functions out of DownloadButton into Row. Move buttons into their own class --- .../ResourceDownloadHelpers.h | 11 + .../ResourceDownloadOptions.cpp | 177 ++++++++++++++ .../ResourceDownloadOptions.h | 75 ++++++ .../ResourceDownload/ResourceDownloadRow.cpp | 226 ++++-------------- .../ResourceDownload/ResourceDownloadRow.h | 88 ++----- .../ResourceDownloadWidget.cpp | 4 +- SerialPrograms/cmake/SourceFiles.cmake | 2 + 7 files changed, 332 insertions(+), 251 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp create mode 100644 SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h index 15d666558b..9855ff63f3 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h @@ -40,6 +40,17 @@ enum class ResourceVersionStatus{ // BLANK, // not yet fetched version info from remote }; +enum class RemoteMetadataStatus{ + UNINITIALIZED, + NOT_AVAILABLE, + AVAILABLE, +}; +struct RemoteMetadata { + RemoteMetadataStatus status = RemoteMetadataStatus::UNINITIALIZED; + DownloadedResourceMetadata metadata; +}; + + const std::vector& local_resource_download_list(); const std::vector& remote_resource_download_list(); uint16_t get_resource_version_num(Filesystem::Path folder_path); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp new file mode 100644 index 0000000000..0480d7f898 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp @@ -0,0 +1,177 @@ +/* Resource Download Row + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Tools/GlobalThreadPools.h" +#include "Common/Cpp/Exceptions.h" +#include "ResourceDownloadRow.h" +#include "ResourceDownloadOptions.h" + +// #include + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ResourceDownloadButton +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +ResourceDownloadButton::~ResourceDownloadButton(){ + m_worker1.wait_and_ignore_exceptions(); + m_worker2.wait_and_ignore_exceptions(); +} + + +ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) + , m_enabled(true) +{} + + +void ResourceDownloadButton::ensure_remote_metadata_loaded(){ + m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + try { + m_enabled = false; + // std::this_thread::sleep_for(std::chrono::seconds(1)); + std::string predownload_warning; + RemoteMetadata& remote_handle = row.fetch_remote_metadata(); + // cout << "Fetched remote metadata" << endl; + // throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "testing"); + + predownload_warning = predownload_warning_summary(remote_handle); + m_enabled = true; + emit metadata_fetch_finished(predownload_warning); + + }catch(...){ + m_enabled = true; + // cout << "Exception thrown in thread" << endl; + emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); + // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; + // QMessageBox box; + // box.warning(nullptr, "Error:", + // QString::fromStdString("Error: Unknown error. Embedding session failed.")); + return; + } + + } + ); + +} + +std::string ResourceDownloadButton::predownload_warning_summary(RemoteMetadata& remote_handle){ + + std::string predownload_warning; + + switch (remote_handle.status){ + case RemoteMetadataStatus::UNINITIALIZED: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Remote metadata uninitialized."); + case RemoteMetadataStatus::NOT_AVAILABLE: + predownload_warning = "Resource no longer available for download. We recommend updating the Computer Control program."; + break; + case RemoteMetadataStatus::AVAILABLE: + { + uint16_t local_version_num = row.m_local_metadata.version_num.value(); + + DownloadedResourceMetadata remote_metadata = remote_handle.metadata; + uint16_t remote_version_num = remote_metadata.version_num.value(); + size_t compressed_size = remote_metadata.size_compressed_bytes; + size_t decompressed_size = remote_metadata.size_decompressed_bytes; + + std::string disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; + + if (local_version_num < remote_version_num){ + predownload_warning = "The resource you are downloading is a more updated version than the program expects. " + "This may or may not cause issues with the programs. " + "We recommend updating the Computer Control program.
" + + disk_space_requirement; + }else if (local_version_num == remote_version_num){ + predownload_warning = "Update available.
" + disk_space_requirement; + }else if (local_version_num > remote_version_num){ + predownload_warning = "The resource you are downloading is a less updated version than the program expects. " + "Please report this as a bug.
" + + disk_space_requirement; + } + } + break; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Unknown enum."); + } + + return predownload_warning; +} + + + +void ResourceDownloadButton::start_download(){ + m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + try { + m_enabled = false; + // std::this_thread::sleep_for(std::chrono::seconds(7)); + RemoteMetadata& remote_handle = row.fetch_remote_metadata(); + if (remote_handle.status != RemoteMetadataStatus::AVAILABLE){ + switch (remote_handle.status){ + case RemoteMetadataStatus::UNINITIALIZED: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Remote metadata uninitialized."); + case RemoteMetadataStatus::NOT_AVAILABLE: + cout << "start_download: Download not available. Cancel download." << endl; + m_enabled = true; + emit download_finished(); + return; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Unknown enum."); + } + } + + // Download is available + DownloadedResourceMetadata metadata = remote_handle.metadata; + row.run_download(metadata); + + cout << "Done Download" << endl; + + m_enabled = true; + emit download_finished(); + }catch(...){ + m_enabled = true; + // cout << "Exception thrown in thread" << endl; + emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); + // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; + // QMessageBox box; + // box.warning(nullptr, "Error:", + // QString::fromStdString("Error: Unknown error. Embedding session failed.")); + return; + } + } + ); + +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ResourceDeleteButton +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ResourceProgressBar +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + +ResourceProgressBar::ResourceProgressBar(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) +{} + + +} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h new file mode 100644 index 0000000000..099ac0b80d --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h @@ -0,0 +1,75 @@ +/* Resource Download Row + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_ResourceDownloadOptions_H +#define PokemonAutomation_ResourceDownloadOptions_H + +#include +// #include "Common/Cpp/Containers/Pimpl.h" +#include "Common/Cpp/Concurrency/AsyncTask.h" +// #include "Common/Cpp/Options/StaticTableOption.h" +#include "ResourceDownloadHelpers.h" + + +namespace PokemonAutomation{ + +class ResourceDownloadRow; + + +class ResourceDownloadButton : public QObject, public ConfigOptionImpl{ + Q_OBJECT +public: + ~ResourceDownloadButton(); + ResourceDownloadButton(ResourceDownloadRow& p_row); + +signals: + void metadata_fetch_finished(std::string popup_message); + void exception_caught(std::string function_name); + void download_finished(); + +public: + void start_download(); + + void ensure_remote_metadata_loaded(); + std::string predownload_warning_summary(RemoteMetadata& remote_metadata); + + inline bool get_enabled(){ return m_enabled; } + inline void set_enabled(bool enabled){ + m_enabled = enabled; + } + +public: + ResourceDownloadRow& row; + +private: + bool m_enabled; // button should be blocked during an active task. m_enabled is false when blocked + + AsyncTask m_worker1; + AsyncTask m_worker2; + + + + +}; + +class ResourceDeleteButton : public ConfigOptionImpl{ +public: + ResourceDeleteButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; +}; + +class ResourceProgressBar : public ConfigOptionImpl{ +public: + ResourceProgressBar(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; +}; + + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 2f32412dff..1b780fa74f 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -4,7 +4,6 @@ * */ -// #include #include "CommonFramework/Globals.h" #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/Exceptions.h" @@ -14,7 +13,7 @@ // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" -#include +// #include // #include #include @@ -23,6 +22,11 @@ using std::endl; namespace PokemonAutomation{ +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ResourceDownloadRow +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + std::string resource_version_to_string(ResourceVersionStatus version){ switch(version){ case ResourceVersionStatus::CURRENT: @@ -74,102 +78,41 @@ struct ResourceDownloadRow::Data{ }; -ResourceDownloadButton::~ResourceDownloadButton(){ - m_worker1.wait_and_ignore_exceptions(); - m_worker2.wait_and_ignore_exceptions(); -} - - -ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) - , m_enabled(true) +ResourceDownloadRow::~ResourceDownloadRow(){} +ResourceDownloadRow::ResourceDownloadRow( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + std::optional version_num, + ResourceVersionStatus version_status +) + : StaticTableRow(resource_name) + , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) + , m_download_button(*this) + , m_delete_button(*this) + , m_progress_bar(*this) , m_local_metadata(initialize_local_metadata()) -{} - - -void ResourceDownloadButton::ensure_remote_metadata_loaded(){ - m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - try { - m_enabled = false; - // std::this_thread::sleep_for(std::chrono::seconds(1)); - std::string predownload_warning; - ResourceDownloadButton::RemoteMetadata& remote_handle = fetch_remote_metadata(); - // cout << "Fetched remote metadata" << endl; - // throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "testing"); - - predownload_warning = predownload_warning_summary(remote_handle); - m_enabled = true; - emit metadata_fetch_finished(predownload_warning); - - }catch(...){ - m_enabled = true; - // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); - // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; - // QMessageBox box; - // box.warning(nullptr, "Error:", - // QString::fromStdString("Error: Unknown error. Embedding session failed.")); - return; - } - - } - ); +{ + PA_ADD_STATIC(m_data->m_resource_name); + PA_ADD_STATIC(m_data->m_file_size_label); + PA_ADD_STATIC(m_data->m_is_downloaded_label); + PA_ADD_STATIC(m_data->m_version_status_label); + PA_ADD_STATIC(m_download_button); + PA_ADD_STATIC(m_delete_button); + PA_ADD_STATIC(m_progress_bar); } -std::string ResourceDownloadButton::predownload_warning_summary(ResourceDownloadButton::RemoteMetadata& remote_handle){ - - std::string predownload_warning; - - switch (remote_handle.status){ - case RemoteMetadataStatus::UNINITIALIZED: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Remote metadata uninitialized."); - case RemoteMetadataStatus::NOT_AVAILABLE: - predownload_warning = "Resource no longer available for download. We recommend updating the Computer Control program."; - break; - case RemoteMetadataStatus::AVAILABLE: - { - uint16_t local_version_num = m_local_metadata.version_num.value(); - - DownloadedResourceMetadata remote_metadata = remote_handle.metadata; - uint16_t remote_version_num = remote_metadata.version_num.value(); - size_t compressed_size = remote_metadata.size_compressed_bytes; - size_t decompressed_size = remote_metadata.size_decompressed_bytes; - - std::string disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; - - if (local_version_num < remote_version_num){ - predownload_warning = "The resource you are downloading is a more updated version than the program expects. " - "This may or may not cause issues with the programs. " - "We recommend updating the Computer Control program.
" + - disk_space_requirement; - }else if (local_version_num == remote_version_num){ - predownload_warning = "Update available.
" + disk_space_requirement; - }else if (local_version_num > remote_version_num){ - predownload_warning = "The resource you are downloading is a less updated version than the program expects. " - "Please report this as a bug.
" + - disk_space_requirement; - } - } - break; - default: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Unknown enum."); - } - return predownload_warning; -} - -void ResourceDownloadButton::initialize_remote_metadata(){ +void ResourceDownloadRow::initialize_remote_metadata(){ DownloadedResourceMetadata corresponding_remote_metadata; RemoteMetadataStatus status = RemoteMetadataStatus::NOT_AVAILABLE; std::vector all_remote_metadata = remote_resource_download_list(); cout << "done remote_resource_download_list" << endl; - std::string resource_name = row.m_data->m_resource_name.text(); + std::string resource_name = m_data->m_resource_name.text(); for (DownloadedResourceMetadata remote_metadata : all_remote_metadata){ if (remote_metadata.resource_name == resource_name){ @@ -184,17 +127,17 @@ void ResourceDownloadButton::initialize_remote_metadata(){ m_remote_metadata = std::make_unique(remote_metadata); } -ResourceDownloadButton::RemoteMetadata& ResourceDownloadButton::fetch_remote_metadata(){ +RemoteMetadata& ResourceDownloadRow::fetch_remote_metadata(){ // Only runs once per instance - std::call_once(init_flag, &ResourceDownloadButton::initialize_remote_metadata, this); + std::call_once(init_flag, &ResourceDownloadRow::initialize_remote_metadata, this); return *m_remote_metadata; } -DownloadedResourceMetadata ResourceDownloadButton::initialize_local_metadata(){ +DownloadedResourceMetadata ResourceDownloadRow::initialize_local_metadata(){ DownloadedResourceMetadata corresponding_local_metadata; std::vector all_local_metadata = local_resource_download_list(); - std::string resource_name = row.m_data->m_resource_name.text(); + std::string resource_name = m_data->m_resource_name.text(); bool found = false; for (DownloadedResourceMetadata local_metadata : all_local_metadata){ @@ -213,98 +156,21 @@ DownloadedResourceMetadata ResourceDownloadButton::initialize_local_metadata(){ } -void ResourceDownloadButton::run_download(){ - m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - try { - m_enabled = false; - // std::this_thread::sleep_for(std::chrono::seconds(7)); - Logger& logger = global_logger_tagged(); - ResourceDownloadButton::RemoteMetadata& remote_handle = fetch_remote_metadata(); - if (remote_handle.status != RemoteMetadataStatus::AVAILABLE){ - switch (remote_handle.status){ - case RemoteMetadataStatus::UNINITIALIZED: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_download: Remote metadata uninitialized."); - case RemoteMetadataStatus::NOT_AVAILABLE: - cout << "run_download: Download not available. Cancel download." << endl; - m_enabled = true; - emit download_finished(); - return; - default: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "run_download: Unknown enum."); - } - } - - // Download is available - - DownloadedResourceMetadata metadata = remote_handle.metadata; - std::string url = metadata.url; - std::string resource_name = metadata.resource_name; - qint64 expected_size = metadata.size_compressed_bytes; - FileDownloader::download_file_to_disk( - logger, - url, - DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip", - expected_size, - [this](int percentage_progress){ - download_progress(percentage_progress); - } - ); - - cout << "Done Download" << endl; - - m_enabled = true; - emit download_finished(); - }catch(...){ - m_enabled = true; - // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); - // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; - // QMessageBox box; - // box.warning(nullptr, "Error:", - // QString::fromStdString("Error: Unknown error. Embedding session failed.")); - return; - } +void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metadata){ + Logger& logger = global_logger_tagged(); + + std::string url = resource_metadata.url; + std::string resource_name = resource_metadata.resource_name; + qint64 expected_size = resource_metadata.size_compressed_bytes; + FileDownloader::download_file_to_disk( + logger, + url, + DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip", + expected_size, + [this](int percentage_progress){ + download_progress(percentage_progress); } ); - -} - -ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) -{} - - -ResourceProgressBar::ResourceProgressBar(ResourceDownloadRow& p_row) - : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) - , row(p_row) -{} - - - -ResourceDownloadRow::~ResourceDownloadRow(){} -ResourceDownloadRow::ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, - bool is_downloaded, - std::optional version_num, - ResourceVersionStatus version_status -) - : StaticTableRow(resource_name) - , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) - , m_download_button(*this) - , m_delete_button(*this) - , m_progress_bar(*this) -{ - PA_ADD_STATIC(m_data->m_resource_name); - PA_ADD_STATIC(m_data->m_file_size_label); - PA_ADD_STATIC(m_data->m_is_downloaded_label); - PA_ADD_STATIC(m_data->m_version_status_label); - - PA_ADD_STATIC(m_download_button); - PA_ADD_STATIC(m_delete_button); - PA_ADD_STATIC(m_progress_bar); } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 50e74de4cb..26627648e7 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -9,104 +9,54 @@ #include #include "Common/Cpp/Containers/Pimpl.h" -#include "Common/Cpp/Concurrency/AsyncTask.h" -#include "CommonFramework/Tools/GlobalThreadPools.h" +// #include "Common/Cpp/Concurrency/AsyncTask.h" +// #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" #include "ResourceDownloadHelpers.h" +#include "ResourceDownloadOptions.h" namespace PokemonAutomation{ -class ResourceDownloadRow; -class ResourceDownloadButton : public QObject, public ConfigOptionImpl{ +class ResourceDownloadRow : public QObject, public StaticTableRow{ Q_OBJECT public: - ~ResourceDownloadButton(); - ResourceDownloadButton(ResourceDownloadRow& p_row); + ~ResourceDownloadRow(); + ResourceDownloadRow( + std::string&& resource_name, + size_t file_size, + bool is_downloaded, + std::optional version_num, + ResourceVersionStatus version_status + ); signals: - void metadata_fetch_finished(std::string popup_message); void download_progress(int percentage); - void exception_caught(std::string function_name); - void download_finished(); -public: - enum class RemoteMetadataStatus{ - UNINITIALIZED, - NOT_AVAILABLE, - AVAILABLE, - }; - struct RemoteMetadata { - RemoteMetadataStatus status = RemoteMetadataStatus::UNINITIALIZED; - DownloadedResourceMetadata metadata; - }; +public: // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row void initialize_remote_metadata(); RemoteMetadata& fetch_remote_metadata(); DownloadedResourceMetadata initialize_local_metadata(); - void ensure_remote_metadata_loaded(); - std::string predownload_warning_summary(RemoteMetadata& remote_metadata); - - void run_download(); - inline bool get_enabled(){ return m_enabled; } - inline void set_enabled(bool enabled){ - m_enabled = enabled; - } + void run_download(DownloadedResourceMetadata resource_metadata); public: - ResourceDownloadRow& row; std::once_flag init_flag; - std::unique_ptr m_remote_metadata; - -private: - bool m_enabled; // button should be blocked during an active task. m_enabled is false when blocked - DownloadedResourceMetadata m_local_metadata; - AsyncTask m_worker1; - AsyncTask m_worker2; - - - - -}; - -class ResourceDeleteButton : public ConfigOptionImpl{ -public: - ResourceDeleteButton(ResourceDownloadRow& p_row); - - ResourceDownloadRow& row; -}; - -class ResourceProgressBar : public ConfigOptionImpl{ -public: - ResourceProgressBar(ResourceDownloadRow& p_row); - - ResourceDownloadRow& row; -}; - - - -class ResourceDownloadRow : public StaticTableRow{ - -public: - ~ResourceDownloadRow(); - ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, - bool is_downloaded, - std::optional version_num, - ResourceVersionStatus version_status - ); -public: struct Data; Pimpl m_data; ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; ResourceProgressBar m_progress_bar; + DownloadedResourceMetadata m_local_metadata; + + std::unique_ptr m_remote_metadata; + + }; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index d0b4cb110e..39c177b66c 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -67,7 +67,7 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt ); connect( - &m_value, &ResourceDownloadButton::download_progress, + &m_value.row, &ResourceDownloadRow::download_progress, this, [this](int percentage_progress){ // Simple Console Progress Bar std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') @@ -136,7 +136,7 @@ void DownloadButtonWidget::show_download_confirm_box( if (clicked == ok){ cout << "Clicked Ok to Download" << endl; - m_value.run_download(); + m_value.start_download(); return; } if (clicked == cancel){ diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index d420bcf833..b9b3ef6d3c 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -489,6 +489,8 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Recording/StreamRecorder.h Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h + Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp + Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp From a0a70d20fc6ff72f7be4ab6f066d0c8c0dc73621 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 12:34:50 -0700 Subject: [PATCH 27/53] more refactor of ResourceDownloadRow class. pass local_metadata to its constructor --- .../ResourceDownload/ResourceDownloadRow.cpp | 53 +++++++++---------- .../ResourceDownload/ResourceDownloadRow.h | 11 ++-- .../ResourceDownloadTable.cpp | 2 +- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 1b780fa74f..fd830fd4a0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -46,7 +46,7 @@ std::string resource_version_to_string(ResourceVersionStatus version){ struct ResourceDownloadRow::Data{ Data( - std::string&& resource_name, + std::string& resource_name, size_t file_size, bool is_downloaded, std::optional version_num, @@ -80,18 +80,17 @@ struct ResourceDownloadRow::Data{ ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, + DownloadedResourceMetadata local_metadata, bool is_downloaded, std::optional version_num, ResourceVersionStatus version_status ) - : StaticTableRow(resource_name) - , m_data(CONSTRUCT_TOKEN, std::move(resource_name), file_size, is_downloaded, version_num, version_status) + : StaticTableRow(local_metadata.resource_name) + , m_local_metadata(local_metadata) + , m_data(CONSTRUCT_TOKEN, local_metadata.resource_name, local_metadata.size_decompressed_bytes, is_downloaded, version_num, version_status) , m_download_button(*this) , m_delete_button(*this) , m_progress_bar(*this) - , m_local_metadata(initialize_local_metadata()) { PA_ADD_STATIC(m_data->m_resource_name); PA_ADD_STATIC(m_data->m_file_size_label); @@ -110,8 +109,6 @@ void ResourceDownloadRow::initialize_remote_metadata(){ RemoteMetadataStatus status = RemoteMetadataStatus::NOT_AVAILABLE; std::vector all_remote_metadata = remote_resource_download_list(); - cout << "done remote_resource_download_list" << endl; - std::string resource_name = m_data->m_resource_name.text(); for (DownloadedResourceMetadata remote_metadata : all_remote_metadata){ @@ -133,27 +130,27 @@ RemoteMetadata& ResourceDownloadRow::fetch_remote_metadata(){ return *m_remote_metadata; } -DownloadedResourceMetadata ResourceDownloadRow::initialize_local_metadata(){ - DownloadedResourceMetadata corresponding_local_metadata; - std::vector all_local_metadata = local_resource_download_list(); +// DownloadedResourceMetadata ResourceDownloadRow::initialize_local_metadata(){ +// DownloadedResourceMetadata corresponding_local_metadata; +// std::vector all_local_metadata = local_resource_download_list(); - std::string resource_name = m_data->m_resource_name.text(); - - bool found = false; - for (DownloadedResourceMetadata local_metadata : all_local_metadata){ - if (local_metadata.resource_name == resource_name){ - corresponding_local_metadata = local_metadata; - found = true; - break; - } - } - - if (!found){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "initialize_local_metadata: Corresponding DownloadedResourceMetadata not found in the local JSON file."); - } - - return corresponding_local_metadata; -} +// std::string resource_name = m_data->m_resource_name.text(); + +// bool found = false; +// for (DownloadedResourceMetadata local_metadata : all_local_metadata){ +// if (local_metadata.resource_name == resource_name){ +// corresponding_local_metadata = local_metadata; +// found = true; +// break; +// } +// } + +// if (!found){ +// throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "initialize_local_metadata: Corresponding DownloadedResourceMetadata not found in the local JSON file."); +// } + +// return corresponding_local_metadata; +// } void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metadata){ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 26627648e7..b3b78b89a8 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -24,8 +24,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ public: ~ResourceDownloadRow(); ResourceDownloadRow( - std::string&& resource_name, - size_t file_size, + DownloadedResourceMetadata local_metadata, bool is_downloaded, std::optional version_num, ResourceVersionStatus version_status @@ -39,12 +38,16 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row void initialize_remote_metadata(); RemoteMetadata& fetch_remote_metadata(); - DownloadedResourceMetadata initialize_local_metadata(); + // DownloadedResourceMetadata initialize_local_metadata(); void run_download(DownloadedResourceMetadata resource_metadata); public: + DownloadedResourceMetadata m_local_metadata; + +private: std::once_flag init_flag; + std::unique_ptr m_remote_metadata; struct Data; Pimpl m_data; @@ -52,9 +55,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; ResourceProgressBar m_progress_bar; - DownloadedResourceMetadata m_local_metadata; - std::unique_ptr m_remote_metadata; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 30639d2c59..49fe551936 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -41,7 +41,7 @@ std::vector> get_resource_download_rows(){ ResourceVersionStatus version_status = get_version_status(expected_version_num, current_version_num); - resource_rows.emplace_back(std::make_unique(std::move(resource_name), resource.size_decompressed_bytes, is_downloaded, current_version_num, version_status)); + resource_rows.emplace_back(std::make_unique(resource, is_downloaded, current_version_num, version_status)); } return resource_rows; From 5f03bb36a94df5518559a668a5fafef3966deaf9 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 15:35:31 -0700 Subject: [PATCH 28/53] working implementation of Progress bar. fix inheritance for DownloadWidget classes. --- .../ResourceDownload/ResourceDownloadRow.cpp | 1 + .../ResourceDownloadWidget.cpp | 44 +++++++++++++------ .../ResourceDownload/ResourceDownloadWidget.h | 6 ++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index fd830fd4a0..130af8a691 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -155,6 +155,7 @@ RemoteMetadata& ResourceDownloadRow::fetch_remote_metadata(){ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metadata){ Logger& logger = global_logger_tagged(); + // std::this_thread::sleep_for(std::chrono::seconds(5)); std::string url = resource_metadata.url; std::string resource_name = resource_metadata.resource_name; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 39c177b66c..4a02ce3ceb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -24,14 +24,19 @@ namespace PokemonAutomation{ template class RegisterConfigWidget; DownloadButtonWidget::~DownloadButtonWidget(){ + // cout << "Destructor for DownloadButtonWidget" << endl; + // m_value.disconnect(this); } DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) - : ConfigWidget(value) + : QWidget(&parent) + , ConfigWidget(value, *this) , m_value(value) { m_button = new QPushButton(&parent); m_widget = m_button; + // cout << "Constructor for DownloadButtonWidget" << endl; + QFont font; font.setBold(true); m_button->setFont(font); @@ -66,21 +71,13 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt } ); - connect( - &m_value.row, &ResourceDownloadRow::download_progress, - this, [this](int percentage_progress){ - // Simple Console Progress Bar - std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') - << std::string(20 - (percentage_progress / 5), ' ') << "] " - << percentage_progress << "%" << endl; - - } - ); // when the download is finished, update the UI to re-enable the button connect( &m_value, &ResourceDownloadButton::download_finished, - this, &DownloadButtonWidget::update_enabled_status + this, [this](){ + update_enabled_status(); + } ); // if the thread catches an exception, show an error box @@ -158,7 +155,8 @@ void show_error_box(std::string function_name){ template class RegisterConfigWidget; DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) - : ConfigWidget(value) + : QWidget(&parent) + , ConfigWidget(value, *this) { QPushButton* button = new QPushButton(&parent); m_widget = button; @@ -178,15 +176,22 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va template class RegisterConfigWidget; +ProgressBarWidget::~ProgressBarWidget(){ + // m_value.row.disconnect(this); + // cout << "Destructor for ProgressBarWidget" << endl; + +} ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value) : QWidget(&parent) , ConfigWidget(value, *this) + , m_value(value) { // 1. Instantiate the widgets m_status_label = new QLabel("Ready", this); m_progress_bar = new QProgressBar(this); + // cout << "Constructor for ProgressBarWidget" << endl; // 2. Configure the progress bar m_progress_bar->setRange(0, 100); @@ -199,6 +204,19 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value layout->addWidget(m_progress_bar); this->setLayout(layout); + + connect( + &m_value.row, &ResourceDownloadRow::download_progress, + this, + [this](int percentage_progress){ + m_progress_bar->setValue(percentage_progress); + // Simple Console Progress Bar + // std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') + // << std::string(20 - (percentage_progress / 5), ' ') << "] " + // << percentage_progress << "%" << endl; + + } + ); } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 0fa3f38178..6d6167d4e9 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -18,7 +18,7 @@ namespace PokemonAutomation{ // class ResourceDownloadButton; -class DownloadButtonWidget : public QObject, public ConfigWidget{ +class DownloadButtonWidget : public QWidget, public ConfigWidget{ Q_OBJECT public: using ParentOption = ResourceDownloadButton; @@ -42,7 +42,7 @@ class DownloadButtonWidget : public QObject, public ConfigWidget{ void show_error_box(std::string function_name); -class DeleteButtonWidget : public ConfigWidget{ +class DeleteButtonWidget : public QWidget, public ConfigWidget{ public: using ParentOption = ResourceDeleteButton; @@ -55,9 +55,11 @@ class ProgressBarWidget : public QWidget, public ConfigWidget{ using ParentOption = ResourceProgressBar; public: + ~ProgressBarWidget(); ProgressBarWidget(QWidget& parent, ResourceProgressBar& value); private: + ResourceProgressBar& m_value; QLabel* m_status_label; QProgressBar* m_progress_bar; }; From 0c2bfc5e4d21d6e29274480912a6156db9abbaf9 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 16:39:54 -0700 Subject: [PATCH 29/53] download failed pop-up if internet disconnected. --- .../ResourceDownloadHelpers.cpp | 8 ++--- .../ResourceDownloadOptions.cpp | 21 ++++++------ .../ResourceDownloadOptions.h | 1 + .../ResourceDownloadWidget.cpp | 32 +++++++++++++++---- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index dc7d8dbcc4..a233edbdec 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -80,15 +80,11 @@ const std::vector& local_resource_download_list(){ JsonValue fetch_resource_download_list_json_from_remote(){ Logger& logger = global_logger_tagged(); - JsonValue json; - try{ - json = FileDownloader::download_json_file( + JsonValue json = + FileDownloader::download_json_file( logger, "https://raw.githubusercontent.com/jw098/Packages/refs/heads/download/Resources/ResourceDownloadList.json" ); - }catch (OperationFailedException&){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "fetch_resource_download_list_json_from_remote: Failed to download JSON."); - } return json; } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp index 0480d7f898..bd2de28581 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp @@ -6,6 +6,7 @@ #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Exceptions.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" #include "ResourceDownloadRow.h" #include "ResourceDownloadOptions.h" @@ -49,14 +50,15 @@ void ResourceDownloadButton::ensure_remote_metadata_loaded(){ m_enabled = true; emit metadata_fetch_finished(predownload_warning); + }catch(OperationFailedException&){ + m_enabled = true; + // cout << "failed" << endl; + emit download_failed(); + return; }catch(...){ m_enabled = true; // cout << "Exception thrown in thread" << endl; emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); - // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; - // QMessageBox box; - // box.warning(nullptr, "Error:", - // QString::fromStdString("Error: Unknown error. Embedding session failed.")); return; } @@ -138,14 +140,15 @@ void ResourceDownloadButton::start_download(){ m_enabled = true; emit download_finished(); + }catch(OperationFailedException&){ + m_enabled = true; + // cout << "failed" << endl; + emit download_failed(); + return; }catch(...){ m_enabled = true; // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); - // std::cerr << "Error: Unknown error. Embedding session failed." << std::endl; - // QMessageBox box; - // box.warning(nullptr, "Error:", - // QString::fromStdString("Error: Unknown error. Embedding session failed.")); + emit exception_caught("ResourceDownloadButton::start_download"); return; } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h index 099ac0b80d..a777b750eb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h @@ -28,6 +28,7 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl; DownloadButtonWidget::~DownloadButtonWidget(){ @@ -90,6 +106,16 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt show_error_box(function_name); } ); + + // if download fails + connect( + &m_value, &ResourceDownloadButton::download_failed, + this, [this](){ + m_value.set_enabled(true); + update_enabled_status(); + show_download_failed_box(); + } + ); } @@ -143,13 +169,7 @@ void DownloadButtonWidget::show_download_confirm_box( } } -void show_error_box(std::string function_name){ - std::cerr << "Error: Exception thrown in thread. From " + function_name + ". Report this as a bug." << std::endl; - QMessageBox box; - box.warning(nullptr, "Error:", - QString::fromStdString("Error: Exception thrown in thread. From " + function_name + ". Report this as a bug.")); -} From 5a771d20a252f96f0b0cf4fb6e4567a45842f778 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 21:30:11 -0700 Subject: [PATCH 30/53] delete folder prior to downloading. create parent directory during the download, if needed. --- .../ResourceDownload/ResourceDownloadRow.cpp | 14 +++++++++-- .../CommonFramework/Tools/FileDownloader.cpp | 24 ++++++++++++------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 130af8a691..c64df1eb7d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -15,6 +15,7 @@ // #include // #include +#include #include using std::cout; @@ -22,6 +23,8 @@ using std::endl; namespace PokemonAutomation{ + namespace fs = std::filesystem; + ///////////////////////////////////////////////////////////////////////////////////////////////////////// // ResourceDownloadRow ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -156,14 +159,21 @@ RemoteMetadata& ResourceDownloadRow::fetch_remote_metadata(){ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metadata){ Logger& logger = global_logger_tagged(); // std::this_thread::sleep_for(std::chrono::seconds(5)); - + std::string url = resource_metadata.url; std::string resource_name = resource_metadata.resource_name; qint64 expected_size = resource_metadata.size_compressed_bytes; + + std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; + + // delete directory and the old resource + fs::remove_all(resource_directory); + + // download FileDownloader::download_file_to_disk( logger, url, - DOWNLOADED_RESOURCE_PATH() + resource_name + "/temp.zip", + resource_directory + "/temp.zip", expected_size, [this](int percentage_progress){ download_progress(percentage_progress); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp index c650db0264..30de48bc23 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "Common/Cpp/Json/JsonValue.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "FileDownloader.h" @@ -101,6 +103,12 @@ void download_file_to_disk( // cout << "download_file()" << endl; QNetworkAccessManager network_access_manager; QEventLoop loop; + + // ensure the directory exists + QString filePath = QString::fromStdString(file_path); + QFileInfo fileInfo(filePath); + QString dirPath = fileInfo.absolutePath(); + QDir().mkpath(dirPath); // 1. Initialize QSaveFile QSaveFile file(QString::fromStdString(file_path)); @@ -110,7 +118,7 @@ void download_file_to_disk( } QNetworkRequest request(QUrl(QString::fromStdString(url))); - + // request.setAttribute(QNetworkRequest::AutoRedirectionPolicyAttribute, true); // enable auto-redirects request.setTransferTimeout(std::chrono::seconds(5)); // 2. Start the GET request @@ -123,10 +131,10 @@ void download_file_to_disk( // Use expected_size if the network doesn't provide one qint64 total = (bytesTotal > 0) ? bytesTotal : expected_size; - int percentage_progress = static_cast((bytesReceived * 100) / total); - progress_callback(percentage_progress); - - }); + int percentage_progress = (total > 0) ? static_cast((bytesReceived * 100) / total) : 0; + progress_callback(std::min(percentage_progress, 100)); + } + ); // 3. Stream chunks directly to the temporary file QObject::connect(reply, &QNetworkReply::readyRead, [&file, reply]() { @@ -142,9 +150,9 @@ void download_file_to_disk( loop.exec(); // // Final check for remaining data - // if (reply->bytesAvailable() > 0) { - // file.write(reply->readAll()); - // } + if (reply->bytesAvailable() > 0) { + file.write(reply->readAll()); + } // 5. Finalize the transaction if (reply->error() == QNetworkReply::NoError) { From 38b979251985bafe9000cf01fbba305f763e720f Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 22:01:47 -0700 Subject: [PATCH 31/53] catch errors with get_resource_version_num() --- .../ResourceDownloadHelpers.cpp | 17 +++++++++++------ .../ResourceDownload/ResourceDownloadHelpers.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index a233edbdec..2e551255c0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -101,14 +101,19 @@ const std::vector& remote_resource_download_list(){ return remote_resources; } -uint16_t get_resource_version_num(Filesystem::Path folder_path){ - std::string file_name = folder_path.string() + "/version.json"; - const JsonValue& json = load_json_file(file_name); +std::optional get_resource_version_num(Filesystem::Path folder_path){ + try{ + std::string file_name = folder_path.string() + "/version.json"; + const JsonValue& json = load_json_file(file_name); - const JsonObject& obj = json.to_object_throw(); - uint16_t version_num = (uint16_t)obj.get_integer_throw("version"); + const JsonObject& obj = json.to_object_throw(); + uint16_t version_num = (uint16_t)obj.get_integer_throw("version"); + return version_num; + }catch(...){ + std::cerr << "Unable to determine the version number from version.json." << endl; + return std::nullopt; + } - return version_num; } ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num){ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h index 9855ff63f3..25372ddc2c 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h @@ -53,7 +53,7 @@ struct RemoteMetadata { const std::vector& local_resource_download_list(); const std::vector& remote_resource_download_list(); -uint16_t get_resource_version_num(Filesystem::Path folder_path); +std::optional get_resource_version_num(Filesystem::Path folder_path); ResourceVersionStatus get_version_status(uint16_t expected_version_num, std::optional current_version_num); } From 746eff76c317c754ae752711ec1b256749efb53c Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 22:36:18 -0700 Subject: [PATCH 32/53] add unzip progress bar --- .../ResourceDownload/ResourceDownloadRow.cpp | 19 +- .../ResourceDownload/ResourceDownloadRow.h | 1 + .../ResourceDownloadWidget.cpp | 11 + .../CommonFramework/Tools/FileUnzip.cpp | 310 +++++++++--------- .../Source/CommonFramework/Tools/FileUnzip.h | 7 +- 5 files changed, 193 insertions(+), 155 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index c64df1eb7d..343eb4869a 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -9,6 +9,7 @@ #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Logging/Logger.h" #include "CommonFramework/Tools/FileDownloader.h" +#include "CommonFramework/Tools/FileUnzip.h" #include "CommonFramework/Options/LabelCellOption.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" @@ -169,16 +170,30 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad // delete directory and the old resource fs::remove_all(resource_directory); - // download + // download + std::string zip_path = resource_directory + "/temp.zip"; FileDownloader::download_file_to_disk( logger, url, - resource_directory + "/temp.zip", + zip_path, expected_size, [this](int percentage_progress){ download_progress(percentage_progress); } ); + + // unzip + unzip_file( + zip_path.c_str(), + resource_directory.c_str(), + [this](int percentage_progress){ + unzip_progress(percentage_progress); + } + ); + + // delete old zip file + fs::remove(zip_path); + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index b3b78b89a8..398d122a93 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -32,6 +32,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ signals: void download_progress(int percentage); + void unzip_progress(int percentage); public: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 1c055aacff..604beebbcc 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -229,6 +229,7 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value &m_value.row, &ResourceDownloadRow::download_progress, this, [this](int percentage_progress){ + m_status_label->setText("Downloading:"); m_progress_bar->setValue(percentage_progress); // Simple Console Progress Bar // std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') @@ -237,6 +238,16 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value } ); + + + connect( + &m_value.row, &ResourceDownloadRow::unzip_progress, + this, + [this](int percentage_progress){ + m_status_label->setText("Unzipping:"); + m_progress_bar->setValue(percentage_progress); + } + ); } diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index c56fd9ccf9..9e69fdad99 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -26,178 +26,184 @@ using std::endl; namespace PokemonAutomation{ - namespace fs = std::filesystem; - - struct ProgressData { - std::ofstream* out_file; - uint64_t total_bytes; - uint64_t processed_bytes; - int last_percentage; - }; - - // Callback triggered for every chunk of decompressed data - // pOpaque is an opaque pointer that actually represents ProgressData - size_t write_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n){ - ProgressData* data = static_cast(pOpaque); - - // 1. Check if we actually need to seek - // tellp() returns the current 'put' position. get the current position of the write pointer in an output stream. - if (static_cast(data->out_file->tellp()) != file_ofs){ - data->out_file->seekp(file_ofs); - } - - // Write chunk to disk - data->out_file->write(static_cast(pBuf), n); +namespace fs = std::filesystem; + +struct ProgressData { + std::ofstream* out_file; + uint64_t total_bytes; + uint64_t processed_bytes; + int last_percentage; + std::function progress_callback; +}; + +// Callback triggered for every chunk of decompressed data +// pOpaque is an opaque pointer that actually represents ProgressData +size_t write_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n){ + ProgressData* data = static_cast(pOpaque); + + // 1. Check if we actually need to seek + // tellp() returns the current 'put' position. get the current position of the write pointer in an output stream. + if (static_cast(data->out_file->tellp()) != file_ofs){ + data->out_file->seekp(file_ofs); + } - // Update and display progress - data->processed_bytes += n; - double percent = (double)data->processed_bytes / data->total_bytes * 100.0; - int current_percent = static_cast(percent); - - // Only print if the integer value has changed - if (current_percent > data->last_percentage){ - data->last_percentage = current_percent; - std::cout << "\rProgress: " << current_percent << "% (" - << data->processed_bytes << "/" << data->total_bytes << " bytes)" << endl; - } - - return n; + // Write chunk to disk + data->out_file->write(static_cast(pBuf), n); + + // Update and display progress + data->processed_bytes += n; + double percent = (double)data->processed_bytes / data->total_bytes * 100.0; + int current_percent = static_cast(percent); + + // Only print if the integer value has changed + if (current_percent > data->last_percentage){ + data->progress_callback(data->last_percentage); + // data->last_percentage = current_percent; + // std::cout << "\rProgress: " << current_percent << "% (" + // << data->processed_bytes << "/" << data->total_bytes << " bytes)" << endl; } - - // ensure that entry_name is inside target_dir, to prevent path traversal attacks. - bool is_safe(const std::string& target_dir, const std::string& entry_name){ - try { - // 1. Get absolute, normalized paths - // handles symlinks. and resolves .. and . components. throws error if path doesn't exist - Filesystem::Path base = fs::canonical(Filesystem::Path(target_dir)); - // confirms that base is a directory, and not a file - if (!fs::is_directory(base)) return false; - // resolves .. and . components and returns an absolute path without requiring the final path to exist. - fs::path target = fs::weakly_canonical(Filesystem::Path(base / entry_name)); - - // cout << base << endl; - // cout << target << endl; - - // 2. Use lexically_relative to find the path from base to target - fs::path rel = target.lexically_relative(base); - - // 3. Validation: - // - If rel is empty, they are likely different roots - // - If rel starts with "..", it escaped the base - // - If rel is ".", it IS the base directory (usually safe) - if (rel.empty() || *rel.begin() == ".."){ - return false; - } - - return true; - } catch (...){ - cout << "target_dir path doesn't exist." << endl; + return n; +} + +// ensure that entry_name is inside target_dir, to prevent path traversal attacks. +bool is_safe(const std::string& target_dir, const std::string& entry_name){ + try { + // 1. Get absolute, normalized paths + // handles symlinks. and resolves .. and . components. throws error if path doesn't exist + Filesystem::Path base = fs::canonical(Filesystem::Path(target_dir)); + // confirms that base is a directory, and not a file + if (!fs::is_directory(base)) return false; + + // resolves .. and . components and returns an absolute path without requiring the final path to exist. + fs::path target = fs::weakly_canonical(Filesystem::Path(base / Filesystem::Path(entry_name))); + + // cout << base << endl; + // cout << target << endl; + + // 2. Use lexically_relative to find the path from base to target + fs::path rel = target.lexically_relative(base); + + // 3. Validation: + // - If rel is empty, they are likely different roots + // - If rel starts with "..", it escaped the base + // - If rel is ".", it IS the base directory (usually safe) + if (rel.empty() || *rel.begin() == ".."){ return false; } - } - void unzip_file(const char* zip_path, const char* target_dir){ - Filesystem::Path p{zip_path}; - if (!fs::exists(p)){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that doesn't exist."); - } + return true; + } catch (...){ + cout << "target_dir path doesn't exist." << endl; + return false; + } +} - mz_zip_archive zip_archive; - memset(&zip_archive, 0, sizeof(zip_archive)); +void unzip_file( + const char* zip_path, + const char* target_dir, + std::function progress_callback +){ + Filesystem::Path p{zip_path}; + if (!fs::exists(p)){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that doesn't exist."); + } + + mz_zip_archive zip_archive; + memset(&zip_archive, 0, sizeof(zip_archive)); + + // Opens the ZIP file at zip_path + // zip_archive holds the state and metadata of the ZIP archive. + if (!mz_zip_reader_init_file(&zip_archive, zip_path, 0)){ + cout << "failed to run mz_zip_reader_init_file" << endl; + cout << "mz_zip_error: " << mz_zip_get_last_error(&zip_archive) << endl; + return; + } + + // Get total number of files in the archive + int num_files = (int)mz_zip_reader_get_num_files(&zip_archive); + + // calculate total uncompressed size + uint64_t total_uncompressed_size = 0; + for (int i = 0; i < num_files; i++){ + mz_zip_archive_file_stat file_stat; // holds info on the specific file + + // fills file_stat with the data for the current index + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue; + + cout << std::to_string(file_stat.m_uncomp_size) << endl; + total_uncompressed_size += file_stat.m_uncomp_size; + } - // Opens the ZIP file at zip_path - // zip_archive holds the state and metadata of the ZIP archive. - if (!mz_zip_reader_init_file(&zip_archive, zip_path, 0)){ - cout << "failed to run mz_zip_reader_init_file" << endl; - cout << "mz_zip_error: " << mz_zip_get_last_error(&zip_archive) << endl; - return; - } + uint64_t total_processed_bytes = 0; + for (int i = 0; i < num_files; i++){ + mz_zip_archive_file_stat file_stat; // holds info on the specific file - // Get total number of files in the archive - int num_files = (int)mz_zip_reader_get_num_files(&zip_archive); + // fills file_stat with the data for the current index + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue; - // calculate total uncompressed size - uint64_t total_uncompressed_size = 0; - for (int i = 0; i < num_files; i++){ - mz_zip_archive_file_stat file_stat; // holds info on the specific file + // Checks if the current entry is a folder. Miniz treats folders as entries; + // this code skips them to avoid trying to "write" a folder as if it were a file. + if (mz_zip_reader_is_file_a_directory(&zip_archive, i)){ + continue; + } - // fills file_stat with the data for the current index - if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue; + // Construct your output path (e.g., target_dir + file_stat.m_filename) + std::string out_path = std::string(target_dir) + "/" + file_stat.m_filename; + Filesystem::Path const parent_dir{Filesystem::Path(out_path).parent_path()}; - cout << std::to_string(file_stat.m_uncomp_size) << endl; - total_uncompressed_size += file_stat.m_uncomp_size; + // Create the entire directory, including intermediate directories for this file + std::error_code ec{}; + fs::create_directories(parent_dir, ec); + if (ec){ + std::cerr << "Error creating " << parent_dir << ": " << ec.message() << std::endl; + ec.clear(); } - uint64_t total_processed_bytes = 0; - for (int i = 0; i < num_files; i++){ - mz_zip_archive_file_stat file_stat; // holds info on the specific file - - // fills file_stat with the data for the current index - if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue; - - // Checks if the current entry is a folder. Miniz treats folders as entries; - // this code skips them to avoid trying to "write" a folder as if it were a file. - if (mz_zip_reader_is_file_a_directory(&zip_archive, i)){ - continue; - } - - // Construct your output path (e.g., target_dir + file_stat.m_filename) - std::string out_path = std::string(target_dir) + "/" + file_stat.m_filename; - Filesystem::Path const parent_dir{Filesystem::Path(out_path).parent_path()}; - - // Create the entire directory, including intermediate directories for this file - std::error_code ec{}; - fs::create_directories(parent_dir, ec); - if (ec){ - std::cerr << "Error creating " << parent_dir << ": " << ec.message() << std::endl; - ec.clear(); - } - - // ensure that entry_name is inside target_dir. to prevent path traversal attacks. - if (!is_safe(target_dir, file_stat.m_filename)){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); - } - - std::ofstream out_file(out_path, std::ios::binary); // std::ios::binary is to prevent line-ending conversions. - ProgressData progress = { &out_file, total_uncompressed_size, total_processed_bytes, -1 }; - - // Extract using the callback - // decompresses the file in chunks and repeatedly calls write_callback to save those chunks to the disk via the out_file - mz_zip_reader_extract_to_callback(&zip_archive, i, write_callback, &progress, 0); - std::cout << "\nFinished: " << file_stat.m_filename << std::endl; - total_processed_bytes += file_stat.m_uncomp_size; + // ensure that entry_name is inside target_dir. to prevent path traversal attacks. + if (!is_safe(target_dir, file_stat.m_filename)){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); } - - mz_zip_reader_end(&zip_archive); + + std::ofstream out_file(out_path, std::ios::binary); // std::ios::binary is to prevent line-ending conversions. + ProgressData progress = { &out_file, total_uncompressed_size, total_processed_bytes, -1, progress_callback }; + + // Extract using the callback + // decompresses the file in chunks and repeatedly calls write_callback to save those chunks to the disk via the out_file + mz_zip_reader_extract_to_callback(&zip_archive, i, write_callback, &progress, 0); + std::cout << "\nFinished: " << file_stat.m_filename << std::endl; + total_processed_bytes += file_stat.m_uncomp_size; } - // void unzip_file(const std::string& zip_path, const std::string& output_dir){ - // cout << "try to unzip the file." << endl; - // miniz_cpp::zip_file archive(zip_path); - - // // create folder structure before extracting. - // // since miniz-cpp does not automatically create subdirectories if they exist within the zip archive - // std::vector const info_list = archive.infolist(); - // auto const current_directory = std::filesystem::current_path(); - // std::error_code ec{}; - // for(miniz_cpp::zip_info const & info: info_list ){ - // std::filesystem::path const p{(std::filesystem::path(output_dir) / info.filename).parent_path()}; - // // Create the entire directory tree for this file - // std::filesystem::create_directories(p, ec); - - // if (ec){ - // std::cerr << "Error creating " << p << ": " << ec.message() << std::endl; - // ec.clear(); - // } - // } + mz_zip_reader_end(&zip_archive); +} + +// void unzip_file(const std::string& zip_path, const std::string& output_dir){ +// cout << "try to unzip the file." << endl; +// miniz_cpp::zip_file archive(zip_path); + +// // create folder structure before extracting. +// // since miniz-cpp does not automatically create subdirectories if they exist within the zip archive +// std::vector const info_list = archive.infolist(); +// auto const current_directory = std::filesystem::current_path(); +// std::error_code ec{}; +// for(miniz_cpp::zip_info const & info: info_list ){ +// std::filesystem::path const p{(std::filesystem::path(output_dir) / info.filename).parent_path()}; +// // Create the entire directory tree for this file +// std::filesystem::create_directories(p, ec); + +// if (ec){ +// std::cerr << "Error creating " << p << ": " << ec.message() << std::endl; +// ec.clear(); +// } +// } - // // Extract all files to the specified path - // archive.extractall(output_dir); +// // Extract all files to the specified path +// archive.extractall(output_dir); - // cout << "done unzipping the file." << endl; +// cout << "done unzipping the file." << endl; - // } +// } } diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h index a2b509bdba..0f70a9dfd4 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h @@ -8,10 +8,15 @@ #define PokemonAutomation_FileUnzip_H #include +#include namespace PokemonAutomation{ -void unzip_file(const char* zip_path, const char* target_dir); +void unzip_file( + const char* zip_path, + const char* target_dir, + std::function progress_callback +); } #endif From 03689ac66f83c0166d57e6f607b39891fb16ca38 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 23:02:54 -0700 Subject: [PATCH 33/53] minor UI changes with the progress bar --- .../ResourceDownload/ResourceDownloadWidget.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 604beebbcc..2a896f5a2b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -208,7 +208,7 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value { // 1. Instantiate the widgets - m_status_label = new QLabel("Ready", this); + m_status_label = new QLabel("", this); m_progress_bar = new QProgressBar(this); // cout << "Constructor for ProgressBarWidget" << endl; @@ -217,6 +217,7 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value m_progress_bar->setRange(0, 100); m_progress_bar->setValue(0); m_progress_bar->setTextVisible(true); // Shows % inside the bar + m_progress_bar->hide(); // 3. Create a horizontal layout to hold them QHBoxLayout *layout = new QHBoxLayout(); @@ -224,11 +225,15 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value layout->addWidget(m_progress_bar); this->setLayout(layout); + this->setMinimumWidth(170); connect( &m_value.row, &ResourceDownloadRow::download_progress, this, [this](int percentage_progress){ + if (m_progress_bar->isHidden()) { + m_progress_bar->show(); // Make it visible when progress starts + } m_status_label->setText("Downloading:"); m_progress_bar->setValue(percentage_progress); // Simple Console Progress Bar @@ -244,6 +249,9 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value &m_value.row, &ResourceDownloadRow::unzip_progress, this, [this](int percentage_progress){ + if (m_progress_bar->isHidden()) { + m_progress_bar->show(); // Make it visible when progress starts + } m_status_label->setText("Unzipping:"); m_progress_bar->setValue(percentage_progress); } From f77790326998b38f50b01b88c25e3373751c3b74 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 23:13:44 -0700 Subject: [PATCH 34/53] minor fix --- SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index 9e69fdad99..520475b92d 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -57,7 +57,7 @@ size_t write_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_ // Only print if the integer value has changed if (current_percent > data->last_percentage){ - data->progress_callback(data->last_percentage); + data->progress_callback(current_percent); // data->last_percentage = current_percent; // std::cout << "\rProgress: " << current_percent << "% (" // << data->processed_bytes << "/" << data->total_bytes << " bytes)" << endl; From 7d125a81f2530458c48bb92d015f1707c4de8bb4 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 31 Mar 2026 21:40:50 -0700 Subject: [PATCH 35/53] update the Version and Downloaded column after downloading --- .../ResourceDownload/ResourceDownloadRow.cpp | 21 ++++++++++++++++++- .../ResourceDownload/ResourceDownloadRow.h | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 343eb4869a..df2529c269 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -48,6 +48,10 @@ std::string resource_version_to_string(ResourceVersionStatus version){ } } +std::string is_downloaded_string(bool is_downloaded){ + return is_downloaded ? "Yes" : "--"; +} + struct ResourceDownloadRow::Data{ Data( std::string& resource_name, @@ -60,7 +64,7 @@ struct ResourceDownloadRow::Data{ , m_file_size(file_size) , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) , m_is_downloaded(is_downloaded) - , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded ? "Yes" : "--") + , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded_string(is_downloaded)) , m_version_num(version_num) , m_version_status(version_status) , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) @@ -81,6 +85,17 @@ struct ResourceDownloadRow::Data{ }; +void ResourceDownloadRow::set_version_status(ResourceVersionStatus version_status){ + m_data->m_version_status = version_status; + m_data->m_version_status_label.set_text(resource_version_to_string(version_status)); +} + + +void ResourceDownloadRow::set_is_downloaded(bool is_downloaded){ + m_data->m_is_downloaded = is_downloaded; + m_data->m_is_downloaded_label.set_text(is_downloaded_string(is_downloaded)); +} + ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( @@ -194,6 +209,10 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad // delete old zip file fs::remove(zip_path); + // update the table labels + set_is_downloaded(true); + set_version_status(ResourceVersionStatus::CURRENT); + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 398d122a93..bd8be39a5c 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -36,6 +36,9 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ public: + void set_version_status(ResourceVersionStatus version_status); + void set_is_downloaded(bool is_downloaded); + // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row void initialize_remote_metadata(); RemoteMetadata& fetch_remote_metadata(); From 4874eef5e4096fdd273459bc8b2415c17bc4bd79 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 31 Mar 2026 21:57:21 -0700 Subject: [PATCH 36/53] add cancel button --- .../ResourceDownloadOptions.cpp | 11 ++++++++++ .../ResourceDownloadOptions.h | 13 ++++++++++++ .../ResourceDownload/ResourceDownloadRow.cpp | 2 ++ .../ResourceDownload/ResourceDownloadRow.h | 1 + .../ResourceDownloadTable.cpp | 1 + .../ResourceDownloadWidget.cpp | 21 +++++++++++++++++++ .../ResourceDownload/ResourceDownloadWidget.h | 8 +++++++ 7 files changed, 57 insertions(+) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp index bd2de28581..25130c8228 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp @@ -163,6 +163,17 @@ void ResourceDownloadButton::start_download(){ ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) + , m_enabled(true) +{} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ResourceCancelButton +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +ResourceCancelButton::ResourceCancelButton(ResourceDownloadRow& p_row) + : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) + , row(p_row) + , m_enabled(true) {} diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h index a777b750eb..b9a893f8bf 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h @@ -61,6 +61,19 @@ class ResourceDeleteButton : public ConfigOptionImpl{ ResourceDeleteButton(ResourceDownloadRow& p_row); ResourceDownloadRow& row; + +private: + bool m_enabled; +}; + +class ResourceCancelButton : public ConfigOptionImpl{ +public: + ResourceCancelButton(ResourceDownloadRow& p_row); + + ResourceDownloadRow& row; + +private: + bool m_enabled; }; class ResourceProgressBar : public ConfigOptionImpl{ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index df2529c269..03eb2fae61 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -109,6 +109,7 @@ ResourceDownloadRow::ResourceDownloadRow( , m_data(CONSTRUCT_TOKEN, local_metadata.resource_name, local_metadata.size_decompressed_bytes, is_downloaded, version_num, version_status) , m_download_button(*this) , m_delete_button(*this) + , m_cancel_button(*this) , m_progress_bar(*this) { PA_ADD_STATIC(m_data->m_resource_name); @@ -118,6 +119,7 @@ ResourceDownloadRow::ResourceDownloadRow( PA_ADD_STATIC(m_download_button); PA_ADD_STATIC(m_delete_button); + PA_ADD_STATIC(m_cancel_button); PA_ADD_STATIC(m_progress_bar); } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index bd8be39a5c..e5b366fa3c 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -58,6 +58,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ ResourceDownloadButton m_download_button; ResourceDeleteButton m_delete_button; + ResourceCancelButton m_cancel_button; ResourceProgressBar m_progress_bar; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 49fe551936..79f8162744 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -71,6 +71,7 @@ std::vector ResourceDownloadTable::make_header() const{ "", "", "", + "", }; return ret; } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 2a896f5a2b..b8728b5773 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -195,6 +195,27 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va } +template class RegisterConfigWidget; +CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) + : QWidget(&parent) + , ConfigWidget(value, *this) +{ + QPushButton* button = new QPushButton(&parent); + m_widget = button; + + QFont font; + font.setBold(true); + button->setFont(font); + button->setText("Cancel"); + + button->connect( + button, &QPushButton::clicked, + button, [&](bool){ + cout << "Clicked Cancel Button" << endl; + } + ); +} + template class RegisterConfigWidget; ProgressBarWidget::~ProgressBarWidget(){ // m_value.row.disconnect(this); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 6d6167d4e9..ac609c287a 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -50,6 +50,14 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget{ DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); }; +class CancelButtonWidget : public QWidget, public ConfigWidget{ +public: + using ParentOption = ResourceCancelButton; + +public: + CancelButtonWidget(QWidget& parent, ResourceCancelButton& value); +}; + class ProgressBarWidget : public QWidget, public ConfigWidget{ public: using ParentOption = ResourceProgressBar; From abcd6e80b0e9149bfa335551fd116c2a9f657c38 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 15:08:31 -0700 Subject: [PATCH 37/53] add cancel callback to download routine --- .../ResourceDownloadOptions.cpp | 8 ++ .../ResourceDownload/ResourceDownloadRow.cpp | 82 ++++++++++++------- .../ResourceDownload/ResourceDownloadRow.h | 2 + .../ResourceDownloadWidget.cpp | 31 +++---- .../ResourceDownload/ResourceDownloadWidget.h | 8 ++ .../CommonFramework/Tools/FileDownloader.cpp | 24 ++++-- .../CommonFramework/Tools/FileDownloader.h | 3 +- 7 files changed, 105 insertions(+), 53 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp index 25130c8228..3ba53147d0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp @@ -139,7 +139,15 @@ void ResourceDownloadButton::start_download(){ cout << "Done Download" << endl; m_enabled = true; + row.set_cancel_action(false); emit download_finished(); + }catch(OperationCancelledException&){ + m_enabled = true; + + row.set_cancel_action(false); + + emit download_finished(); + return; }catch(OperationFailedException&){ m_enabled = true; // cout << "failed" << endl; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 03eb2fae61..66a9fb6bbb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -68,6 +68,7 @@ struct ResourceDownloadRow::Data{ , m_version_num(version_num) , m_version_status(version_status) , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) + , m_cancel_action(false) {} LabelCellOption m_resource_name; @@ -82,6 +83,8 @@ struct ResourceDownloadRow::Data{ ResourceVersionStatus m_version_status; LabelCellOption m_version_status_label; + std::atomic m_cancel_action; + }; @@ -96,6 +99,10 @@ void ResourceDownloadRow::set_is_downloaded(bool is_downloaded){ m_data->m_is_downloaded_label.set_text(is_downloaded_string(is_downloaded)); } +void ResourceDownloadRow::set_cancel_action(bool cancel_action){ + m_data->m_cancel_action = cancel_action; +} + ResourceDownloadRow::~ResourceDownloadRow(){} ResourceDownloadRow::ResourceDownloadRow( @@ -183,37 +190,50 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad qint64 expected_size = resource_metadata.size_compressed_bytes; std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; - - // delete directory and the old resource - fs::remove_all(resource_directory); - - // download - std::string zip_path = resource_directory + "/temp.zip"; - FileDownloader::download_file_to_disk( - logger, - url, - zip_path, - expected_size, - [this](int percentage_progress){ - download_progress(percentage_progress); - } - ); - - // unzip - unzip_file( - zip_path.c_str(), - resource_directory.c_str(), - [this](int percentage_progress){ - unzip_progress(percentage_progress); - } - ); - - // delete old zip file - fs::remove(zip_path); - - // update the table labels - set_is_downloaded(true); - set_version_status(ResourceVersionStatus::CURRENT); + try{ + // delete directory and the old resource + fs::remove_all(resource_directory); + + // download + std::string zip_path = resource_directory + "/temp.zip"; + FileDownloader::download_file_to_disk( + logger, + url, + zip_path, + expected_size, + [this](int percentage_progress){ + download_progress(percentage_progress); + }, + [this](){ + return m_data->m_cancel_action.load(); + } + ); + + // unzip + unzip_file( + zip_path.c_str(), + resource_directory.c_str(), + [this](int percentage_progress){ + unzip_progress(percentage_progress); + } + ); + + // delete old zip file + fs::remove(zip_path); + + // update the table labels + set_is_downloaded(true); + set_version_status(ResourceVersionStatus::CURRENT); + }catch(OperationCancelledException& e){ + // delete directory and the resource + fs::remove_all(resource_directory); + + // update the table labels + set_is_downloaded(false); + set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + + throw e; + } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index e5b366fa3c..0ae9c64955 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -38,12 +38,14 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ public: void set_version_status(ResourceVersionStatus version_status); void set_is_downloaded(bool is_downloaded); + void set_cancel_action(bool cancel_action); // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row void initialize_remote_metadata(); RemoteMetadata& fetch_remote_metadata(); // DownloadedResourceMetadata initialize_local_metadata(); + // throws OperationCancelledException if the user cancels the action void run_download(DownloadedResourceMetadata resource_metadata); public: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index b8728b5773..80fd80cd05 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -177,18 +177,19 @@ template class RegisterConfigWidget; DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) : QWidget(&parent) , ConfigWidget(value, *this) + , m_value(value) { - QPushButton* button = new QPushButton(&parent); - m_widget = button; + m_button = new QPushButton(&parent); + m_widget = m_button; QFont font; font.setBold(true); - button->setFont(font); - button->setText("Delete"); + m_button->setFont(font); + m_button->setText("Delete"); - button->connect( - button, &QPushButton::clicked, - button, [&](bool){ + m_button->connect( + m_button, &QPushButton::clicked, + m_button, [&](bool){ cout << "Clicked Delete Button" << endl; } ); @@ -199,18 +200,20 @@ template class RegisterConfigWidget; CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) : QWidget(&parent) , ConfigWidget(value, *this) + , m_value(value) { - QPushButton* button = new QPushButton(&parent); - m_widget = button; + m_button = new QPushButton(&parent); + m_widget = m_button; QFont font; font.setBold(true); - button->setFont(font); - button->setText("Cancel"); + m_button->setFont(font); + m_button->setText("Cancel"); - button->connect( - button, &QPushButton::clicked, - button, [&](bool){ + m_button->connect( + m_button, &QPushButton::clicked, + m_button, [&](bool){ + m_value.row.set_cancel_action(true); cout << "Clicked Cancel Button" << endl; } ); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index ac609c287a..8b8ce11dad 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -48,6 +48,10 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget{ public: DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); + +private: + ResourceDeleteButton& m_value; + QPushButton* m_button; }; class CancelButtonWidget : public QWidget, public ConfigWidget{ @@ -56,6 +60,10 @@ class CancelButtonWidget : public QWidget, public ConfigWidget{ public: CancelButtonWidget(QWidget& parent, ResourceCancelButton& value); + +private: + ResourceCancelButton& m_value; + QPushButton* m_button; }; class ProgressBarWidget : public QWidget, public ConfigWidget{ diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp index 30de48bc23..b441be9ed8 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp @@ -98,7 +98,8 @@ void download_file_to_disk( const std::string& url, const std::string& file_path, qint64 expected_size, - std::function progress_callback + std::function progress_callback, + std::function is_cancelled ){ // cout << "download_file()" << endl; QNetworkAccessManager network_access_manager; @@ -124,9 +125,13 @@ void download_file_to_disk( // 2. Start the GET request QNetworkReply* reply = network_access_manager.get(request); - // Progress Bar Logic + // Progress Bar Logic. and check for Cancel QObject::connect(reply, &QNetworkReply::downloadProgress, - [expected_size, progress_callback](qint64 bytesReceived, qint64 bytesTotal) { + [reply, expected_size, progress_callback, is_cancelled](qint64 bytesReceived, qint64 bytesTotal) { + + if (is_cancelled()){ + reply->abort(); + } // Use expected_size if the network doesn't provide one qint64 total = (bytesTotal > 0) ? bytesTotal : expected_size; @@ -162,10 +167,15 @@ void download_file_to_disk( "Failed to commit file to disk: " + file_path); } } else { - QString error_string = reply->errorString(); - // QSaveFile automatically deletes the temp file if commit() isn't called - throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, - "Network Error: " + error_string.toStdString()); + if (is_cancelled()){ + logger.log("Download cancelled by user."); + throw OperationCancelledException(); + }else{ + QString error_string = reply->errorString(); + // QSaveFile automatically deletes the temp file if commit() isn't called + throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, + "Network Error: " + error_string.toStdString()); + } } reply->deleteLater(); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h index a48bcf7569..43b0eda9c0 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h @@ -25,7 +25,8 @@ void download_file_to_disk( const std::string& url, const std::string& file_path, qint64 expected_size, - std::function progress_callback + std::function progress_callback, + std::function is_cancelled ); // Throws OperationFailedException if failed to download. From 90e43b29c3bf17835fe24a60ca355acf9fc50226 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 21:29:31 -0700 Subject: [PATCH 38/53] add cancel callback to unzip function --- .../ResourceDownload/ResourceDownloadRow.cpp | 4 ++ .../CommonFramework/Tools/FileUnzip.cpp | 63 +++++++++++++------ .../Source/CommonFramework/Tools/FileUnzip.h | 3 +- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 66a9fb6bbb..a6e3b90f5a 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -216,6 +216,10 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad [this](int percentage_progress){ unzip_progress(percentage_progress); } + , + [this](){ + return m_data->m_cancel_action.load(); + } ); // delete old zip file diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index 520475b92d..c5caa38e86 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -34,18 +34,23 @@ struct ProgressData { uint64_t processed_bytes; int last_percentage; std::function progress_callback; + std::function is_cancelled; }; // Callback triggered for every chunk of decompressed data // pOpaque is an opaque pointer that actually represents ProgressData -size_t write_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n){ +size_t write_callback(void* pOpaque, [[maybe_unused]] mz_uint64 file_ofs, const void* pBuf, size_t n){ ProgressData* data = static_cast(pOpaque); - // 1. Check if we actually need to seek - // tellp() returns the current 'put' position. get the current position of the write pointer in an output stream. - if (static_cast(data->out_file->tellp()) != file_ofs){ - data->out_file->seekp(file_ofs); + if (data->is_cancelled()){ + return 0; // this causes mz_zip_reader_extract_to_callback to return an error } + + // Check if we actually need to seek + // tellp() returns the current 'put' position. get the current position of the write pointer in an output stream. + // if (static_cast(data->out_file->tellp()) != file_ofs){ + // data->out_file->seekp(file_ofs); + // } // Write chunk to disk data->out_file->write(static_cast(pBuf), n); @@ -58,7 +63,8 @@ size_t write_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_ // Only print if the integer value has changed if (current_percent > data->last_percentage){ data->progress_callback(current_percent); - // data->last_percentage = current_percent; + data->last_percentage = current_percent; + // std::cout << "\rProgress: " << current_percent << "% (" // << data->processed_bytes << "/" << data->total_bytes << " bytes)" << endl; } @@ -102,7 +108,8 @@ bool is_safe(const std::string& target_dir, const std::string& entry_name){ void unzip_file( const char* zip_path, const char* target_dir, - std::function progress_callback + std::function progress_callback, + std::function is_cancelled ){ Filesystem::Path p{zip_path}; if (!fs::exists(p)){ @@ -120,12 +127,20 @@ void unzip_file( return; } + // This automatically calls mz_zip_reader_end when this function exits for any reason. + struct ZipCleanup { + mz_zip_archive* p; + ~ZipCleanup() { mz_zip_reader_end(p); } + } cleanup{&zip_archive}; + // Get total number of files in the archive int num_files = (int)mz_zip_reader_get_num_files(&zip_archive); // calculate total uncompressed size uint64_t total_uncompressed_size = 0; for (int i = 0; i < num_files; i++){ + if (is_cancelled()) return; + mz_zip_archive_file_stat file_stat; // holds info on the specific file // fills file_stat with the data for the current index @@ -137,6 +152,8 @@ void unzip_file( uint64_t total_processed_bytes = 0; for (int i = 0; i < num_files; i++){ + if (is_cancelled()) return; + mz_zip_archive_file_stat file_stat; // holds info on the specific file // fills file_stat with the data for the current index @@ -148,9 +165,14 @@ void unzip_file( continue; } + // ensure that entry_name is inside target_dir. to prevent path traversal attacks. + if (!is_safe(target_dir, file_stat.m_filename)){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); + } + // Construct your output path (e.g., target_dir + file_stat.m_filename) - std::string out_path = std::string(target_dir) + "/" + file_stat.m_filename; - Filesystem::Path const parent_dir{Filesystem::Path(out_path).parent_path()}; + Filesystem::Path out_path = Filesystem::Path(target_dir) / Filesystem::Path(file_stat.m_filename); + Filesystem::Path const parent_dir{out_path.parent_path()}; // Create the entire directory, including intermediate directories for this file std::error_code ec{}; @@ -160,22 +182,27 @@ void unzip_file( ec.clear(); } - // ensure that entry_name is inside target_dir. to prevent path traversal attacks. - if (!is_safe(target_dir, file_stat.m_filename)){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); - } - std::ofstream out_file(out_path, std::ios::binary); // std::ios::binary is to prevent line-ending conversions. - ProgressData progress = { &out_file, total_uncompressed_size, total_processed_bytes, -1, progress_callback }; + std::ofstream out_file(out_path.string(), std::ios::binary); // std::ios::binary is to prevent line-ending conversions. + ProgressData progress = { &out_file, total_uncompressed_size, total_processed_bytes, -1, progress_callback, is_cancelled }; // Extract using the callback // decompresses the file in chunks and repeatedly calls write_callback to save those chunks to the disk via the out_file - mz_zip_reader_extract_to_callback(&zip_archive, i, write_callback, &progress, 0); - std::cout << "\nFinished: " << file_stat.m_filename << std::endl; + mz_bool status = mz_zip_reader_extract_to_callback(&zip_archive, i, write_callback, &progress, 0); + + if (!status){ + out_file.close(); + if (is_cancelled()){ + // close and delete the partially unzipped file + fs::remove(out_path, ec); + return; + } + } + + // std::cout << "\nFinished: " << file_stat.m_filename << std::endl; total_processed_bytes += file_stat.m_uncomp_size; } - mz_zip_reader_end(&zip_archive); } // void unzip_file(const std::string& zip_path, const std::string& output_dir){ diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h index 0f70a9dfd4..5da960be65 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h @@ -15,7 +15,8 @@ namespace PokemonAutomation{ void unzip_file( const char* zip_path, const char* target_dir, - std::function progress_callback + std::function progress_callback, + std::function is_cancelled ); } From 078786aea6aa3b229946f9f5490dcf243ccd3971 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 22:30:26 -0700 Subject: [PATCH 39/53] refactor: move logic from DownloadButton class to DownloadRow class. --- .../ResourceDownloadOptions.cpp | 155 +----------------- .../ResourceDownloadOptions.h | 20 +-- .../ResourceDownload/ResourceDownloadRow.cpp | 135 ++++++++++++++- .../ResourceDownload/ResourceDownloadRow.h | 16 +- .../ResourceDownloadWidget.cpp | 14 +- .../CommonFramework/Tools/FileUnzip.cpp | 2 +- 6 files changed, 159 insertions(+), 183 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp index 3ba53147d0..60fdb6cd84 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.cpp @@ -4,9 +4,6 @@ * */ -#include "CommonFramework/Tools/GlobalThreadPools.h" -#include "Common/Cpp/Exceptions.h" -#include "CommonFramework/Exceptions/OperationFailedException.h" #include "ResourceDownloadRow.h" #include "ResourceDownloadOptions.h" @@ -18,16 +15,7 @@ using std::endl; namespace PokemonAutomation{ -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// ResourceDownloadButton -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -ResourceDownloadButton::~ResourceDownloadButton(){ - m_worker1.wait_and_ignore_exceptions(); - m_worker2.wait_and_ignore_exceptions(); -} - - +// ResourceDownloadButton::~ResourceDownloadButton(){} ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) @@ -35,148 +23,12 @@ ResourceDownloadButton::ResourceDownloadButton(ResourceDownloadRow& p_row) {} -void ResourceDownloadButton::ensure_remote_metadata_loaded(){ - m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - try { - m_enabled = false; - // std::this_thread::sleep_for(std::chrono::seconds(1)); - std::string predownload_warning; - RemoteMetadata& remote_handle = row.fetch_remote_metadata(); - // cout << "Fetched remote metadata" << endl; - // throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "testing"); - - predownload_warning = predownload_warning_summary(remote_handle); - m_enabled = true; - emit metadata_fetch_finished(predownload_warning); - - }catch(OperationFailedException&){ - m_enabled = true; - // cout << "failed" << endl; - emit download_failed(); - return; - }catch(...){ - m_enabled = true; - // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); - return; - } - - } - ); - -} - -std::string ResourceDownloadButton::predownload_warning_summary(RemoteMetadata& remote_handle){ - - std::string predownload_warning; - - switch (remote_handle.status){ - case RemoteMetadataStatus::UNINITIALIZED: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Remote metadata uninitialized."); - case RemoteMetadataStatus::NOT_AVAILABLE: - predownload_warning = "Resource no longer available for download. We recommend updating the Computer Control program."; - break; - case RemoteMetadataStatus::AVAILABLE: - { - uint16_t local_version_num = row.m_local_metadata.version_num.value(); - - DownloadedResourceMetadata remote_metadata = remote_handle.metadata; - uint16_t remote_version_num = remote_metadata.version_num.value(); - size_t compressed_size = remote_metadata.size_compressed_bytes; - size_t decompressed_size = remote_metadata.size_decompressed_bytes; - - std::string disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; - - if (local_version_num < remote_version_num){ - predownload_warning = "The resource you are downloading is a more updated version than the program expects. " - "This may or may not cause issues with the programs. " - "We recommend updating the Computer Control program.
" + - disk_space_requirement; - }else if (local_version_num == remote_version_num){ - predownload_warning = "Update available.
" + disk_space_requirement; - }else if (local_version_num > remote_version_num){ - predownload_warning = "The resource you are downloading is a less updated version than the program expects. " - "Please report this as a bug.
" + - disk_space_requirement; - } - } - break; - default: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Unknown enum."); - } - - return predownload_warning; -} - - - -void ResourceDownloadButton::start_download(){ - m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ - try { - m_enabled = false; - // std::this_thread::sleep_for(std::chrono::seconds(7)); - RemoteMetadata& remote_handle = row.fetch_remote_metadata(); - if (remote_handle.status != RemoteMetadataStatus::AVAILABLE){ - switch (remote_handle.status){ - case RemoteMetadataStatus::UNINITIALIZED: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Remote metadata uninitialized."); - case RemoteMetadataStatus::NOT_AVAILABLE: - cout << "start_download: Download not available. Cancel download." << endl; - m_enabled = true; - emit download_finished(); - return; - default: - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Unknown enum."); - } - } - - // Download is available - DownloadedResourceMetadata metadata = remote_handle.metadata; - row.run_download(metadata); - - cout << "Done Download" << endl; - - m_enabled = true; - row.set_cancel_action(false); - emit download_finished(); - }catch(OperationCancelledException&){ - m_enabled = true; - - row.set_cancel_action(false); - - emit download_finished(); - return; - }catch(OperationFailedException&){ - m_enabled = true; - // cout << "failed" << endl; - emit download_failed(); - return; - }catch(...){ - m_enabled = true; - // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::start_download"); - return; - } - } - ); - -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// ResourceDeleteButton -///////////////////////////////////////////////////////////////////////////////////////////////////////// - ResourceDeleteButton::ResourceDeleteButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) , m_enabled(true) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// ResourceCancelButton -///////////////////////////////////////////////////////////////////////////////////////////////////////// ResourceCancelButton::ResourceCancelButton(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) @@ -185,11 +37,6 @@ ResourceCancelButton::ResourceCancelButton(ResourceDownloadRow& p_row) {} -///////////////////////////////////////////////////////////////////////////////////////////////////////// -// ResourceProgressBar -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - ResourceProgressBar::ResourceProgressBar(ResourceDownloadRow& p_row) : ConfigOptionImpl(LockMode::UNLOCK_WHILE_RUNNING) , row(p_row) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h index b9a893f8bf..4c7622ebb7 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h @@ -9,9 +9,9 @@ #include // #include "Common/Cpp/Containers/Pimpl.h" -#include "Common/Cpp/Concurrency/AsyncTask.h" +// #include "Common/Cpp/Concurrency/AsyncTask.h" // #include "Common/Cpp/Options/StaticTableOption.h" -#include "ResourceDownloadHelpers.h" +// #include "ResourceDownloadHelpers.h" namespace PokemonAutomation{ @@ -22,21 +22,10 @@ class ResourceDownloadRow; class ResourceDownloadButton : public QObject, public ConfigOptionImpl{ Q_OBJECT public: - ~ResourceDownloadButton(); + // ~ResourceDownloadButton(); ResourceDownloadButton(ResourceDownloadRow& p_row); -signals: - void metadata_fetch_finished(std::string popup_message); - void exception_caught(std::string function_name); - void download_failed(); - void download_finished(); - public: - void start_download(); - - void ensure_remote_metadata_loaded(); - std::string predownload_warning_summary(RemoteMetadata& remote_metadata); - inline bool get_enabled(){ return m_enabled; } inline void set_enabled(bool enabled){ m_enabled = enabled; @@ -48,9 +37,6 @@ class ResourceDownloadButton : public QObject, public ConfigOptionImpl" + + disk_space_requirement; + }else if (local_version_num == remote_version_num){ + predownload_warning = "Update available.
" + disk_space_requirement; + }else if (local_version_num > remote_version_num){ + predownload_warning = "The resource you are downloading is a less updated version than the program expects. " + "Please report this as a bug.
" + + disk_space_requirement; + } + } + break; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "predownload_warning_summary: Unknown enum."); + } + + return predownload_warning; +} + + + +void ResourceDownloadRow::start_download(){ + m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + try { + + // std::this_thread::sleep_for(std::chrono::seconds(7)); + RemoteMetadata& remote_handle = fetch_remote_metadata(); + if (remote_handle.status != RemoteMetadataStatus::AVAILABLE){ + switch (remote_handle.status){ + case RemoteMetadataStatus::UNINITIALIZED: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Remote metadata uninitialized."); + case RemoteMetadataStatus::NOT_AVAILABLE: + cout << "start_download: Download not available. Cancel download." << endl; + throw OperationCancelledException(); + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "start_download: Unknown enum."); + } + } + + // Download is available + DownloadedResourceMetadata metadata = remote_handle.metadata; + run_download(metadata); + + cout << "Done Download" << endl; + + actions_done_reenable_buttons(); + emit download_finished(); + }catch(OperationCancelledException&){ + actions_done_reenable_buttons(); + emit download_finished(); + return; + }catch(OperationFailedException&){ + actions_done_reenable_buttons(); + emit download_failed(); + return; + }catch(...){ + actions_done_reenable_buttons(); + emit exception_caught("ResourceDownloadButton::start_download"); + return; + } + } + ); + +} + void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metadata){ Logger& logger = global_logger_tagged(); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 0ae9c64955..e8deb091b6 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -9,7 +9,7 @@ #include #include "Common/Cpp/Containers/Pimpl.h" -// #include "Common/Cpp/Concurrency/AsyncTask.h" +#include "Common/Cpp/Concurrency/AsyncTask.h" // #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" #include "ResourceDownloadHelpers.h" @@ -34,19 +34,30 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void download_progress(int percentage); void unzip_progress(int percentage); + void metadata_fetch_finished(std::string popup_message); + void exception_caught(std::string function_name); + void download_failed(); + void download_finished(); + public: void set_version_status(ResourceVersionStatus version_status); void set_is_downloaded(bool is_downloaded); void set_cancel_action(bool cancel_action); + void actions_done_reenable_buttons(); + + void ensure_remote_metadata_loaded(); + std::string predownload_warning_summary(RemoteMetadata& remote_metadata); // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row void initialize_remote_metadata(); RemoteMetadata& fetch_remote_metadata(); // DownloadedResourceMetadata initialize_local_metadata(); + void start_download(); // throws OperationCancelledException if the user cancels the action void run_download(DownloadedResourceMetadata resource_metadata); + public: DownloadedResourceMetadata m_local_metadata; @@ -63,6 +74,9 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ ResourceCancelButton m_cancel_button; ResourceProgressBar m_progress_bar; + AsyncTask m_worker1; + AsyncTask m_worker2; + diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 80fd80cd05..e3e9a55fa8 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -74,14 +74,14 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt this, [this](){ m_value.set_enabled(false); update_enabled_status(); - m_value.ensure_remote_metadata_loaded(); + m_value.row.ensure_remote_metadata_loaded(); } ); // when json has been fetched, open the update box. // When click Ok in update box, start the download. If click cancel, re-enable the download button connect( - &m_value, &ResourceDownloadButton::metadata_fetch_finished, + &m_value.row, &ResourceDownloadRow::metadata_fetch_finished, this, [this](std::string predownload_warning){ show_download_confirm_box("Download", predownload_warning); } @@ -90,7 +90,7 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // when the download is finished, update the UI to re-enable the button connect( - &m_value, &ResourceDownloadButton::download_finished, + &m_value.row, &ResourceDownloadRow::download_finished, this, [this](){ update_enabled_status(); } @@ -99,9 +99,8 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // if the thread catches an exception, show an error box // since exceptions can't bubble up as usual connect( - &m_value, &ResourceDownloadButton::exception_caught, + &m_value.row, &ResourceDownloadRow::exception_caught, this, [this](std::string function_name){ - m_value.set_enabled(true); update_enabled_status(); show_error_box(function_name); } @@ -109,9 +108,8 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // if download fails connect( - &m_value, &ResourceDownloadButton::download_failed, + &m_value.row, &ResourceDownloadRow::download_failed, this, [this](){ - m_value.set_enabled(true); update_enabled_status(); show_download_failed_box(); } @@ -159,7 +157,7 @@ void DownloadButtonWidget::show_download_confirm_box( if (clicked == ok){ cout << "Clicked Ok to Download" << endl; - m_value.start_download(); + m_value.row.start_download(); return; } if (clicked == cancel){ diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index c5caa38e86..457f7ea1f3 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -146,7 +146,7 @@ void unzip_file( // fills file_stat with the data for the current index if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue; - cout << std::to_string(file_stat.m_uncomp_size) << endl; + // cout << std::to_string(file_stat.m_uncomp_size) << endl; total_uncompressed_size += file_stat.m_uncomp_size; } From c621b0973ba45c0e3f6c45fe5e3dab62bcdfcd35 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 2 Apr 2026 16:28:01 -0700 Subject: [PATCH 40/53] implement delete action for Delete button --- .../ResourceDownloadOptions.h | 14 ++++++ .../ResourceDownload/ResourceDownloadRow.cpp | 27 +++++++++++ .../ResourceDownload/ResourceDownloadRow.h | 7 +-- .../ResourceDownloadWidget.cpp | 46 ++++++++++++++++++- .../ResourceDownload/ResourceDownloadWidget.h | 11 +++-- 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h index 4c7622ebb7..d8b934efc0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadOptions.h @@ -46,6 +46,13 @@ class ResourceDeleteButton : public ConfigOptionImpl{ public: ResourceDeleteButton(ResourceDownloadRow& p_row); +public: + inline bool get_enabled(){ return m_enabled; } + inline void set_enabled(bool enabled){ + m_enabled = enabled; + } + +public: ResourceDownloadRow& row; private: @@ -56,6 +63,13 @@ class ResourceCancelButton : public ConfigOptionImpl{ public: ResourceCancelButton(ResourceDownloadRow& p_row); +public: + inline bool get_enabled(){ return m_enabled; } + inline void set_enabled(bool enabled){ + m_enabled = enabled; + } + +public: ResourceDownloadRow& row; private: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 934bfe9b12..80e874b5aa 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -109,6 +109,7 @@ void ResourceDownloadRow::set_cancel_action(bool cancel_action){ ResourceDownloadRow::~ResourceDownloadRow(){ m_worker1.wait_and_ignore_exceptions(); m_worker2.wait_and_ignore_exceptions(); + m_worker3.wait_and_ignore_exceptions(); } ResourceDownloadRow::ResourceDownloadRow( DownloadedResourceMetadata local_metadata, @@ -373,4 +374,30 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad } +void ResourceDownloadRow::start_delete(){ + m_worker3 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + try { + std::string resource_name = m_local_metadata.resource_name; + + std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; + // delete directory and the old resource + fs::remove_all(resource_directory); + + // update the table labels + set_is_downloaded(false); + set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + + // emit delete_finished(); + }catch(...){ + // actions_done_reenable_buttons(); + emit exception_caught("ResourceDownloadButton::start_delete"); + return; + } + } + ); + +} + + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index e8deb091b6..af2f14a3d0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -57,15 +57,15 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void start_download(); // throws OperationCancelledException if the user cancels the action void run_download(DownloadedResourceMetadata resource_metadata); - -public: - DownloadedResourceMetadata m_local_metadata; + void start_delete(); + private: std::once_flag init_flag; std::unique_ptr m_remote_metadata; + DownloadedResourceMetadata m_local_metadata; struct Data; Pimpl m_data; @@ -76,6 +76,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ AsyncTask m_worker1; AsyncTask m_worker2; + AsyncTask m_worker3; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index e3e9a55fa8..86000787af 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -185,15 +185,57 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va m_button->setFont(font); m_button->setText("Delete"); - m_button->connect( + connect( m_button, &QPushButton::clicked, m_button, [&](bool){ + show_delete_confirm_box(); cout << "Clicked Delete Button" << endl; } ); + + // connect( + // &m_value.row, &ResourceDownloadRow::delete_finished, + // this, [this](){ + // // update_enabled_status(); + // } + // ); } +void DeleteButtonWidget::show_delete_confirm_box(){ + QMessageBox box; + QPushButton* yes = box.addButton(QMessageBox::Yes); + QPushButton* cancel = box.addButton("Cancel", QMessageBox::NoRole); + box.setEscapeButton(cancel); +// cout << "ok = " << ok << endl; +// cout << "skip = " << skip << endl; + + box.setTextFormat(Qt::RichText); + std::string title = "Delete"; + std::string message_body = "Are you suer you want to delete this resource?"; + + box.setWindowTitle(QString::fromStdString(title)); + box.setText(QString::fromStdString(message_body)); + +// box.open(); + + box.exec(); + + QAbstractButton* clicked = box.clickedButton(); +// cout << "clicked = " << clicked << endl; + if (clicked == yes){ + cout << "Clicked Yes to Delete" << endl; + + m_value.row.start_delete(); + return; + } + if (clicked == cancel){ + // m_value.set_enabled(true); + // update_enabled_status(); + return; + } +} + template class RegisterConfigWidget; CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) : QWidget(&parent) @@ -208,7 +250,7 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va m_button->setFont(font); m_button->setText("Cancel"); - m_button->connect( + connect( m_button, &QPushButton::clicked, m_button, [&](bool){ m_value.row.set_cancel_action(true); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 8b8ce11dad..a99bf97aff 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -27,15 +27,17 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ ~DownloadButtonWidget(); DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value); -private: - ResourceDownloadButton& m_value; - QPushButton* m_button; +private: void update_enabled_status(); void show_download_confirm_box( const std::string& title, const std::string& message_body ); + +private: + ResourceDownloadButton& m_value; + QPushButton* m_button; }; @@ -49,6 +51,9 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget{ public: DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); +private: + void show_delete_confirm_box(); + private: ResourceDeleteButton& m_value; QPushButton* m_button; From 7ae6c901bbf3ca0bf09b1e8677c189099b374213 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 2 Apr 2026 18:34:38 -0700 Subject: [PATCH 41/53] unzip routine will throw OperationCancelledException if cancelled. and will throw InternalProgramError if it fails for other reasons. --- .../ResourceDownload/ResourceDownloadRow.cpp | 4 +++ .../CommonFramework/Tools/FileUnzip.cpp | 25 +++++++++++++------ .../Source/CommonFramework/Tools/FileUnzip.h | 4 +++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 80e874b5aa..7234d7a7b5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -357,6 +357,10 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad // delete old zip file fs::remove(zip_path); + if (m_data->m_cancel_action.load()){ + throw OperationCancelledException(); + } + // update the table labels set_is_downloaded(true); set_version_status(ResourceVersionStatus::CURRENT); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index 457f7ea1f3..734a63088f 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -73,6 +73,7 @@ size_t write_callback(void* pOpaque, [[maybe_unused]] mz_uint64 file_ofs, const } // ensure that entry_name is inside target_dir, to prevent path traversal attacks. +// assumes target_dir exists bool is_safe(const std::string& target_dir, const std::string& entry_name){ try { // 1. Get absolute, normalized paths @@ -113,18 +114,26 @@ void unzip_file( ){ Filesystem::Path p{zip_path}; if (!fs::exists(p)){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that doesn't exist."); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_file: Attempted to unzip a file that doesn't exist."); } + { + Filesystem::Path dir{target_dir}; + std::error_code ec{}; + fs::create_directories(dir, ec); + if (ec){ + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_file: Error creating the target directory " + std::string(target_dir) + ": " + ec.message()); + } + } + mz_zip_archive zip_archive; memset(&zip_archive, 0, sizeof(zip_archive)); // Opens the ZIP file at zip_path // zip_archive holds the state and metadata of the ZIP archive. if (!mz_zip_reader_init_file(&zip_archive, zip_path, 0)){ - cout << "failed to run mz_zip_reader_init_file" << endl; - cout << "mz_zip_error: " << mz_zip_get_last_error(&zip_archive) << endl; - return; + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, + "unzip_file: failed to run mz_zip_reader_init_file. mz_zip_error: " + mz_zip_get_last_error(&zip_archive)); } // This automatically calls mz_zip_reader_end when this function exits for any reason. @@ -139,7 +148,7 @@ void unzip_file( // calculate total uncompressed size uint64_t total_uncompressed_size = 0; for (int i = 0; i < num_files; i++){ - if (is_cancelled()) return; + if (is_cancelled()) throw OperationCancelledException(); mz_zip_archive_file_stat file_stat; // holds info on the specific file @@ -152,7 +161,7 @@ void unzip_file( uint64_t total_processed_bytes = 0; for (int i = 0; i < num_files; i++){ - if (is_cancelled()) return; + if (is_cancelled()) throw OperationCancelledException(); mz_zip_archive_file_stat file_stat; // holds info on the specific file @@ -167,7 +176,7 @@ void unzip_file( // ensure that entry_name is inside target_dir. to prevent path traversal attacks. if (!is_safe(target_dir, file_stat.m_filename)){ - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_all: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unzip_file: Attempted to unzip a file that was trying to leave its base directory. This is a security risk."); } // Construct your output path (e.g., target_dir + file_stat.m_filename) @@ -195,7 +204,7 @@ void unzip_file( if (is_cancelled()){ // close and delete the partially unzipped file fs::remove(out_path, ec); - return; + throw OperationCancelledException(); } } diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h index 5da960be65..9eb26fd931 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h @@ -12,6 +12,10 @@ namespace PokemonAutomation{ +// unzips the zip file located in zip_path, to target_dir +// if target_dir doesn't already exist, it will create it. +// throw OperationCancelledException if the is_cancelled callback returns true +// throw InternalProgramError if unzipping fails. void unzip_file( const char* zip_path, const char* target_dir, From cb28e41632e32d185d637bb9e4d6fbce1b5531c9 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 3 Apr 2026 10:09:40 -0700 Subject: [PATCH 42/53] adjust logic that updates button state --- .../ResourceDownload/ResourceDownloadRow.cpp | 75 ++++++++++--- .../ResourceDownload/ResourceDownloadRow.h | 14 ++- .../ResourceDownloadWidget.cpp | 103 ++++++++++++------ .../ResourceDownload/ResourceDownloadWidget.h | 6 +- .../Source/StaticRegistrationQt.cpp | 2 + 5 files changed, 149 insertions(+), 51 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 7234d7a7b5..85f973e2d0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -118,6 +118,7 @@ ResourceDownloadRow::ResourceDownloadRow( ResourceVersionStatus version_status ) : StaticTableRow(local_metadata.resource_name) + , m_button_state(ButtonState::READY) , m_local_metadata(local_metadata) , m_data(CONSTRUCT_TOKEN, local_metadata.resource_name, local_metadata.size_decompressed_bytes, is_downloaded, version_num, version_status) , m_download_button(*this) @@ -186,10 +187,6 @@ RemoteMetadata& ResourceDownloadRow::fetch_remote_metadata(){ // return corresponding_local_metadata; // } -void ResourceDownloadRow::actions_done_reenable_buttons(){ - m_download_button.set_enabled(true); - set_cancel_action(false); -} void ResourceDownloadRow::ensure_remote_metadata_loaded(){ m_worker1 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( @@ -203,16 +200,16 @@ void ResourceDownloadRow::ensure_remote_metadata_loaded(){ predownload_warning = predownload_warning_summary(remote_handle); - actions_done_reenable_buttons(); + // update_button_state(ButtonState::READY); emit metadata_fetch_finished(predownload_warning); }catch(OperationFailedException&){ // cout << "failed" << endl; - actions_done_reenable_buttons(); + update_button_state(ButtonState::READY); emit download_failed(); return; }catch(...){ - actions_done_reenable_buttons(); + update_button_state(ButtonState::READY); // cout << "Exception thrown in thread" << endl; emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); return; @@ -292,18 +289,17 @@ void ResourceDownloadRow::start_download(){ cout << "Done Download" << endl; - actions_done_reenable_buttons(); - emit download_finished(); + update_button_state(ButtonState::READY); + }catch(OperationCancelledException&){ - actions_done_reenable_buttons(); - emit download_finished(); + update_button_state(ButtonState::READY); return; }catch(OperationFailedException&){ - actions_done_reenable_buttons(); + update_button_state(ButtonState::READY); emit download_failed(); return; }catch(...){ - actions_done_reenable_buttons(); + update_button_state(ButtonState::READY); emit exception_caught("ResourceDownloadButton::start_download"); return; } @@ -392,9 +388,9 @@ void ResourceDownloadRow::start_delete(){ set_is_downloaded(false); set_version_status(ResourceVersionStatus::NOT_APPLICABLE); - // emit delete_finished(); + update_button_state(ButtonState::READY); }catch(...){ - // actions_done_reenable_buttons(); + update_button_state(ButtonState::READY); emit exception_caught("ResourceDownloadButton::start_delete"); return; } @@ -403,5 +399,54 @@ void ResourceDownloadRow::start_delete(){ } +void ResourceDownloadRow::update_button_state(ButtonState state){ + switch (state){ + case ButtonState::DOWNLOAD: + // button state can only enter the DOWNLOAD state + // if going from the READY state + if (m_button_state == ButtonState::READY){ + m_download_button.set_enabled(false); + m_delete_button.set_enabled(false); + m_cancel_button.set_enabled(true); + set_cancel_action(false); + m_button_state = state; + } + break; + case ButtonState::DELETE: + // button state can only enter the DELETE state + // if going from the READY state + if (m_button_state == ButtonState::READY){ + m_download_button.set_enabled(false); + m_delete_button.set_enabled(false); + m_cancel_button.set_enabled(false); + set_cancel_action(false); + m_button_state = state; + } + break; + case ButtonState::CANCEL: + // button state can only enter the CANCEL state + // if going from the DOWNLOAD state + if (m_button_state == ButtonState::DOWNLOAD){ + m_download_button.set_enabled(false); + m_delete_button.set_enabled(false); + m_cancel_button.set_enabled(false); + set_cancel_action(true); + m_button_state = state; + } + break; + case ButtonState::READY: + m_download_button.set_enabled(true); + m_delete_button.set_enabled(true); + m_cancel_button.set_enabled(true); + set_cancel_action(false); + m_button_state = state; + break; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "update_button_state: Unknown enum."); + } + + emit button_state_updated(); +} + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index af2f14a3d0..c8ef840730 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -18,7 +18,12 @@ namespace PokemonAutomation{ - +enum class ButtonState{ + DOWNLOAD, + DELETE, + CANCEL, + READY, +}; class ResourceDownloadRow : public QObject, public StaticTableRow{ Q_OBJECT public: @@ -39,14 +44,14 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void download_failed(); void download_finished(); + void button_state_updated(); + public: void set_version_status(ResourceVersionStatus version_status); void set_is_downloaded(bool is_downloaded); void set_cancel_action(bool cancel_action); - void actions_done_reenable_buttons(); - void ensure_remote_metadata_loaded(); std::string predownload_warning_summary(RemoteMetadata& remote_metadata); // get the DownloadedResourceMetadata from the remote JSON, that corresponds to this button/row @@ -59,12 +64,15 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void run_download(DownloadedResourceMetadata resource_metadata); void start_delete(); + + void update_button_state(ButtonState state); private: std::once_flag init_flag; std::unique_ptr m_remote_metadata; + ButtonState m_button_state; DownloadedResourceMetadata m_local_metadata; struct Data; Pimpl m_data; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 86000787af..93d25cbb41 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -61,23 +61,30 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // this status is stored within ResourceDownloadButton::m_enabled // when the button is clicked, m_enabled is set to false // when te download is done, m_enabled is set back to true - // the UI is updated to reflect the status of m_enabled, by using update_enabled_status + // the UI is updated to reflect the status of m_enabled, by using update_UI_state // update the UI based on m_enabled, when the button is constructed - update_enabled_status(); + update_UI_state(); - // when the button is clicked, m_enabled is set to false, - // fetch json + // when the button is clicked, runs row.update_button_state(), which updates the button state + // also, fetch json connect( m_button, &QPushButton::clicked, this, [this](){ - m_value.set_enabled(false); - update_enabled_status(); + m_value.row.update_button_state(ButtonState::DOWNLOAD); m_value.row.ensure_remote_metadata_loaded(); } ); + // when button_state_updated, update the UI state to match + connect( + &m_value.row, &ResourceDownloadRow::button_state_updated, + this, [this](){ + update_UI_state(); + } + ); + // when json has been fetched, open the update box. // When click Ok in update box, start the download. If click cancel, re-enable the download button connect( @@ -87,21 +94,12 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt } ); - - // when the download is finished, update the UI to re-enable the button - connect( - &m_value.row, &ResourceDownloadRow::download_finished, - this, [this](){ - update_enabled_status(); - } - ); - // if the thread catches an exception, show an error box // since exceptions can't bubble up as usual + // this connect handles all exception_caught() emitted by ResourceDownloadRow connect( &m_value.row, &ResourceDownloadRow::exception_caught, this, [this](std::string function_name){ - update_enabled_status(); show_error_box(function_name); } ); @@ -110,14 +108,13 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt connect( &m_value.row, &ResourceDownloadRow::download_failed, this, [this](){ - update_enabled_status(); show_download_failed_box(); } ); } -void DownloadButtonWidget::update_enabled_status(){ +void DownloadButtonWidget::update_UI_state(){ if (m_value.get_enabled()){ m_button->setEnabled(true); m_button->setText("Download"); @@ -161,8 +158,7 @@ void DownloadButtonWidget::show_download_confirm_box( return; } if (clicked == cancel){ - m_value.set_enabled(true); - update_enabled_status(); + m_value.row.update_button_state(ButtonState::READY); return; } } @@ -185,20 +181,40 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va m_button->setFont(font); m_button->setText("Delete"); + + // update the UI based on m_enabled, when the button is constructed + update_UI_state(); + + // when the button is clicked, runs row.update_button_state(), which updates the button state + // also, show the delete confirm box connect( m_button, &QPushButton::clicked, - m_button, [&](bool){ + this, [&](bool){ + m_value.row.update_button_state(ButtonState::DELETE); show_delete_confirm_box(); cout << "Clicked Delete Button" << endl; } ); - // connect( - // &m_value.row, &ResourceDownloadRow::delete_finished, - // this, [this](){ - // // update_enabled_status(); - // } - // ); + // when button_state_updated, update the UI state to match + connect( + &m_value.row, &ResourceDownloadRow::button_state_updated, + this, [this](){ + update_UI_state(); + } + ); + +} + + +void DeleteButtonWidget::update_UI_state(){ + if (m_value.get_enabled()){ + m_button->setEnabled(true); + m_button->setText("Delete"); + }else{ + m_button->setEnabled(false); + m_button->setText("Deleting..."); + } } @@ -212,7 +228,7 @@ void DeleteButtonWidget::show_delete_confirm_box(){ box.setTextFormat(Qt::RichText); std::string title = "Delete"; - std::string message_body = "Are you suer you want to delete this resource?"; + std::string message_body = "Are you sure you want to delete this resource?"; box.setWindowTitle(QString::fromStdString(title)); box.setText(QString::fromStdString(message_body)); @@ -230,8 +246,7 @@ void DeleteButtonWidget::show_delete_confirm_box(){ return; } if (clicked == cancel){ - // m_value.set_enabled(true); - // update_enabled_status(); + m_value.row.update_button_state(ButtonState::READY); return; } } @@ -250,13 +265,37 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va m_button->setFont(font); m_button->setText("Cancel"); + // update the UI based on m_enabled, when the button is constructed + update_UI_state(); + + // when the button is clicked, runs row.update_button_state(), which updates the button state + // also, set cancel state to true connect( m_button, &QPushButton::clicked, - m_button, [&](bool){ - m_value.row.set_cancel_action(true); + this, [&](bool){ + m_value.row.update_button_state(ButtonState::CANCEL); cout << "Clicked Cancel Button" << endl; } ); + + // when button_state_updated, update the UI state to match + connect( + &m_value.row, &ResourceDownloadRow::button_state_updated, + this, [this](){ + update_UI_state(); + } + ); + +} + +void CancelButtonWidget::update_UI_state(){ + if (m_value.get_enabled()){ + m_button->setEnabled(true); + m_button->setText("Cancel"); + }else{ + m_button->setEnabled(false); + m_button->setText("Cancelling..."); + } } template class RegisterConfigWidget; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index a99bf97aff..966337937e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -29,7 +29,7 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ private: - void update_enabled_status(); + void update_UI_state(); void show_download_confirm_box( const std::string& title, const std::string& message_body @@ -52,6 +52,7 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget{ DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); private: + void update_UI_state(); void show_delete_confirm_box(); private: @@ -66,6 +67,9 @@ class CancelButtonWidget : public QWidget, public ConfigWidget{ public: CancelButtonWidget(QWidget& parent, ResourceCancelButton& value); +private: + void update_UI_state(); + private: ResourceCancelButton& m_value; QPushButton* m_button; diff --git a/SerialPrograms/Source/StaticRegistrationQt.cpp b/SerialPrograms/Source/StaticRegistrationQt.cpp index 625c70325c..f4cabb80a6 100644 --- a/SerialPrograms/Source/StaticRegistrationQt.cpp +++ b/SerialPrograms/Source/StaticRegistrationQt.cpp @@ -105,6 +105,8 @@ void register_all_statics(){ // Resource Download RegisterConfigWidget(); RegisterConfigWidget(); + RegisterConfigWidget(); + RegisterConfigWidget(); // Integrations RegisterConfigWidget(); From 8484ba258d9078b276ce79b716fdabed120330bf Mon Sep 17 00:00:00 2001 From: jw098 Date: Sat, 4 Apr 2026 12:42:55 -0700 Subject: [PATCH 43/53] update UI state for progress bar --- .../ResourceDownload/ResourceDownloadRow.h | 2 + .../ResourceDownloadWidget.cpp | 44 ++++++++++++++++++- .../ResourceDownload/ResourceDownloadWidget.h | 3 ++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index c8ef840730..e5d31c12da 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -66,6 +66,8 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void start_delete(); void update_button_state(ButtonState state); + + inline ButtonState get_button_state(){ return m_button_state; } private: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 93d25cbb41..7e681a06a4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -11,6 +11,7 @@ #include #include #include "CommonFramework/Logging/Logger.h" +#include "Common/Cpp/Exceptions.h" #include "CommonFramework/Notifications/ProgramNotifications.h" #include "ResourceDownloadWidget.h" @@ -337,7 +338,7 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value if (m_progress_bar->isHidden()) { m_progress_bar->show(); // Make it visible when progress starts } - m_status_label->setText("Downloading:"); + m_status_label->setText("Downloading"); m_progress_bar->setValue(percentage_progress); // Simple Console Progress Bar // std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') @@ -355,10 +356,49 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value if (m_progress_bar->isHidden()) { m_progress_bar->show(); // Make it visible when progress starts } - m_status_label->setText("Unzipping:"); + m_status_label->setText("Unzipping"); m_progress_bar->setValue(percentage_progress); } ); + + // when button_state_updated, update the UI state to match + connect( + &m_value.row, &ResourceDownloadRow::button_state_updated, + this, [this](){ + update_UI_state(); + } + ); + +} + + +void ProgressBarWidget::update_UI_state(){ + ButtonState state = m_value.row.get_button_state(); + switch (state){ + case ButtonState::DOWNLOAD: + m_status_label->setText("Downloading"); + if (m_progress_bar->isHidden()) { + m_progress_bar->show(); + } + break; + case ButtonState::DELETE: + // m_status_label->setText(""); + // m_progress_bar->hide(); + m_progress_bar->setValue(0); + break; + case ButtonState::CANCEL: + // m_status_label->setText(""); + // m_progress_bar->hide(); + m_progress_bar->setValue(0); + break; + case ButtonState::READY: + m_status_label->setText(""); + m_progress_bar->hide(); + m_progress_bar->setValue(0); + break; + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "update_UI_state: Unknown enum."); + } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 966337937e..86f4d757af 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -83,6 +83,9 @@ class ProgressBarWidget : public QWidget, public ConfigWidget{ ~ProgressBarWidget(); ProgressBarWidget(QWidget& parent, ResourceProgressBar& value); +private: + void update_UI_state(); + private: ResourceProgressBar& m_value; QLabel* m_status_label; From 53daba0cfa3ed8800a07008eb1a1c48413df19b9 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sat, 4 Apr 2026 12:59:29 -0700 Subject: [PATCH 44/53] adjust text and width of buttons --- .../ResourceDownloadWidget.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 7e681a06a4..ada81f79b9 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -58,6 +58,10 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt font.setBold(true); m_button->setFont(font); + QFontMetrics metrics(m_button->font()); + int minWidth = metrics.horizontalAdvance("Downloading..."); + m_button->setMinimumWidth(minWidth); + // Button should be disabled when in the middle of downloading // this status is stored within ResourceDownloadButton::m_enabled // when the button is clicked, m_enabled is set to false @@ -121,7 +125,9 @@ void DownloadButtonWidget::update_UI_state(){ m_button->setText("Download"); }else{ m_button->setEnabled(false); - m_button->setText("Downloading..."); + if (m_value.row.get_button_state() == ButtonState::DOWNLOAD){ + m_button->setText("Downloading..."); + } } } @@ -182,6 +188,10 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va m_button->setFont(font); m_button->setText("Delete"); + QFontMetrics metrics(m_button->font()); + int minWidth = metrics.horizontalAdvance("Deleting..."); + m_button->setMinimumWidth(minWidth); + // update the UI based on m_enabled, when the button is constructed update_UI_state(); @@ -214,7 +224,9 @@ void DeleteButtonWidget::update_UI_state(){ m_button->setText("Delete"); }else{ m_button->setEnabled(false); - m_button->setText("Deleting..."); + if (m_value.row.get_button_state() == ButtonState::DELETE){ + m_button->setText("Deleting..."); + } } } @@ -266,6 +278,10 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va m_button->setFont(font); m_button->setText("Cancel"); + QFontMetrics metrics(m_button->font()); + int minWidth = metrics.horizontalAdvance("Cancelling..."); + m_button->setMinimumWidth(minWidth); + // update the UI based on m_enabled, when the button is constructed update_UI_state(); @@ -295,7 +311,9 @@ void CancelButtonWidget::update_UI_state(){ m_button->setText("Cancel"); }else{ m_button->setEnabled(false); - m_button->setText("Cancelling..."); + if (m_value.row.get_button_state() == ButtonState::CANCEL){ + m_button->setText("Cancelling..."); + } } } From d4ae873eb734578f5397bbbc7dbe9606e47d7333 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 7 Apr 2026 22:20:10 -0700 Subject: [PATCH 45/53] get SHA 256 after each download --- .../ResourceDownload/ResourceDownloadRow.cpp | 10 ++++ .../ResourceDownload/ResourceDownloadRow.h | 1 + .../ResourceDownloadWidget.cpp | 12 ++++ .../Source/CommonFramework/Tools/FileHash.cpp | 59 +++++++++++++++++++ .../Source/CommonFramework/Tools/FileHash.h | 19 ++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 6 files changed, 103 insertions(+) create mode 100644 SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Tools/FileHash.h diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 85f973e2d0..8537653229 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -12,6 +12,7 @@ #include "CommonFramework/Logging/Logger.h" #include "CommonFramework/Tools/FileDownloader.h" #include "CommonFramework/Tools/FileUnzip.h" +#include "CommonFramework/Tools/FileHash.h" #include "CommonFramework/Options/LabelCellOption.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" @@ -337,6 +338,15 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad } ); + std::string hash = + hash_file( + zip_path, + [this](int percentage_progress){ + hash_progress(percentage_progress); + } + ); + cout << hash << endl; + // unzip unzip_file( zip_path.c_str(), diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index e5d31c12da..6ff5912a6e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -38,6 +38,7 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ signals: void download_progress(int percentage); void unzip_progress(int percentage); + void hash_progress(int percentage); void metadata_fetch_finished(std::string popup_message); void exception_caught(std::string function_name); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index ada81f79b9..78d3b036a4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -379,6 +379,18 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value } ); + connect( + &m_value.row, &ResourceDownloadRow::hash_progress, + this, + [this](int percentage_progress){ + if (m_progress_bar->isHidden()) { + m_progress_bar->show(); // Make it visible when progress starts + } + m_status_label->setText("Verifying"); + m_progress_bar->setValue(percentage_progress); + } + ); + // when button_state_updated, update the UI state to match connect( &m_value.row, &ResourceDownloadRow::button_state_updated, diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp new file mode 100644 index 0000000000..08cd5cbcab --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp @@ -0,0 +1,59 @@ +/* File Hash + * + * From: https://github.com/PokemonAutomation/ + * + */ + + + + + +#include "Common/Cpp/Exceptions.h" +#include +#include +#include + + + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + + + +std::string hash_file(const std::string& file_path, std::function hash_progress) { + QFile file(QString::fromStdString(file_path)); + if (!file.open(QIODevice::ReadOnly)) { + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "hash_file: Could not open file."); + } + + QCryptographicHash hash(QCryptographicHash::Sha256); + qint64 file_size = file.size(); + qint64 total_bytes_read = 0; + + QByteArray buffer(1024 * 1024, 0); // Pre-allocate 1MB once + int last_percentage = -1; + while (!file.atEnd()) { + qint64 num_bytes_in_chunk = file.read(buffer.data(), buffer.size()); + if (total_bytes_read == -1) { + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "hash_file: Read error:" + file.errorString().toStdString()); + } + + hash.addData(buffer.data(), num_bytes_in_chunk); + total_bytes_read += num_bytes_in_chunk; + + double percent = (static_cast(total_bytes_read) / file_size) * 100.0; + int current_percent = static_cast(percent); + // Only trigger callback if the integer value has changed + if (current_percent > last_percentage){ + hash_progress(percent); + last_percentage = current_percent; + } + } + + return hash.result().toHex().toStdString(); +} + +} diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileHash.h b/SerialPrograms/Source/CommonFramework/Tools/FileHash.h new file mode 100644 index 0000000000..b2c9245b8f --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/FileHash.h @@ -0,0 +1,19 @@ +/* File Hash + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_FileHash_H +#define PokemonAutomation_FileHash_H + +#include +#include + +namespace PokemonAutomation{ + +// uses SHA 256 +std::string hash_file(const std::string& file_path, std::function hash_progress); + +} +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index b9b3ef6d3c..c33ca65cd2 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -507,6 +507,8 @@ file(GLOB LIBRARY_SOURCES Source/CommonFramework/Tools/ErrorDumper.h Source/CommonFramework/Tools/FileDownloader.cpp Source/CommonFramework/Tools/FileDownloader.h + Source/CommonFramework/Tools/FileHash.cpp + Source/CommonFramework/Tools/FileHash.h Source/CommonFramework/Tools/FileUnzip.cpp Source/CommonFramework/Tools/FileUnzip.h Source/CommonFramework/Tools/GlobalThreadPools.cpp From 7ea35b35deab9d4d93f9c5c70ea16b339764acbe Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 7 Apr 2026 22:46:16 -0700 Subject: [PATCH 46/53] compare with expected hash --- .../ResourceDownload/ResourceDownloadHelpers.cpp | 10 ++++++++-- .../ResourceDownload/ResourceDownloadHelpers.h | 1 + .../ResourceDownload/ResourceDownloadRow.cpp | 7 ++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index 2e551255c0..329e535295 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -49,6 +49,7 @@ std::vector deserialize_resource_list_json(const Jso size_t compressed_bytes = (size_t)resource_obj.get_integer_throw("CompressedBytes"); size_t decompressed_bytes = (size_t)resource_obj.get_integer_throw("DecompressedBytes"); std::string url = resource_obj.get_string_throw("URL"); + std::string sha_256 = resource_obj.get_string_throw("SHA_256"); DownloadedResourceMetadata resource = { resource_name, @@ -56,7 +57,8 @@ std::vector deserialize_resource_list_json(const Jso resource_type, compressed_bytes, decompressed_bytes, - url + url, + sha_256 }; resources.emplace_back(std::move(resource)); @@ -64,7 +66,9 @@ std::vector deserialize_resource_list_json(const Jso } }catch (ParseException&){ - throw ParseException("JSON parsing error. Given JSON file doesn't match the expected format."); + std::cerr << "JSON parsing error. Given JSON file doesn't match the expected format." << endl; + // throw ParseException("JSON parsing error. Given JSON file doesn't match the expected format."); + return std::vector(); } return resources; @@ -72,6 +76,7 @@ std::vector deserialize_resource_list_json(const Jso const std::vector& local_resource_download_list(){ + // cout << "local_resource_download_list" << endl; static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); return local_resources; @@ -96,6 +101,7 @@ const JsonValue& remote_resource_download_list_json(){ } const std::vector& remote_resource_download_list(){ + // cout << "remote_resource_download_list" << endl; static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); return remote_resources; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h index 25372ddc2c..f9775a7c5b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.h @@ -29,6 +29,7 @@ struct DownloadedResourceMetadata{ size_t size_compressed_bytes; size_t size_decompressed_bytes; std::string url; + std::string sha_256; }; enum class ResourceVersionStatus{ diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 8537653229..cfab48afdf 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -338,6 +338,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad } ); + // hash std::string hash = hash_file( zip_path, @@ -345,7 +346,11 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad hash_progress(percentage_progress); } ); - cout << hash << endl; + std::string expected_hash = resource_metadata.sha_256; + if (hash != expected_hash){ + throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, + "Downloaded file failed verification. SHA 256 hash did not match the expected value."); + } // unzip unzip_file( From 15cb29e72013c728e512b24abc1898161ee13f20 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 12:44:43 -0700 Subject: [PATCH 47/53] minior UI changes. update file size in table to KiB/MiB/GiB. --- .../ResourceDownload/ResourceDownloadRow.cpp | 7 ++++++- .../ResourceDownload/ResourceDownloadTable.cpp | 2 +- .../ResourceDownload/ResourceDownloadWidget.cpp | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index cfab48afdf..f9acebbd7e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -6,6 +6,7 @@ #include "CommonFramework/Globals.h" #include "Common/Cpp/Containers/Pimpl.tpp" +#include "Common/Cpp/PrettyPrint.h" // #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Tools/GlobalThreadPools.h" #include "CommonFramework/Exceptions/OperationFailedException.h" @@ -13,6 +14,7 @@ #include "CommonFramework/Tools/FileDownloader.h" #include "CommonFramework/Tools/FileUnzip.h" #include "CommonFramework/Tools/FileHash.h" +#include "Common/Cpp/Filesystem.h" #include "CommonFramework/Options/LabelCellOption.h" // #include "ResourceDownloadTable.h" #include "ResourceDownloadRow.h" @@ -65,7 +67,7 @@ struct ResourceDownloadRow::Data{ ) : m_resource_name(LockMode::LOCK_WHILE_RUNNING, resource_name) , m_file_size(file_size) - , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, std::to_string(file_size)) + , m_file_size_label(LockMode::LOCK_WHILE_RUNNING, tostr_bytes(file_size)) , m_is_downloaded(is_downloaded) , m_is_downloaded_label(LockMode::LOCK_WHILE_RUNNING, is_downloaded_string(is_downloaded)) , m_version_num(version_num) @@ -352,6 +354,9 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad "Downloaded file failed verification. SHA 256 hash did not match the expected value."); } + // Filesystem::Path p{zip_path}; + // cout << "File size: " << std::filesystem::file_size(p) << endl; + // unzip unzip_file( zip_path.c_str(), diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 79f8162744..2f06419659 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -65,7 +65,7 @@ ResourceDownloadTable::ResourceDownloadTable() std::vector ResourceDownloadTable::make_header() const{ std::vector ret{ "Resource", - "Size (MB)", + "Size", "Downloaded", "Version", "", diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 78d3b036a4..11be58b4eb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -31,10 +31,10 @@ void show_error_box(std::string function_name){ } void show_download_failed_box(){ - std::cerr << "Error: Download failed. Check your internet connection." << std::endl; + std::cerr << "Error: Download failed. Check your internet connection and check you have enough disk space." << std::endl; QMessageBox box; box.warning(nullptr, "Error:", - QString::fromStdString("Error: Download failed. Check your internet connection.")); + QString::fromStdString("Error: Download failed. Check your internet connection and check you have enough disk space.")); } From 36dfc7daafa69a9d85dfcea2cad1426a039f2ee7 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 13:04:41 -0700 Subject: [PATCH 48/53] minor changes --- .../ResourceDownload/ResourceDownloadRow.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index f9acebbd7e..c74b31f59e 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -242,7 +242,7 @@ std::string ResourceDownloadRow::predownload_warning_summary(RemoteMetadata& rem size_t compressed_size = remote_metadata.size_compressed_bytes; size_t decompressed_size = remote_metadata.size_decompressed_bytes; - std::string disk_space_requirement = "This will require " + std::to_string(decompressed_size + compressed_size) + " bytes of free space"; + std::string disk_space_requirement = "This will require " + tostr_bytes(decompressed_size + compressed_size) + " of free space"; if (local_version_num < remote_version_num){ predownload_warning = "The resource you are downloading is a more updated version than the program expects. " @@ -350,6 +350,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad ); std::string expected_hash = resource_metadata.sha_256; if (hash != expected_hash){ + std::cerr << "current hash: " << hash << endl; throw_and_log(logger, ErrorReport::NO_ERROR_REPORT, "Downloaded file failed verification. SHA 256 hash did not match the expected value."); } @@ -389,6 +390,15 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad set_version_status(ResourceVersionStatus::NOT_APPLICABLE); throw e; + }catch(...){ + // delete directory and the resource + fs::remove_all(resource_directory); + + // update the table labels + set_is_downloaded(false); + set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + + throw; } } From 334935fe7486256914ab022bdcf42eee1e7d1778 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 15:50:27 -0700 Subject: [PATCH 49/53] remove tabs --- .../ResourceDownload/ResourceDownloadHelpers.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp index 329e535295..9d08b31e16 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadHelpers.cpp @@ -77,9 +77,9 @@ std::vector deserialize_resource_list_json(const Jso const std::vector& local_resource_download_list(){ // cout << "local_resource_download_list" << endl; - static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); + static std::vector local_resources = deserialize_resource_list_json(load_json_file(RESOURCE_PATH() + "ResourceDownloadList.json")); - return local_resources; + return local_resources; } @@ -102,9 +102,9 @@ const JsonValue& remote_resource_download_list_json(){ const std::vector& remote_resource_download_list(){ // cout << "remote_resource_download_list" << endl; - static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); + static std::vector remote_resources = deserialize_resource_list_json(remote_resource_download_list_json()); - return remote_resources; + return remote_resources; } std::optional get_resource_version_num(Filesystem::Path folder_path){ From 15b69d809a5b385e347b6ecb4209e68034408a06 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 16:49:46 -0700 Subject: [PATCH 50/53] fix build --- .../ResourceDownload/ResourceDownloadTable.cpp | 10 +++++++++- .../ResourceDownload/ResourceDownloadWidget.cpp | 4 ++-- .../Source/CommonFramework/Tools/FileHash.cpp | 6 +++--- .../Source/CommonFramework/Tools/FileUnzip.cpp | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp index 2f06419659..6a2187241a 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadTable.cpp @@ -5,6 +5,7 @@ */ #include "CommonFramework/Globals.h" +#include "Common/Cpp/Exceptions.h" // #include "CommonFramework/Logging/Logger.h" // #include "CommonFramework/Tools/GlobalThreadPools.h" // #include "CommonFramework/Tools/FileDownloader.h" @@ -28,7 +29,14 @@ namespace PokemonAutomation{ std::vector> get_resource_download_rows(){ std::vector> resource_rows; - for (const DownloadedResourceMetadata& resource : local_resource_download_list()){ + std::vector resource_list; + try{ + resource_list = local_resource_download_list(); + }catch(FileException&){ + return {}; + } + + for (const DownloadedResourceMetadata& resource : resource_list){ std::string resource_name = resource.resource_name; uint16_t expected_version_num = resource.version_num.value(); std::optional current_version_num; // default nullopt diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 11be58b4eb..97f2d670a4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -104,7 +104,7 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // this connect handles all exception_caught() emitted by ResourceDownloadRow connect( &m_value.row, &ResourceDownloadRow::exception_caught, - this, [this](std::string function_name){ + this, [](std::string function_name){ show_error_box(function_name); } ); @@ -112,7 +112,7 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt // if download fails connect( &m_value.row, &ResourceDownloadRow::download_failed, - this, [this](){ + this, [](){ show_download_failed_box(); } ); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp index 08cd5cbcab..7342287c0b 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp @@ -37,18 +37,18 @@ std::string hash_file(const std::string& file_path, std::function has int last_percentage = -1; while (!file.atEnd()) { qint64 num_bytes_in_chunk = file.read(buffer.data(), buffer.size()); - if (total_bytes_read == -1) { + if (num_bytes_in_chunk == -1) { throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "hash_file: Read error:" + file.errorString().toStdString()); } - hash.addData(buffer.data(), num_bytes_in_chunk); + hash.addData(QByteArrayView(buffer.data(), num_bytes_in_chunk)); total_bytes_read += num_bytes_in_chunk; double percent = (static_cast(total_bytes_read) / file_size) * 100.0; int current_percent = static_cast(percent); // Only trigger callback if the integer value has changed if (current_percent > last_percentage){ - hash_progress(percent); + hash_progress(current_percent); last_percentage = current_percent; } } diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index 734a63088f..a0eef27369 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -133,7 +133,7 @@ void unzip_file( // zip_archive holds the state and metadata of the ZIP archive. if (!mz_zip_reader_init_file(&zip_archive, zip_path, 0)){ throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, - "unzip_file: failed to run mz_zip_reader_init_file. mz_zip_error: " + mz_zip_get_last_error(&zip_archive)); + "unzip_file: failed to run mz_zip_reader_init_file. mz_zip_error: " + std::to_string(mz_zip_get_last_error(&zip_archive))); } // This automatically calls mz_zip_reader_end when this function exits for any reason. From cf7a3d21f679911c645887f2f96ab01379cdd216 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 17:25:07 -0700 Subject: [PATCH 51/53] fix build --- .../CommonFramework/ResourceDownload/ResourceDownloadRow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 6ff5912a6e..937ee3d0fb 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -14,7 +14,7 @@ #include "Common/Cpp/Options/StaticTableOption.h" #include "ResourceDownloadHelpers.h" #include "ResourceDownloadOptions.h" - +#include namespace PokemonAutomation{ From 5824c487d5fe90810f6cce336690b8add51a63e7 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 19:24:11 -0700 Subject: [PATCH 52/53] use Filesystem::Path instead of implicitly using std::filesystem --- .../ResourceDownload/ResourceDownloadRow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index c74b31f59e..3b1645cd2b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -323,7 +323,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; try{ // delete directory and the old resource - fs::remove_all(resource_directory); + fs::remove_all(Filesystem::Path(resource_directory)); // download std::string zip_path = resource_directory + "/temp.zip"; @@ -372,7 +372,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad ); // delete old zip file - fs::remove(zip_path); + fs::remove(Filesystem::Path(zip_path)); if (m_data->m_cancel_action.load()){ throw OperationCancelledException(); @@ -383,7 +383,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad set_version_status(ResourceVersionStatus::CURRENT); }catch(OperationCancelledException& e){ // delete directory and the resource - fs::remove_all(resource_directory); + fs::remove_all(Filesystem::Path(resource_directory)); // update the table labels set_is_downloaded(false); @@ -392,7 +392,7 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad throw e; }catch(...){ // delete directory and the resource - fs::remove_all(resource_directory); + fs::remove_all(Filesystem::Path(resource_directory)); // update the table labels set_is_downloaded(false); From 7f03141db95ea1c226b66b7503376312b65fb9c8 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 20:22:43 -0700 Subject: [PATCH 53/53] fix build --- .../CommonFramework/ResourceDownload/ResourceDownloadRow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 3b1645cd2b..9f5a76592d 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -412,7 +412,7 @@ void ResourceDownloadRow::start_delete(){ std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; // delete directory and the old resource - fs::remove_all(resource_directory); + fs::remove_all(Filesystem::Path(resource_directory)); // update the table labels set_is_downloaded(false);