From af8be5094b24a6cdf783b1fb755371db1f189f2e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 12:55:56 -0700 Subject: [PATCH 01/65] 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 3c9da57a5e..512673d6eb 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 34fd0606ca72b8dd4ccd761d76b694a7afe6cb3f Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 13:19:49 -0700 Subject: [PATCH 02/65] 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 14bb4941c7df44013a83d7cfd3aeae40b5f2ba0e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 14:05:19 -0700 Subject: [PATCH 03/65] 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 512673d6eb..deb43ec0ef 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 9c8044dfb687ae6dc208af1bad446967039753b8 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 14:48:27 -0700 Subject: [PATCH 04/65] 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 9bb08923d200e62c76a507d3683c90f4d87b6e09 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 17:26:33 -0700 Subject: [PATCH 05/65] 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 98aee6c79a229bcd01ac6fe85caa33d878691382 Mon Sep 17 00:00:00 2001 From: jw098 Date: Sun, 15 Mar 2026 23:13:14 -0700 Subject: [PATCH 06/65] 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 4c1d407d19c11382162dfb0414952b780e8a9301 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 16 Mar 2026 21:11:30 -0700 Subject: [PATCH 07/65] 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 6554806ca1f54e4001ebcb7c1f84bddd7fd56e03 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 16 Mar 2026 21:39:20 -0700 Subject: [PATCH 08/65] 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 60c40247f1014c3019e8d7acb6ef4add916512ef Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 15:37:44 -0700 Subject: [PATCH 09/65] 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 40ba8508df..03f04fffaa 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 f7a66520cf8f85c891ad319dcc3509895a63ecb8 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 15:48:57 -0700 Subject: [PATCH 10/65] 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 e582f0f395374e359f4e43b396fd5418f4a06e6c Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 22:16:05 -0700 Subject: [PATCH 11/65] 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 64ae1e627d2302d5378c000934feeee335c908da Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 17 Mar 2026 22:45:04 -0700 Subject: [PATCH 12/65] 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 be129ed0b286040f2256964624ee5548d46e7f19 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 18 Mar 2026 21:51:25 -0700 Subject: [PATCH 13/65] 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 058e8c1534259d87fbf4aacb1bc6ed2d945cce7a Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 19 Mar 2026 15:08:28 -0700 Subject: [PATCH 14/65] 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 b136c2f6555ddc035003ecdc96c0fed2f45f0fc9 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 19 Mar 2026 21:27:44 -0700 Subject: [PATCH 15/65] 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 deb43ec0ef..874830e965 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 767d1485d8f384c00071a43b0dcfa489481e3a17 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 20 Mar 2026 13:03:38 -0700 Subject: [PATCH 16/65] 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 008317c4834fd29eab6f2e7438dfc962e6a2ac00 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 20 Mar 2026 22:30:52 -0700 Subject: [PATCH 17/65] 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 881349ba71aa2a872df026f2662e08d495d61070 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 23 Mar 2026 23:15:50 -0700 Subject: [PATCH 18/65] 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 874830e965..1e1028bcde 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 d30759c082c533eef0cb68ae7a242a51ee9c18ed Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 23 Mar 2026 23:35:41 -0700 Subject: [PATCH 19/65] 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 18362a03e278cd01877fc60060b56b6cddbbae31 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 24 Mar 2026 22:06:49 -0700 Subject: [PATCH 20/65] 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 1736cb8c5f8a2c665c64481c932d8dfb9af9cd13 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 24 Mar 2026 22:10:59 -0700 Subject: [PATCH 21/65] 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 8b9e41e888216a1c27da29df7c79133f7c382604 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 13:42:23 -0700 Subject: [PATCH 22/65] 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 fafd581bc889ba73197bf833ba5318cb9a8a3d6d Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 16:05:42 -0700 Subject: [PATCH 23/65] 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 6d6d9ad77eb1ad499469c1c88f921dbdfefaaecd Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 17:41:21 -0700 Subject: [PATCH 24/65] 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 2ad2b7350d6380f9dbc13f0c4f8d00ae7153a6bd Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 26 Mar 2026 22:01:36 -0700 Subject: [PATCH 25/65] 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 d63ed484c740f733fa5845211c9227dec5767837 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 12:09:16 -0700 Subject: [PATCH 26/65] 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 1e1028bcde..777c8d9a80 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 07450a84124167066bb263a9ee0d04da44dd570f Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 12:34:50 -0700 Subject: [PATCH 27/65] 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 c91ece7a362837ec750adff07224a31d08ac05cf Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 15:35:31 -0700 Subject: [PATCH 28/65] 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 5e9d39d8f8d1d97506df29d1a09226bf7de0dbb2 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 27 Mar 2026 16:39:54 -0700 Subject: [PATCH 29/65] 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 ee4fd7e99901584ae1994ee7de2b0d52e5f9a02b Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 21:30:11 -0700 Subject: [PATCH 30/65] 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 a154fbe1f5eddd4367045bd60a03dd787800abfa Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 22:01:47 -0700 Subject: [PATCH 31/65] 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 0b4a5f91f25013d90f8ea31e25032236a2b4bb35 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 22:36:18 -0700 Subject: [PATCH 32/65] 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 b3d0184953..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 17a280d8bd33a18da23f6eff2576fe1dee308138 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 23:02:54 -0700 Subject: [PATCH 33/65] 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 eaa37db1020d51eb1062493b52e503f62b6638a7 Mon Sep 17 00:00:00 2001 From: jw098 Date: Mon, 30 Mar 2026 23:13:44 -0700 Subject: [PATCH 34/65] 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 d9012cab9aa0609b119011ae9016b08adb76f9c3 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 31 Mar 2026 21:40:50 -0700 Subject: [PATCH 35/65] 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 918f18feac86bcdcec57fb214e690cb201545f57 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 31 Mar 2026 21:57:21 -0700 Subject: [PATCH 36/65] 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 788942205635bb3e677f1c8eff4034bbd81da630 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 15:08:31 -0700 Subject: [PATCH 37/65] 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 8913a4b440582d5112c1d8a93de9e6f9b6d7fb23 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 21:29:31 -0700 Subject: [PATCH 38/65] 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 4196b88fd5f002344fcb2a38cba3a719f2e5aaed Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 1 Apr 2026 22:30:26 -0700 Subject: [PATCH 39/65] 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 a61d90b92e4a6d62ed180ec1ec92e4ba36875830 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 2 Apr 2026 16:28:01 -0700 Subject: [PATCH 40/65] 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 861031d01d915567d7006169c7ea3fec601dfa35 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 2 Apr 2026 18:34:38 -0700 Subject: [PATCH 41/65] 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 8620003bc0bdb6227507c6aa8d6206b0df7acf67 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 3 Apr 2026 10:09:40 -0700 Subject: [PATCH 42/65] 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 30adc57065e6b304940ea69ece5dcb5898495e7e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sat, 4 Apr 2026 12:42:55 -0700 Subject: [PATCH 43/65] 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 3821cc0e730aaf04c1cb7116293f07d5f085ef3e Mon Sep 17 00:00:00 2001 From: jw098 Date: Sat, 4 Apr 2026 12:59:29 -0700 Subject: [PATCH 44/65] 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 bd42eca3520de9cfe7d75b4dbee4ed2876993b7f Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 7 Apr 2026 22:20:10 -0700 Subject: [PATCH 45/65] 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 777c8d9a80..b262b0bd6e 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 9ab8984cccdc8650a811bcff966d85ca92c3ccf2 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 7 Apr 2026 22:46:16 -0700 Subject: [PATCH 46/65] 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 ab0a65ee08eecb681f127c47f1ce89793a5c4d57 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 12:44:43 -0700 Subject: [PATCH 47/65] 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 fccc36acdaa7d28d7c8201fc50e7fc07b2012b4f Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 13:04:41 -0700 Subject: [PATCH 48/65] 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 be7f2abed54d1b57e86e5bcec51f5e0e82963070 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 15:50:27 -0700 Subject: [PATCH 49/65] 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 e29ba6c811c148819e2e37123ffbbb332e1ce4d7 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 16:49:46 -0700 Subject: [PATCH 50/65] 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 66dca0df827dce49909a5162f13721cd0ecbe0d2 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 17:25:07 -0700 Subject: [PATCH 51/65] 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 a3d376f8666cce07c465ac07f8ba1f39216c7cc3 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 19:24:11 -0700 Subject: [PATCH 52/65] 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 0932d46ae31dc6626e215695edead141594b15e8 Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 8 Apr 2026 20:22:43 -0700 Subject: [PATCH 53/65] 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); From 4b82781cde4deade21a5d57d574db75488da9fa5 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 10 Apr 2026 23:26:39 -0700 Subject: [PATCH 54/65] draft: use CancellableScope --- .../ResourceDownload/ResourceDownloadRow.cpp | 43 ++++++++++--------- .../ResourceDownload/ResourceDownloadRow.h | 14 +++++- .../CommonFramework/Tools/FileDownloader.cpp | 10 ++--- .../CommonFramework/Tools/FileDownloader.h | 6 ++- .../Source/CommonFramework/Tools/FileHash.cpp | 6 ++- .../Source/CommonFramework/Tools/FileHash.h | 4 +- .../CommonFramework/Tools/FileUnzip.cpp | 16 +++---- .../Source/CommonFramework/Tools/FileUnzip.h | 8 ++-- 8 files changed, 65 insertions(+), 42 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 9f5a76592d..6fd41a2638 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -31,6 +31,19 @@ namespace PokemonAutomation{ namespace fs = std::filesystem; + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// DownloadThread +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +DownloadThread::~DownloadThread(){ + m_worker.wait_and_ignore_exceptions(); +} +DownloadThread::DownloadThread(ResourceDownloadRow& row) + : CancellableScope() + , m_row(row) +{} ///////////////////////////////////////////////////////////////////////////////////////////////////////// // ResourceDownloadRow ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,7 +86,6 @@ 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; @@ -88,8 +100,6 @@ struct ResourceDownloadRow::Data{ ResourceVersionStatus m_version_status; LabelCellOption m_version_status_label; - std::atomic m_cancel_action; - }; @@ -104,9 +114,6 @@ 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(){ @@ -322,27 +329,30 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad std::string resource_directory = DOWNLOADED_RESOURCE_PATH() + resource_name; try{ + + DownloadThread scope(*this); // TODO: use the thread itself, not just CancellableScope + + // delete directory and the old resource fs::remove_all(Filesystem::Path(resource_directory)); // download std::string zip_path = resource_directory + "/temp.zip"; FileDownloader::download_file_to_disk( + scope, logger, url, zip_path, expected_size, [this](int percentage_progress){ download_progress(percentage_progress); - }, - [this](){ - return m_data->m_cancel_action.load(); } ); // hash std::string hash = hash_file( + scope, zip_path, [this](int percentage_progress){ hash_progress(percentage_progress); @@ -360,23 +370,18 @@ void ResourceDownloadRow::run_download(DownloadedResourceMetadata resource_metad // unzip unzip_file( + scope, zip_path.c_str(), resource_directory.c_str(), [this](int percentage_progress){ unzip_progress(percentage_progress); } - , - [this](){ - return m_data->m_cancel_action.load(); - } ); // delete old zip file fs::remove(Filesystem::Path(zip_path)); - if (m_data->m_cancel_action.load()){ - throw OperationCancelledException(); - } + scope.throw_if_cancelled(); // update the table labels set_is_downloaded(true); @@ -438,7 +443,6 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ 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; @@ -449,7 +453,6 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ 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; @@ -460,7 +463,8 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ m_download_button.set_enabled(false); m_delete_button.set_enabled(false); m_cancel_button.set_enabled(false); - set_cancel_action(true); + // set_cancel_action(true); + // TODO: set cancel to true m_button_state = state; } break; @@ -468,7 +472,6 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ 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: diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 937ee3d0fb..cdd11e82e7 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -10,6 +10,7 @@ #include #include "Common/Cpp/Containers/Pimpl.h" #include "Common/Cpp/Concurrency/AsyncTask.h" +#include "Common/Cpp/CancellableScope.h" // #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" #include "ResourceDownloadHelpers.h" @@ -18,6 +19,18 @@ namespace PokemonAutomation{ + +class DownloadThread : public CancellableScope { +public: + ~DownloadThread(); + DownloadThread(ResourceDownloadRow& row); + + +private: + ResourceDownloadRow& m_row; + AsyncTask m_worker; +}; + enum class ButtonState{ DOWNLOAD, DELETE, @@ -51,7 +64,6 @@ 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); void ensure_remote_metadata_loaded(); std::string predownload_warning_summary(RemoteMetadata& remote_metadata); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp index b441be9ed8..6ffb8457b1 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.cpp @@ -94,12 +94,12 @@ std::string download_file(Logger& logger, const std::string& url){ } void download_file_to_disk( + CancellableScope& scope, Logger& logger, const std::string& url, const std::string& file_path, qint64 expected_size, - std::function progress_callback, - std::function is_cancelled + std::function progress_callback ){ // cout << "download_file()" << endl; QNetworkAccessManager network_access_manager; @@ -127,9 +127,9 @@ void download_file_to_disk( // Progress Bar Logic. and check for Cancel QObject::connect(reply, &QNetworkReply::downloadProgress, - [reply, expected_size, progress_callback, is_cancelled](qint64 bytesReceived, qint64 bytesTotal) { + [reply, &scope, expected_size, progress_callback](qint64 bytesReceived, qint64 bytesTotal) { - if (is_cancelled()){ + if (scope.cancelled()){ reply->abort(); } @@ -167,7 +167,7 @@ void download_file_to_disk( "Failed to commit file to disk: " + file_path); } } else { - if (is_cancelled()){ + if (scope.cancelled()){ logger.log("Download cancelled by user."); throw OperationCancelledException(); }else{ diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h index 43b0eda9c0..7ebb840fd4 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileDownloader.h @@ -10,6 +10,8 @@ #include #include #include +#include "Common/Cpp/CancellableScope.h" + namespace PokemonAutomation{ class Logger; @@ -21,12 +23,12 @@ std::string download_file(Logger& logger, const std::string& url); // Throws OperationFailedException if failed to download. void download_file_to_disk( + CancellableScope& scope, Logger& logger, const std::string& url, const std::string& file_path, qint64 expected_size, - std::function progress_callback, - std::function is_cancelled + std::function progress_callback ); // Throws OperationFailedException if failed to download. diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp index 7342287c0b..1b8a6be137 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileHash.cpp @@ -12,7 +12,7 @@ #include #include #include - +#include "FileHash.h" #include @@ -23,7 +23,7 @@ namespace PokemonAutomation{ -std::string hash_file(const std::string& file_path, std::function hash_progress) { +std::string hash_file(CancellableScope& scope, 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."); @@ -36,6 +36,8 @@ std::string hash_file(const std::string& file_path, std::function has QByteArray buffer(1024 * 1024, 0); // Pre-allocate 1MB once int last_percentage = -1; while (!file.atEnd()) { + scope.throw_if_cancelled(); + qint64 num_bytes_in_chunk = file.read(buffer.data(), buffer.size()); if (num_bytes_in_chunk == -1) { throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "hash_file: Read error:" + file.errorString().toStdString()); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileHash.h b/SerialPrograms/Source/CommonFramework/Tools/FileHash.h index b2c9245b8f..e069b70c58 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileHash.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileHash.h @@ -9,11 +9,13 @@ #include #include +#include "Common/Cpp/CancellableScope.h" + namespace PokemonAutomation{ // uses SHA 256 -std::string hash_file(const std::string& file_path, std::function hash_progress); +std::string hash_file(CancellableScope& scope, const std::string& file_path, std::function hash_progress); } #endif diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp index a0eef27369..162fb7078e 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.cpp @@ -34,7 +34,7 @@ struct ProgressData { uint64_t processed_bytes; int last_percentage; std::function progress_callback; - std::function is_cancelled; + CancellableScope& scope; }; // Callback triggered for every chunk of decompressed data @@ -42,7 +42,7 @@ struct ProgressData { size_t write_callback(void* pOpaque, [[maybe_unused]] mz_uint64 file_ofs, const void* pBuf, size_t n){ ProgressData* data = static_cast(pOpaque); - if (data->is_cancelled()){ + if (data->scope.cancelled()){ return 0; // this causes mz_zip_reader_extract_to_callback to return an error } @@ -107,10 +107,10 @@ bool is_safe(const std::string& target_dir, const std::string& entry_name){ } void unzip_file( + CancellableScope& scope, const char* zip_path, const char* target_dir, - std::function progress_callback, - std::function is_cancelled + std::function progress_callback ){ Filesystem::Path p{zip_path}; if (!fs::exists(p)){ @@ -148,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()) throw OperationCancelledException(); + scope.throw_if_cancelled(); mz_zip_archive_file_stat file_stat; // holds info on the specific file @@ -161,7 +161,7 @@ void unzip_file( uint64_t total_processed_bytes = 0; for (int i = 0; i < num_files; i++){ - if (is_cancelled()) throw OperationCancelledException(); + scope.throw_if_cancelled(); mz_zip_archive_file_stat file_stat; // holds info on the specific file @@ -193,7 +193,7 @@ void unzip_file( 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 }; + ProgressData progress = { &out_file, total_uncompressed_size, total_processed_bytes, -1, progress_callback, scope }; // 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 @@ -201,7 +201,7 @@ void unzip_file( if (!status){ out_file.close(); - if (is_cancelled()){ + if (scope.cancelled()){ // close and delete the partially unzipped file fs::remove(out_path, ec); throw OperationCancelledException(); diff --git a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h index 9eb26fd931..286a3643ff 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h +++ b/SerialPrograms/Source/CommonFramework/Tools/FileUnzip.h @@ -9,18 +9,20 @@ #include #include +#include "Common/Cpp/CancellableScope.h" + 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 OperationCancelledException if the CancellableScope is cancelled // throw InternalProgramError if unzipping fails. void unzip_file( + CancellableScope& scope, const char* zip_path, const char* target_dir, - std::function progress_callback, - std::function is_cancelled + std::function progress_callback ); } From 80e2d68b891955b17590699b7a9a7aff8ee004b5 Mon Sep 17 00:00:00 2001 From: jw098 Date: Tue, 14 Apr 2026 15:38:35 -0700 Subject: [PATCH 55/65] move thread into DownloadThread class --- .../ResourceDownload/ResourceDownloadRow.cpp | 294 ++++++++++-------- .../ResourceDownload/ResourceDownloadRow.h | 14 +- 2 files changed, 173 insertions(+), 135 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 6fd41a2638..b461324e95 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -38,12 +38,147 @@ namespace PokemonAutomation{ ///////////////////////////////////////////////////////////////////////////////////////////////////////// DownloadThread::~DownloadThread(){ + // cout << "~DownloadThread" << endl; + this->cancel(); m_worker.wait_and_ignore_exceptions(); } DownloadThread::DownloadThread(ResourceDownloadRow& row) : CancellableScope() , m_row(row) {} + +void DownloadThread::start_download_thread(){ + m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + [this]{ + + // runs when lambda is finished + // updates button state, and cleans up this thread + // use a signal/slot, since an object can't safely call reset() on itself. + auto guard = qScopeGuard([&] { emit m_row.download_done(); }); + + try { + // std::this_thread::sleep_for(std::chrono::seconds(7)); + RemoteMetadata& remote_handle = m_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; + 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; + + }catch(OperationCancelledException&){ + // user cancelled action + + }catch(OperationFailedException&){ + emit m_row.download_failed(); + }catch(...){ + emit m_row.exception_caught("ResourceDownloadButton::start_download"); + } + + + // emit m_row.download_done(); + } + ); + +} + +void DownloadThread::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; + try{ + + // delete directory and the old resource + fs::remove_all(Filesystem::Path(resource_directory)); + + // download + std::string zip_path = resource_directory + "/temp.zip"; + FileDownloader::download_file_to_disk( + *this, + logger, + url, + zip_path, + expected_size, + [this](int percentage_progress){ + m_row.download_progress(percentage_progress); + } + ); + + // hash + std::string hash = + hash_file( + *this, + zip_path, + [this](int percentage_progress){ + m_row.hash_progress(percentage_progress); + } + ); + 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."); + } + + // Filesystem::Path p{zip_path}; + // cout << "File size: " << std::filesystem::file_size(p) << endl; + + // unzip + unzip_file( + *this, + zip_path.c_str(), + resource_directory.c_str(), + [this](int percentage_progress){ + m_row.unzip_progress(percentage_progress); + } + ); + + // delete old zip file + fs::remove(Filesystem::Path(zip_path)); + + throw_if_cancelled(); + + // update the table labels + m_row.set_is_downloaded(true); + m_row.set_version_status(ResourceVersionStatus::CURRENT); + }catch(OperationCancelledException& e){ + // delete directory and the resource + fs::remove_all(Filesystem::Path(resource_directory)); + + // update the table labels + m_row.set_is_downloaded(false); + m_row.set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + + throw e; + }catch(...){ + // delete directory and the resource + fs::remove_all(Filesystem::Path(resource_directory)); + + // update the table labels + m_row.set_is_downloaded(false); + m_row.set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + + throw; + } + +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////// // ResourceDownloadRow ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -117,8 +252,9 @@ void ResourceDownloadRow::set_is_downloaded(bool is_downloaded){ ResourceDownloadRow::~ResourceDownloadRow(){ + // cout << "~ResourceDownloadRow" << endl; m_worker1.wait_and_ignore_exceptions(); - m_worker2.wait_and_ignore_exceptions(); + // m_worker2.wait_and_ignore_exceptions(); m_worker3.wait_and_ignore_exceptions(); } ResourceDownloadRow::ResourceDownloadRow( @@ -136,6 +272,15 @@ ResourceDownloadRow::ResourceDownloadRow( , m_cancel_button(*this) , m_progress_bar(*this) { + + connect( + this, &ResourceDownloadRow::download_done, + this, [this](){ + on_download_finished(); + }, + Qt::QueuedConnection + ); + 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); @@ -275,137 +420,11 @@ std::string ResourceDownloadRow::predownload_warning_summary(RemoteMetadata& rem 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; - - update_button_state(ButtonState::READY); - - }catch(OperationCancelledException&){ - update_button_state(ButtonState::READY); - return; - }catch(OperationFailedException&){ - update_button_state(ButtonState::READY); - emit download_failed(); - return; - }catch(...){ - update_button_state(ButtonState::READY); - emit exception_caught("ResourceDownloadButton::start_download"); - return; - } - } - ); - -} - - -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; - try{ - - DownloadThread scope(*this); // TODO: use the thread itself, not just CancellableScope - - - // delete directory and the old resource - fs::remove_all(Filesystem::Path(resource_directory)); - - // download - std::string zip_path = resource_directory + "/temp.zip"; - FileDownloader::download_file_to_disk( - scope, - logger, - url, - zip_path, - expected_size, - [this](int percentage_progress){ - download_progress(percentage_progress); - } - ); - // hash - std::string hash = - hash_file( - scope, - zip_path, - [this](int percentage_progress){ - hash_progress(percentage_progress); - } - ); - 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."); - } - - // Filesystem::Path p{zip_path}; - // cout << "File size: " << std::filesystem::file_size(p) << endl; - - // unzip - unzip_file( - scope, - zip_path.c_str(), - resource_directory.c_str(), - [this](int percentage_progress){ - unzip_progress(percentage_progress); - } - ); - - // delete old zip file - fs::remove(Filesystem::Path(zip_path)); - - scope.throw_if_cancelled(); - - // update the table labels - set_is_downloaded(true); - set_version_status(ResourceVersionStatus::CURRENT); - }catch(OperationCancelledException& e){ - // delete directory and the resource - fs::remove_all(Filesystem::Path(resource_directory)); - - // update the table labels - set_is_downloaded(false); - set_version_status(ResourceVersionStatus::NOT_APPLICABLE); - - throw e; - }catch(...){ - // delete directory and the resource - fs::remove_all(Filesystem::Path(resource_directory)); - - // update the table labels - set_is_downloaded(false); - set_version_status(ResourceVersionStatus::NOT_APPLICABLE); - - throw; + if (m_download_thread == nullptr){ + m_download_thread = std::make_unique(*this); + m_download_thread->start_download_thread(); } - } @@ -434,6 +453,16 @@ void ResourceDownloadRow::start_delete(){ } +void ResourceDownloadRow::on_download_finished(){ + + update_button_state(ButtonState::READY); + if (m_download_thread){ + + // cout << "reset m_download_thread" << endl; + m_download_thread.reset(); // destroy the DownloadThread + } +} + void ResourceDownloadRow::update_button_state(ButtonState state){ switch (state){ case ButtonState::DOWNLOAD: @@ -463,8 +492,9 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ m_download_button.set_enabled(false); m_delete_button.set_enabled(false); m_cancel_button.set_enabled(false); - // set_cancel_action(true); - // TODO: set cancel to true + if (m_download_thread){ + m_download_thread->cancel(); // cancel the download thread + } m_button_state = state; } break; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index cdd11e82e7..989c54e9c4 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -25,6 +25,11 @@ class DownloadThread : public CancellableScope { ~DownloadThread(); DownloadThread(ResourceDownloadRow& row); +public: + void start_download_thread(); + + // throws OperationCancelledException if the user cancels the action + void run_download(DownloadedResourceMetadata resource_metadata); private: ResourceDownloadRow& m_row; @@ -55,8 +60,9 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void metadata_fetch_finished(std::string popup_message); void exception_caught(std::string function_name); + void download_done(); void download_failed(); - void download_finished(); + void download_completed(); void button_state_updated(); @@ -73,8 +79,8 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ // DownloadedResourceMetadata initialize_local_metadata(); void start_download(); - // throws OperationCancelledException if the user cancels the action - void run_download(DownloadedResourceMetadata resource_metadata); + + void on_download_finished(); void start_delete(); @@ -101,6 +107,8 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ AsyncTask m_worker2; AsyncTask m_worker3; + std::unique_ptr m_download_thread; + From bdcf4bcb4c7ba0efd66aa88886f0cc51a757712b Mon Sep 17 00:00:00 2001 From: jw098 Date: Wed, 15 Apr 2026 23:46:36 -0700 Subject: [PATCH 56/65] refactor DownloadThread and its lifetime --- .../ResourceDownload/ResourceDownloadRow.cpp | 41 ++++++++++--------- .../ResourceDownload/ResourceDownloadRow.h | 8 ++-- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index b461324e95..3d6fa336df 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -48,13 +48,19 @@ DownloadThread::DownloadThread(ResourceDownloadRow& row) {} void DownloadThread::start_download_thread(){ + auto self = shared_from_this(); m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this]{ + [this, self]{ // runs when lambda is finished - // updates button state, and cleans up this thread - // use a signal/slot, since an object can't safely call reset() on itself. - auto guard = qScopeGuard([&] { emit m_row.download_done(); }); + // updates button state, and releases DownloadRow's ownership over this thread + // the thread cleans itself up when self goes out of scope at the end of this lambda + struct ScopeGuard { + DownloadThread* thread_ptr; + ~ScopeGuard() { + thread_ptr->m_row.on_download_finished(); + } + } guard{this}; try { // std::this_thread::sleep_for(std::chrono::seconds(7)); @@ -86,8 +92,6 @@ void DownloadThread::start_download_thread(){ emit m_row.exception_caught("ResourceDownloadButton::start_download"); } - - // emit m_row.download_done(); } ); @@ -272,15 +276,6 @@ ResourceDownloadRow::ResourceDownloadRow( , m_cancel_button(*this) , m_progress_bar(*this) { - - connect( - this, &ResourceDownloadRow::download_done, - this, [this](){ - on_download_finished(); - }, - Qt::QueuedConnection - ); - 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); @@ -422,7 +417,7 @@ std::string ResourceDownloadRow::predownload_warning_summary(RemoteMetadata& rem void ResourceDownloadRow::start_download(){ if (m_download_thread == nullptr){ - m_download_thread = std::make_unique(*this); + m_download_thread = std::make_shared(*this); m_download_thread->start_download_thread(); } } @@ -456,10 +451,16 @@ void ResourceDownloadRow::start_delete(){ void ResourceDownloadRow::on_download_finished(){ update_button_state(ButtonState::READY); - if (m_download_thread){ - - // cout << "reset m_download_thread" << endl; - m_download_thread.reset(); // destroy the DownloadThread + { + std::lock_guard lock(m_thread_mutex); + if (m_download_thread){ + // cout << "reset m_download_thread" << endl; + + // This releases the Row's ownership. + // The object will actually delete itself once the lambda + // in start_download_thread() finishes (releasing 'self'). + m_download_thread.reset(); + } } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 989c54e9c4..4adf737f62 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -20,12 +20,13 @@ namespace PokemonAutomation{ -class DownloadThread : public CancellableScope { +class DownloadThread : public CancellableScope, public std::enable_shared_from_this { public: ~DownloadThread(); DownloadThread(ResourceDownloadRow& row); public: + // static std::shared_ptr create(ResourceDownloadRow& row); void start_download_thread(); // throws OperationCancelledException if the user cancels the action @@ -60,7 +61,6 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void metadata_fetch_finished(std::string popup_message); void exception_caught(std::string function_name); - void download_done(); void download_failed(); void download_completed(); @@ -107,7 +107,9 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ AsyncTask m_worker2; AsyncTask m_worker3; - std::unique_ptr m_download_thread; + std::shared_ptr m_download_thread; + + std::mutex m_thread_mutex; From 0f3578cec3fcd55eb50791b2235d12e71a4c8795 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 16 Apr 2026 00:03:06 -0700 Subject: [PATCH 57/65] update initialization of DownloadThread to use factory method to ensure it is always a shared_ptr --- .../ResourceDownload/ResourceDownloadRow.cpp | 12 ++++++++---- .../ResourceDownload/ResourceDownloadRow.h | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 3d6fa336df..4b9c5757e5 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -42,19 +42,23 @@ DownloadThread::~DownloadThread(){ this->cancel(); m_worker.wait_and_ignore_exceptions(); } -DownloadThread::DownloadThread(ResourceDownloadRow& row) +DownloadThread::DownloadThread(ConstructorKey, ResourceDownloadRow& row) : CancellableScope() , m_row(row) {} +std::shared_ptr DownloadThread::create(ResourceDownloadRow& row){ + return std::make_shared(ConstructorKey{}, row); +} + void DownloadThread::start_download_thread(){ auto self = shared_from_this(); m_worker = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( - [this, self]{ + [this, self]{ // runs when lambda is finished // updates button state, and releases DownloadRow's ownership over this thread - // the thread cleans itself up when self goes out of scope at the end of this lambda + // the thread cleans itself up when `self` goes out of scope at the end of this lambda struct ScopeGuard { DownloadThread* thread_ptr; ~ScopeGuard() { @@ -417,7 +421,7 @@ std::string ResourceDownloadRow::predownload_warning_summary(RemoteMetadata& rem void ResourceDownloadRow::start_download(){ if (m_download_thread == nullptr){ - m_download_thread = std::make_shared(*this); + m_download_thread = DownloadThread::create(*this); //std::make_shared(*this); m_download_thread->start_download_thread(); } } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 4adf737f62..c82815ced0 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -20,13 +20,23 @@ namespace PokemonAutomation{ +// must be initialized as shared_ptr, so that `shared_from_this` can work +// so you're forced to use the factory method `create` class DownloadThread : public CancellableScope, public std::enable_shared_from_this { + +private: + struct ConstructorKey { + explicit ConstructorKey() = default; + }; + public: ~DownloadThread(); - DownloadThread(ResourceDownloadRow& row); + DownloadThread(ConstructorKey, ResourceDownloadRow& row); public: - // static std::shared_ptr create(ResourceDownloadRow& row); + // factor method to initialize DownloadThread, since it must be a shared_ptr + static std::shared_ptr create(ResourceDownloadRow& row); + void start_download_thread(); // throws OperationCancelledException if the user cancels the action From df476294a9d9720a5dff50d112d1303940587ef5 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 16 Apr 2026 11:38:31 -0700 Subject: [PATCH 58/65] fix ResourceDownload buttons, which fixes the issue with blank box in corner --- .../ResourceDownloadWidget.cpp | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 97f2d670a4..2de7639c7a 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -49,14 +49,21 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt , ConfigWidget(value, *this) , m_value(value) { - m_button = new QPushButton(&parent); - m_widget = m_button; + + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + m_button = new QPushButton("Download", this); + // m_widget = this; + + layout->addWidget(m_button); // cout << "Constructor for DownloadButtonWidget" << endl; QFont font; font.setBold(true); m_button->setFont(font); + m_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QFontMetrics metrics(m_button->font()); int minWidth = metrics.horizontalAdvance("Downloading..."); @@ -180,13 +187,19 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va , ConfigWidget(value, *this) , m_value(value) { - m_button = new QPushButton(&parent); - m_widget = m_button; + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + m_button = new QPushButton("Delete", this); + // m_widget = m_button; + + layout->addWidget(m_button); QFont font; font.setBold(true); m_button->setFont(font); m_button->setText("Delete"); + m_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QFontMetrics metrics(m_button->font()); int minWidth = metrics.horizontalAdvance("Deleting..."); @@ -270,13 +283,19 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va , ConfigWidget(value, *this) , m_value(value) { - m_button = new QPushButton(&parent); - m_widget = m_button; + QHBoxLayout* layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + m_button = new QPushButton("Cancel", this); + // m_widget = m_button; + + layout->addWidget(m_button); QFont font; font.setBold(true); m_button->setFont(font); m_button->setText("Cancel"); + m_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QFontMetrics metrics(m_button->font()); int minWidth = metrics.horizontalAdvance("Cancelling..."); From 741d1c6b41b94d0c6d56cad24438361eabe16353 Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 16 Apr 2026 22:14:30 -0700 Subject: [PATCH 59/65] add missing semicolon --- .../Source/CommonFramework/VideoPipeline/VideoOverlay.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.h b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.h index 340d8a23c1..2712b617c8 100644 --- a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.h +++ b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.h @@ -80,7 +80,7 @@ class VideoOverlay{ public: struct MouseListener{ - virtual void on_mouse_press(double x, double y){} + virtual void on_mouse_press(double x, double y){}; virtual void on_mouse_release(double x, double y){}; virtual void on_mouse_move(double x, double y){}; }; @@ -94,7 +94,7 @@ class VideoOverlay{ void issue_mouse_move(double x, double y); struct KeyEventListener{ - virtual void on_key_press(QKeyEvent* event){} + virtual void on_key_press(QKeyEvent* event){}; virtual void on_key_release(QKeyEvent* event){}; }; void add_keyevent_listener(KeyEventListener& listener); From 92621b643e4c65a50215f8bbac46765e06cc00ca Mon Sep 17 00:00:00 2001 From: jw098 Date: Thu, 16 Apr 2026 22:34:21 -0700 Subject: [PATCH 60/65] add DownloadListener. first step of migration away from Qt signals --- .../ResourceDownload/ResourceDownloadRow.cpp | 47 ++++++++++++++++ .../ResourceDownload/ResourceDownloadRow.h | 31 ++++++++++ .../ResourceDownloadWidget.cpp | 56 ++++++++++++++++++- .../ResourceDownload/ResourceDownloadWidget.h | 22 ++++++-- 4 files changed, 151 insertions(+), 5 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 4b9c5757e5..1205422199 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -7,6 +7,7 @@ #include "CommonFramework/Globals.h" #include "Common/Cpp/Containers/Pimpl.tpp" #include "Common/Cpp/PrettyPrint.h" +#include "Common/Cpp/ListenerSet.h" // #include "Common/Cpp/Exceptions.h" #include "CommonFramework/Tools/GlobalThreadPools.h" #include "CommonFramework/Exceptions/OperationFailedException.h" @@ -231,6 +232,8 @@ struct ResourceDownloadRow::Data{ , m_version_status_label(LockMode::LOCK_WHILE_RUNNING, resource_version_to_string(version_status)) {} + ListenerSet listeners; + LabelCellOption m_resource_name; size_t m_file_size; @@ -516,5 +519,49 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ emit button_state_updated(); } +void ResourceDownloadRow::add_listener(DownloadListener& listener){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.add(listener); +} +void ResourceDownloadRow::remove_listener(DownloadListener& listener){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.remove(listener); +} + +void ResourceDownloadRow::report_download_progress(int percentage){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_download_progress, percentage); +} +void ResourceDownloadRow::report_unzip_progress(int percentage){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_unzip_progress, percentage); +} +void ResourceDownloadRow::report_hash_progress(int percentage){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_hash_progress, percentage); +} + +void ResourceDownloadRow::report_metadata_fetch_finished(std::string popup_message){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_metadata_fetch_finished, popup_message); +} +void ResourceDownloadRow::report_exception_caught(std::string function_name){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_exception_caught, function_name); +} +void ResourceDownloadRow::report_download_failed(){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_download_failed); +} +// void ResourceDownloadRow::report_download_completed(){ +// auto scope = m_lifetime_sanitizer.check_scope(); +// m_data->listeners.run_method(&DownloadListener::on_download_completed); +// } + +void ResourceDownloadRow::report_button_state_updated(){ + auto scope = m_lifetime_sanitizer.check_scope(); + m_data->listeners.run_method(&DownloadListener::on_button_state_updated); +} + } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index c82815ced0..0d49f870aa 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -11,6 +11,7 @@ #include "Common/Cpp/Containers/Pimpl.h" #include "Common/Cpp/Concurrency/AsyncTask.h" #include "Common/Cpp/CancellableScope.h" +#include "Common/Cpp/LifetimeSanitizer.h" // #include "CommonFramework/Tools/GlobalThreadPools.h" #include "Common/Cpp/Options/StaticTableOption.h" #include "ResourceDownloadHelpers.h" @@ -76,6 +77,34 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void button_state_updated(); +public: + struct DownloadListener{ + virtual void on_download_progress(int percentage){}; + virtual void on_unzip_progress(int percentage){}; + virtual void on_hash_progress(int percentage){}; + + virtual void on_metadata_fetch_finished(std::string popup_message){}; + virtual void on_exception_caught(std::string function_name){}; + virtual void on_download_failed(){}; + // virtual void on_download_completed(){}; + + virtual void on_button_state_updated(){}; + }; + + void add_listener(DownloadListener& listener); + void remove_listener(DownloadListener& listener); + + void report_download_progress(int percentage); + void report_unzip_progress(int percentage); + void report_hash_progress(int percentage); + + void report_metadata_fetch_finished(std::string popup_message); + void report_exception_caught(std::string function_name); + void report_download_failed(); + // void report_download_completed(); + + void report_button_state_updated(); + public: void set_version_status(ResourceVersionStatus version_status); @@ -121,6 +150,8 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ std::mutex m_thread_mutex; + LifetimeSanitizer m_lifetime_sanitizer; + diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 2de7639c7a..c08bfa53b9 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -38,6 +38,9 @@ void show_download_failed_box(){ } +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// DownloadButtonWidget +///////////////////////////////////////////////////////////////////////////////////////////////////////// template class RegisterConfigWidget; DownloadButtonWidget::~DownloadButtonWidget(){ @@ -123,6 +126,8 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt show_download_failed_box(); } ); + + value.row.add_listener(*this); } @@ -178,9 +183,24 @@ void DownloadButtonWidget::show_download_confirm_box( } +void DownloadButtonWidget::on_metadata_fetch_finished(std::string popup_message){ +} +void DownloadButtonWidget::on_exception_caught(std::string function_name){ + +} +void DownloadButtonWidget::on_download_failed(){ + +} +void DownloadButtonWidget::on_button_state_updated(){ + +} +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// DeleteButtonWidget +///////////////////////////////////////////////////////////////////////////////////////////////////////// + template class RegisterConfigWidget; DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) : QWidget(&parent) @@ -228,6 +248,7 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va } ); + value.row.add_listener(*this); } @@ -277,6 +298,15 @@ void DeleteButtonWidget::show_delete_confirm_box(){ } } + +void DeleteButtonWidget::on_button_state_updated(){ + +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// CancelButtonWidget +///////////////////////////////////////////////////////////////////////////////////////////////////////// + template class RegisterConfigWidget; CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) : QWidget(&parent) @@ -322,6 +352,8 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va } ); + value.row.add_listener(*this); + } void CancelButtonWidget::update_UI_state(){ @@ -336,6 +368,14 @@ void CancelButtonWidget::update_UI_state(){ } } +void CancelButtonWidget::on_button_state_updated(){ + +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// ProgressBarWidget +///////////////////////////////////////////////////////////////////////////////////////////////////////// + template class RegisterConfigWidget; ProgressBarWidget::~ProgressBarWidget(){ // m_value.row.disconnect(this); @@ -417,7 +457,8 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value update_UI_state(); } ); - + + value.row.add_listener(*this); } @@ -450,5 +491,18 @@ void ProgressBarWidget::update_UI_state(){ } } +void ProgressBarWidget::on_download_progress(int percentage){ + +} +void ProgressBarWidget::on_unzip_progress(int percentage){ + +} +void ProgressBarWidget::on_hash_progress(int percentage){ + +} + +void ProgressBarWidget::on_button_state_updated(){ + +} } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 86f4d757af..0b6b1041b6 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 QWidget, public ConfigWidget{ +class DownloadButtonWidget : public QWidget, public ConfigWidget, public ResourceDownloadRow::DownloadListener{ Q_OBJECT public: using ParentOption = ResourceDownloadButton; @@ -27,6 +27,10 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ ~DownloadButtonWidget(); DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value); + virtual void on_metadata_fetch_finished(std::string popup_message) override; + virtual void on_exception_caught(std::string function_name) override; + virtual void on_download_failed() override; + virtual void on_button_state_updated() override; private: void update_UI_state(); @@ -44,13 +48,15 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget{ void show_error_box(std::string function_name); -class DeleteButtonWidget : public QWidget, public ConfigWidget{ +class DeleteButtonWidget : public QWidget, public ConfigWidget, public ResourceDownloadRow::DownloadListener{ public: using ParentOption = ResourceDeleteButton; public: DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); + virtual void on_button_state_updated() override; + private: void update_UI_state(); void show_delete_confirm_box(); @@ -60,13 +66,15 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget{ QPushButton* m_button; }; -class CancelButtonWidget : public QWidget, public ConfigWidget{ +class CancelButtonWidget : public QWidget, public ConfigWidget, public ResourceDownloadRow::DownloadListener{ public: using ParentOption = ResourceCancelButton; public: CancelButtonWidget(QWidget& parent, ResourceCancelButton& value); + virtual void on_button_state_updated() override; + private: void update_UI_state(); @@ -75,7 +83,7 @@ class CancelButtonWidget : public QWidget, public ConfigWidget{ QPushButton* m_button; }; -class ProgressBarWidget : public QWidget, public ConfigWidget{ +class ProgressBarWidget : public QWidget, public ConfigWidget, public ResourceDownloadRow::DownloadListener{ public: using ParentOption = ResourceProgressBar; @@ -83,6 +91,12 @@ class ProgressBarWidget : public QWidget, public ConfigWidget{ ~ProgressBarWidget(); ProgressBarWidget(QWidget& parent, ResourceProgressBar& value); + virtual void on_download_progress(int percentage) override; + virtual void on_unzip_progress(int percentage) override; + virtual void on_hash_progress(int percentage) override; + + virtual void on_button_state_updated() override; + private: void update_UI_state(); From 772573e5c2d64365da724fd79592668e04212277 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 17 Apr 2026 17:55:16 -0700 Subject: [PATCH 61/65] remove Qt signals from DownloadRow. remove slots from DownloadWidget --- .../ResourceDownload/ResourceDownloadRow.cpp | 28 ++- .../ResourceDownload/ResourceDownloadRow.h | 26 +-- .../ResourceDownloadWidget.cpp | 174 +++++++----------- .../ResourceDownload/ResourceDownloadWidget.h | 13 +- 4 files changed, 90 insertions(+), 151 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 1205422199..53ecd4a471 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -92,9 +92,9 @@ void DownloadThread::start_download_thread(){ // user cancelled action }catch(OperationFailedException&){ - emit m_row.download_failed(); + m_row.report_download_failed(); }catch(...){ - emit m_row.exception_caught("ResourceDownloadButton::start_download"); + m_row.report_exception_caught("ResourceDownloadButton::start_download"); } } @@ -125,7 +125,7 @@ void DownloadThread::run_download(DownloadedResourceMetadata resource_metadata){ zip_path, expected_size, [this](int percentage_progress){ - m_row.download_progress(percentage_progress); + m_row.report_download_progress(percentage_progress); } ); @@ -135,7 +135,7 @@ void DownloadThread::run_download(DownloadedResourceMetadata resource_metadata){ *this, zip_path, [this](int percentage_progress){ - m_row.hash_progress(percentage_progress); + m_row.report_hash_progress(percentage_progress); } ); std::string expected_hash = resource_metadata.sha_256; @@ -154,7 +154,7 @@ void DownloadThread::run_download(DownloadedResourceMetadata resource_metadata){ zip_path.c_str(), resource_directory.c_str(), [this](int percentage_progress){ - m_row.unzip_progress(percentage_progress); + m_row.report_unzip_progress(percentage_progress); } ); @@ -358,17 +358,17 @@ void ResourceDownloadRow::ensure_remote_metadata_loaded(){ predownload_warning = predownload_warning_summary(remote_handle); // update_button_state(ButtonState::READY); - emit metadata_fetch_finished(predownload_warning); + report_metadata_fetch_finished(predownload_warning); }catch(OperationFailedException&){ // cout << "failed" << endl; update_button_state(ButtonState::READY); - emit download_failed(); + report_download_failed(); return; }catch(...){ update_button_state(ButtonState::READY); // cout << "Exception thrown in thread" << endl; - emit exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); + report_exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); return; } @@ -447,7 +447,7 @@ void ResourceDownloadRow::start_delete(){ update_button_state(ButtonState::READY); }catch(...){ update_button_state(ButtonState::READY); - emit exception_caught("ResourceDownloadButton::start_delete"); + report_exception_caught("ResourceDownloadButton::start_delete"); return; } } @@ -516,7 +516,7 @@ void ResourceDownloadRow::update_button_state(ButtonState state){ throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "update_button_state: Unknown enum."); } - emit button_state_updated(); + report_button_state_updated(); } void ResourceDownloadRow::add_listener(DownloadListener& listener){ @@ -541,11 +541,11 @@ void ResourceDownloadRow::report_hash_progress(int percentage){ m_data->listeners.run_method(&DownloadListener::on_hash_progress, percentage); } -void ResourceDownloadRow::report_metadata_fetch_finished(std::string popup_message){ +void ResourceDownloadRow::report_metadata_fetch_finished(const std::string& popup_message){ auto scope = m_lifetime_sanitizer.check_scope(); m_data->listeners.run_method(&DownloadListener::on_metadata_fetch_finished, popup_message); } -void ResourceDownloadRow::report_exception_caught(std::string function_name){ +void ResourceDownloadRow::report_exception_caught(const std::string& function_name){ auto scope = m_lifetime_sanitizer.check_scope(); m_data->listeners.run_method(&DownloadListener::on_exception_caught, function_name); } @@ -553,10 +553,6 @@ void ResourceDownloadRow::report_download_failed(){ auto scope = m_lifetime_sanitizer.check_scope(); m_data->listeners.run_method(&DownloadListener::on_download_failed); } -// void ResourceDownloadRow::report_download_completed(){ -// auto scope = m_lifetime_sanitizer.check_scope(); -// m_data->listeners.run_method(&DownloadListener::on_download_completed); -// } void ResourceDownloadRow::report_button_state_updated(){ auto scope = m_lifetime_sanitizer.check_scope(); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 0d49f870aa..f876952f0c 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -7,7 +7,6 @@ #ifndef PokemonAutomation_ResourceDownloadRow_H #define PokemonAutomation_ResourceDownloadRow_H -#include #include "Common/Cpp/Containers/Pimpl.h" #include "Common/Cpp/Concurrency/AsyncTask.h" #include "Common/Cpp/CancellableScope.h" @@ -54,8 +53,7 @@ enum class ButtonState{ CANCEL, READY, }; -class ResourceDownloadRow : public QObject, public StaticTableRow{ - Q_OBJECT +class ResourceDownloadRow : public StaticTableRow{ public: ~ResourceDownloadRow(); ResourceDownloadRow( @@ -65,28 +63,15 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ ResourceVersionStatus version_status ); -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); - void download_failed(); - void download_completed(); - - void button_state_updated(); - public: struct DownloadListener{ virtual void on_download_progress(int percentage){}; virtual void on_unzip_progress(int percentage){}; virtual void on_hash_progress(int percentage){}; - virtual void on_metadata_fetch_finished(std::string popup_message){}; - virtual void on_exception_caught(std::string function_name){}; + virtual void on_metadata_fetch_finished(const std::string& popup_message){}; + virtual void on_exception_caught(const std::string& function_name){}; virtual void on_download_failed(){}; - // virtual void on_download_completed(){}; virtual void on_button_state_updated(){}; }; @@ -98,10 +83,9 @@ class ResourceDownloadRow : public QObject, public StaticTableRow{ void report_unzip_progress(int percentage); void report_hash_progress(int percentage); - void report_metadata_fetch_finished(std::string popup_message); - void report_exception_caught(std::string function_name); + void report_metadata_fetch_finished(const std::string& popup_message); + void report_exception_caught(const std::string& function_name); void report_download_failed(); - // void report_download_completed(); void report_button_state_updated(); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index c08bfa53b9..bf986b87f9 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -46,11 +46,13 @@ template class RegisterConfigWidget; DownloadButtonWidget::~DownloadButtonWidget(){ // cout << "Destructor for DownloadButtonWidget" << endl; // m_value.disconnect(this); + m_value.row.remove_listener(*this); } DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) : QWidget(&parent) , ConfigWidget(value, *this) , m_value(value) + , m_row(value.row) { QHBoxLayout* layout = new QHBoxLayout(this); @@ -92,40 +94,6 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt } ); - // 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( - &m_value.row, &ResourceDownloadRow::metadata_fetch_finished, - this, [this](std::string predownload_warning){ - show_download_confirm_box("Download", predownload_warning); - } - ); - - // 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, [](std::string function_name){ - show_error_box(function_name); - } - ); - - // if download fails - connect( - &m_value.row, &ResourceDownloadRow::download_failed, - this, [](){ - show_download_failed_box(); - } - ); value.row.add_listener(*this); } @@ -182,18 +150,35 @@ void DownloadButtonWidget::show_download_confirm_box( } } - -void DownloadButtonWidget::on_metadata_fetch_finished(std::string popup_message){ +// 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 +void DownloadButtonWidget::on_metadata_fetch_finished(const std::string& popup_message){ + QMetaObject::invokeMethod(this, [this, popup_message]{ + show_download_confirm_box("Download", popup_message); + }, Qt::QueuedConnection); } -void DownloadButtonWidget::on_exception_caught(std::string function_name){ + +// if the thread catches an exception, show an error box +// since exceptions can't bubble up as usual +// handles all exception_caught() reported by ResourceDownloadRow +void DownloadButtonWidget::on_exception_caught(const std::string& function_name){ + QMetaObject::invokeMethod(this, [function_name]{ + show_error_box(function_name); + }, Qt::QueuedConnection); } + void DownloadButtonWidget::on_download_failed(){ - + QMetaObject::invokeMethod(this, [this]{ + show_download_failed_box(); + }, Qt::QueuedConnection); } + void DownloadButtonWidget::on_button_state_updated(){ - + QMetaObject::invokeMethod(this, [this]{ + update_UI_state(); + }, Qt::QueuedConnection); } @@ -202,10 +187,14 @@ void DownloadButtonWidget::on_button_state_updated(){ ///////////////////////////////////////////////////////////////////////////////////////////////////////// template class RegisterConfigWidget; +DeleteButtonWidget::~DeleteButtonWidget(){ + m_value.row.remove_listener(*this); +} DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) : QWidget(&parent) , ConfigWidget(value, *this) , m_value(value) + , m_row(value.row) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -240,14 +229,6 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va } ); - // when button_state_updated, update the UI state to match - connect( - &m_value.row, &ResourceDownloadRow::button_state_updated, - this, [this](){ - update_UI_state(); - } - ); - value.row.add_listener(*this); } @@ -298,8 +279,11 @@ void DeleteButtonWidget::show_delete_confirm_box(){ } } - +// when button_state_updated, update the UI state to match void DeleteButtonWidget::on_button_state_updated(){ + QMetaObject::invokeMethod(this, [this]{ + update_UI_state(); + }, Qt::QueuedConnection); } @@ -308,10 +292,14 @@ void DeleteButtonWidget::on_button_state_updated(){ ///////////////////////////////////////////////////////////////////////////////////////////////////////// template class RegisterConfigWidget; +CancelButtonWidget::~CancelButtonWidget(){ + m_value.row.remove_listener(*this); +} CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) : QWidget(&parent) , ConfigWidget(value, *this) , m_value(value) + , m_row(value.row) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -344,14 +332,6 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va } ); - // when button_state_updated, update the UI state to match - connect( - &m_value.row, &ResourceDownloadRow::button_state_updated, - this, [this](){ - update_UI_state(); - } - ); - value.row.add_listener(*this); } @@ -368,8 +348,11 @@ void CancelButtonWidget::update_UI_state(){ } } +// when button_state_updated, update the UI state to match void CancelButtonWidget::on_button_state_updated(){ - + QMetaObject::invokeMethod(this, [this]{ + update_UI_state(); + }, Qt::QueuedConnection); } ///////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -380,12 +363,13 @@ template class RegisterConfigWidget; ProgressBarWidget::~ProgressBarWidget(){ // m_value.row.disconnect(this); // cout << "Destructor for ProgressBarWidget" << endl; - + m_value.row.remove_listener(*this); } ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value) : QWidget(&parent) , ConfigWidget(value, *this) , m_value(value) + , m_row(value.row) { // 1. Instantiate the widgets @@ -408,56 +392,6 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value 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 - // std::cout << "\rProgress: [" << std::string(percentage_progress / 5, '#') - // << std::string(20 - (percentage_progress / 5), ' ') << "] " - // << percentage_progress << "%" << endl; - - } - ); - - - connect( - &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); - } - ); - - 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, - this, [this](){ - update_UI_state(); - } - ); - value.row.add_listener(*this); } @@ -491,17 +425,35 @@ void ProgressBarWidget::update_UI_state(){ } } +void ProgressBarWidget::update_progress_bar(int percentage, const std::string& text){ + if (m_progress_bar->isHidden()) { + m_progress_bar->show(); // Make it visible when progress starts + } + m_status_label->setText(QString::fromStdString(text)); + m_progress_bar->setValue(percentage); +} + void ProgressBarWidget::on_download_progress(int percentage){ + QMetaObject::invokeMethod(this, [this, percentage]{ + update_progress_bar(percentage, "Downloading"); + }, Qt::QueuedConnection); } void ProgressBarWidget::on_unzip_progress(int percentage){ - + QMetaObject::invokeMethod(this, [this, percentage]{ + update_progress_bar(percentage, "Unzipping"); + }, Qt::QueuedConnection); } void ProgressBarWidget::on_hash_progress(int percentage){ - + QMetaObject::invokeMethod(this, [this, percentage]{ + update_progress_bar(percentage, "Verifying"); + }, Qt::QueuedConnection); } - +// when button_state_updated, update the UI state to match void ProgressBarWidget::on_button_state_updated(){ + QMetaObject::invokeMethod(this, [this]{ + update_UI_state(); + }, Qt::QueuedConnection); } diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h index 0b6b1041b6..2523e530aa 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.h @@ -27,8 +27,8 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget, public Resourc ~DownloadButtonWidget(); DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value); - virtual void on_metadata_fetch_finished(std::string popup_message) override; - virtual void on_exception_caught(std::string function_name) override; + virtual void on_metadata_fetch_finished(const std::string& popup_message) override; + virtual void on_exception_caught(const std::string& function_name) override; virtual void on_download_failed() override; virtual void on_button_state_updated() override; @@ -41,6 +41,7 @@ class DownloadButtonWidget : public QWidget, public ConfigWidget, public Resourc private: ResourceDownloadButton& m_value; + ResourceDownloadRow& m_row; QPushButton* m_button; }; @@ -53,6 +54,7 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget, public Resource using ParentOption = ResourceDeleteButton; public: + ~DeleteButtonWidget(); DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value); virtual void on_button_state_updated() override; @@ -63,6 +65,7 @@ class DeleteButtonWidget : public QWidget, public ConfigWidget, public Resource private: ResourceDeleteButton& m_value; + ResourceDownloadRow& m_row; QPushButton* m_button; }; @@ -71,6 +74,7 @@ class CancelButtonWidget : public QWidget, public ConfigWidget, public Resource using ParentOption = ResourceCancelButton; public: + ~CancelButtonWidget(); CancelButtonWidget(QWidget& parent, ResourceCancelButton& value); virtual void on_button_state_updated() override; @@ -80,6 +84,7 @@ class CancelButtonWidget : public QWidget, public ConfigWidget, public Resource private: ResourceCancelButton& m_value; + ResourceDownloadRow& m_row; QPushButton* m_button; }; @@ -97,11 +102,13 @@ class ProgressBarWidget : public QWidget, public ConfigWidget, public ResourceDo virtual void on_button_state_updated() override; -private: +private: void update_UI_state(); + void update_progress_bar(int percentage, const std::string& text); private: ResourceProgressBar& m_value; + ResourceDownloadRow& m_row; QLabel* m_status_label; QProgressBar* m_progress_bar; }; From e9d471214c2c1afc0153a2c50f87cee45402ed85 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 17 Apr 2026 19:26:42 -0700 Subject: [PATCH 62/65] fix build --- .../CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index bf986b87f9..559560032b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -170,7 +170,7 @@ void DownloadButtonWidget::on_exception_caught(const std::string& function_name) } void DownloadButtonWidget::on_download_failed(){ - QMetaObject::invokeMethod(this, [this]{ + QMetaObject::invokeMethod(this, []{ show_download_failed_box(); }, Qt::QueuedConnection); } From d2f708a4a5fb2b68db95f52b67f8a3c807fa304f Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 17 Apr 2026 19:32:30 -0700 Subject: [PATCH 63/65] minor refactor --- .../ResourceDownloadWidget.cpp | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp index 559560032b..22d9391e1b 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadWidget.cpp @@ -46,7 +46,7 @@ template class RegisterConfigWidget; DownloadButtonWidget::~DownloadButtonWidget(){ // cout << "Destructor for DownloadButtonWidget" << endl; // m_value.disconnect(this); - m_value.row.remove_listener(*this); + m_row.remove_listener(*this); } DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButton& value) : QWidget(&parent) @@ -89,13 +89,13 @@ DownloadButtonWidget::DownloadButtonWidget(QWidget& parent, ResourceDownloadButt connect( m_button, &QPushButton::clicked, this, [this](){ - m_value.row.update_button_state(ButtonState::DOWNLOAD); - m_value.row.ensure_remote_metadata_loaded(); + m_row.update_button_state(ButtonState::DOWNLOAD); + m_row.ensure_remote_metadata_loaded(); } ); - value.row.add_listener(*this); + m_row.add_listener(*this); } @@ -105,7 +105,7 @@ void DownloadButtonWidget::update_UI_state(){ m_button->setText("Download"); }else{ m_button->setEnabled(false); - if (m_value.row.get_button_state() == ButtonState::DOWNLOAD){ + if (m_row.get_button_state() == ButtonState::DOWNLOAD){ m_button->setText("Downloading..."); } } @@ -141,11 +141,11 @@ void DownloadButtonWidget::show_download_confirm_box( if (clicked == ok){ cout << "Clicked Ok to Download" << endl; - m_value.row.start_download(); + m_row.start_download(); return; } if (clicked == cancel){ - m_value.row.update_button_state(ButtonState::READY); + m_row.update_button_state(ButtonState::READY); return; } } @@ -188,7 +188,7 @@ void DownloadButtonWidget::on_button_state_updated(){ template class RegisterConfigWidget; DeleteButtonWidget::~DeleteButtonWidget(){ - m_value.row.remove_listener(*this); + m_row.remove_listener(*this); } DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& value) : QWidget(&parent) @@ -223,13 +223,13 @@ DeleteButtonWidget::DeleteButtonWidget(QWidget& parent, ResourceDeleteButton& va connect( m_button, &QPushButton::clicked, this, [&](bool){ - m_value.row.update_button_state(ButtonState::DELETE); + m_row.update_button_state(ButtonState::DELETE); show_delete_confirm_box(); cout << "Clicked Delete Button" << endl; } ); - value.row.add_listener(*this); + m_row.add_listener(*this); } @@ -239,7 +239,7 @@ void DeleteButtonWidget::update_UI_state(){ m_button->setText("Delete"); }else{ m_button->setEnabled(false); - if (m_value.row.get_button_state() == ButtonState::DELETE){ + if (m_row.get_button_state() == ButtonState::DELETE){ m_button->setText("Deleting..."); } } @@ -270,11 +270,11 @@ void DeleteButtonWidget::show_delete_confirm_box(){ if (clicked == yes){ cout << "Clicked Yes to Delete" << endl; - m_value.row.start_delete(); + m_row.start_delete(); return; } if (clicked == cancel){ - m_value.row.update_button_state(ButtonState::READY); + m_row.update_button_state(ButtonState::READY); return; } } @@ -293,7 +293,7 @@ void DeleteButtonWidget::on_button_state_updated(){ template class RegisterConfigWidget; CancelButtonWidget::~CancelButtonWidget(){ - m_value.row.remove_listener(*this); + m_row.remove_listener(*this); } CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& value) : QWidget(&parent) @@ -327,12 +327,12 @@ CancelButtonWidget::CancelButtonWidget(QWidget& parent, ResourceCancelButton& va connect( m_button, &QPushButton::clicked, this, [&](bool){ - m_value.row.update_button_state(ButtonState::CANCEL); + m_row.update_button_state(ButtonState::CANCEL); cout << "Clicked Cancel Button" << endl; } ); - value.row.add_listener(*this); + m_row.add_listener(*this); } @@ -342,7 +342,7 @@ void CancelButtonWidget::update_UI_state(){ m_button->setText("Cancel"); }else{ m_button->setEnabled(false); - if (m_value.row.get_button_state() == ButtonState::CANCEL){ + if (m_row.get_button_state() == ButtonState::CANCEL){ m_button->setText("Cancelling..."); } } @@ -361,9 +361,8 @@ void CancelButtonWidget::on_button_state_updated(){ template class RegisterConfigWidget; ProgressBarWidget::~ProgressBarWidget(){ - // m_value.row.disconnect(this); // cout << "Destructor for ProgressBarWidget" << endl; - m_value.row.remove_listener(*this); + m_row.remove_listener(*this); } ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value) : QWidget(&parent) @@ -392,12 +391,12 @@ ProgressBarWidget::ProgressBarWidget(QWidget& parent, ResourceProgressBar& value this->setLayout(layout); this->setMinimumWidth(170); - value.row.add_listener(*this); + m_row.add_listener(*this); } void ProgressBarWidget::update_UI_state(){ - ButtonState state = m_value.row.get_button_state(); + ButtonState state = m_row.get_button_state(); switch (state){ case ButtonState::DOWNLOAD: m_status_label->setText("Downloading"); From 0e0792983096704e630295cd7fd1e8608f379706 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 17 Apr 2026 19:56:09 -0700 Subject: [PATCH 64/65] update table labels --- .../ResourceDownload/ResourceDownloadRow.cpp | 33 ++++++++++++------- .../ResourceDownload/ResourceDownloadRow.h | 3 ++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index 53ecd4a471..dfef1a3d84 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -88,12 +88,20 @@ void DownloadThread::start_download_thread(){ cout << "Done Download" << endl; + m_row.update_table_label(true); + }catch(OperationCancelledException&){ // user cancelled action + m_row.update_table_label(false); + }catch(OperationFailedException&){ + m_row.update_table_label(false); + m_row.report_download_failed(); }catch(...){ + m_row.update_table_label(false); + m_row.report_exception_caught("ResourceDownloadButton::start_download"); } @@ -163,26 +171,20 @@ void DownloadThread::run_download(DownloadedResourceMetadata resource_metadata){ throw_if_cancelled(); - // update the table labels - m_row.set_is_downloaded(true); - m_row.set_version_status(ResourceVersionStatus::CURRENT); - }catch(OperationCancelledException& e){ + }catch(OperationCancelledException&){ // delete directory and the resource fs::remove_all(Filesystem::Path(resource_directory)); - // update the table labels - m_row.set_is_downloaded(false); - m_row.set_version_status(ResourceVersionStatus::NOT_APPLICABLE); + throw; + }catch(OperationFailedException&){ + // delete directory and the resource + fs::remove_all(Filesystem::Path(resource_directory)); - throw e; + throw; }catch(...){ // delete directory and the resource fs::remove_all(Filesystem::Path(resource_directory)); - // update the table labels - m_row.set_is_downloaded(false); - m_row.set_version_status(ResourceVersionStatus::NOT_APPLICABLE); - throw; } @@ -260,6 +262,11 @@ void ResourceDownloadRow::set_is_downloaded(bool is_downloaded){ m_data->m_is_downloaded_label.set_text(is_downloaded_string(is_downloaded)); } +void ResourceDownloadRow::update_table_label(bool success){ + set_is_downloaded(success); + set_version_status(success ? ResourceVersionStatus::CURRENT : ResourceVersionStatus::NOT_APPLICABLE); +} + ResourceDownloadRow::~ResourceDownloadRow(){ @@ -362,10 +369,12 @@ void ResourceDownloadRow::ensure_remote_metadata_loaded(){ }catch(OperationFailedException&){ // cout << "failed" << endl; + // update_table_label(false); update_button_state(ButtonState::READY); report_download_failed(); return; }catch(...){ + // update_table_label(false); update_button_state(ButtonState::READY); // cout << "Exception thrown in thread" << endl; report_exception_caught("ResourceDownloadButton::ensure_remote_metadata_loaded"); diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index f876952f0c..94f7b5b561 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -42,6 +42,8 @@ class DownloadThread : public CancellableScope, public std::enable_shared_from_t // throws OperationCancelledException if the user cancels the action void run_download(DownloadedResourceMetadata resource_metadata); + + private: ResourceDownloadRow& m_row; AsyncTask m_worker; @@ -93,6 +95,7 @@ class ResourceDownloadRow : public StaticTableRow{ public: void set_version_status(ResourceVersionStatus version_status); void set_is_downloaded(bool is_downloaded); + void update_table_label(bool success); void ensure_remote_metadata_loaded(); std::string predownload_warning_summary(RemoteMetadata& remote_metadata); From 8e09153d502fc1d14193ace0b6f229f72a02aa96 Mon Sep 17 00:00:00 2001 From: jw098 Date: Fri, 17 Apr 2026 20:35:54 -0700 Subject: [PATCH 65/65] minor --- .../CommonFramework/ResourceDownload/ResourceDownloadRow.cpp | 5 ++--- .../CommonFramework/ResourceDownload/ResourceDownloadRow.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp index dfef1a3d84..af66c66c61 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.cpp @@ -272,8 +272,7 @@ void ResourceDownloadRow::update_table_label(bool success){ ResourceDownloadRow::~ResourceDownloadRow(){ // cout << "~ResourceDownloadRow" << endl; m_worker1.wait_and_ignore_exceptions(); - // m_worker2.wait_and_ignore_exceptions(); - m_worker3.wait_and_ignore_exceptions(); + m_worker2.wait_and_ignore_exceptions(); } ResourceDownloadRow::ResourceDownloadRow( DownloadedResourceMetadata local_metadata, @@ -440,7 +439,7 @@ void ResourceDownloadRow::start_download(){ void ResourceDownloadRow::start_delete(){ - m_worker3 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( + m_worker2 = GlobalThreadPools::unlimited_normal().dispatch_now_blocking( [this]{ try { std::string resource_name = m_local_metadata.resource_name; diff --git a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h index 94f7b5b561..f2f456b8f1 100644 --- a/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h +++ b/SerialPrograms/Source/CommonFramework/ResourceDownload/ResourceDownloadRow.h @@ -131,7 +131,6 @@ class ResourceDownloadRow : public StaticTableRow{ AsyncTask m_worker1; AsyncTask m_worker2; - AsyncTask m_worker3; std::shared_ptr m_download_thread;