diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index a5d741af45..c28b17aad7 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3746,6 +3746,7 @@ STR_6295 :MiB STR_6296 :GiB STR_6297 :TiB STR_6298 :{STRING}/sec +STR_6299 :Download all ############# # Scenarios # diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index bca8314ca4..b5275dc941 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -9,9 +9,14 @@ #include #include +#include +#include +#include #include +#include #include #include +#include #include #include #include @@ -26,7 +31,8 @@ enum WINDOW_OBJECT_LOAD_ERROR_WIDGET_IDX { WIDX_COLUMN_OBJECT_TYPE, WIDX_SCROLL, WIDX_COPY_CURRENT, - WIDX_COPY_ALL + WIDX_COPY_ALL, + WIDX_DOWNLOAD_ALL }; #define WW 450 @@ -45,8 +51,9 @@ static rct_widget window_object_load_error_widgets[] = { { WWT_TABLE_HEADER, 0, SOURCE_COL_LEFT, TYPE_COL_LEFT - 1, 57, 70, STR_OBJECT_SOURCE, STR_NONE }, // 'Object source' header { WWT_TABLE_HEADER, 0, TYPE_COL_LEFT, WW_LESS_PADDING - 1, 57, 70, STR_OBJECT_TYPE, STR_NONE }, // 'Object type' header { WWT_SCROLL, 0, 4, WW_LESS_PADDING, 70, WH - 33, SCROLL_VERTICAL, STR_NONE }, // Scrollable list area - { WWT_BUTTON, 0, 45, 220, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn - { WWT_BUTTON, 0, 230, 400, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WWT_BUTTON, 0, 4, 148, WH - 23, WH - 10, STR_COPY_SELECTED, STR_NONE }, // Copy selected btn + { WWT_BUTTON, 0, 152, 296, WH - 23, WH - 10, STR_COPY_ALL, STR_NONE }, // Copy all btn + { WWT_BUTTON, 0, 300, WW_LESS_PADDING, WH - 23, WH - 10, STR_DOWNLOAD_ALL, STR_NONE }, // Download all { WIDGETS_END }, }; @@ -59,6 +66,7 @@ static void window_object_load_error_scrollmouseover(rct_window *w, int32_t scro static void window_object_load_error_scrollmousedown(rct_window *w, int32_t scrollIndex, int32_t x, int32_t y); static void window_object_load_error_paint(rct_window *w, rct_drawpixelinfo *dpi); static void window_object_load_error_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int32_t scrollIndex); +static void window_object_load_error_download_all(rct_window* w); static rct_window_event_list window_object_load_error_events = { window_object_load_error_close, @@ -191,7 +199,8 @@ rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, window = window_create_centred(WW, WH, &window_object_load_error_events, WC_OBJECT_LOAD_ERROR, 0); window->widgets = window_object_load_error_widgets; - window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COPY_CURRENT) | (1 << WIDX_COPY_ALL); + window->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_COPY_CURRENT) | (1 << WIDX_COPY_ALL) + | (1 << WIDX_DOWNLOAD_ALL); window_init_scroll_widgets(window); window->colours[0] = COLOUR_LIGHT_BLUE; @@ -245,6 +254,9 @@ static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widg case WIDX_COPY_ALL: copy_object_names_to_clipboard(w); break; + case WIDX_DOWNLOAD_ALL: + window_object_load_error_download_all(w); + break; } } @@ -335,3 +347,71 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf gfx_draw_string_left(dpi, type, nullptr, COLOUR_DARK_GREEN, TYPE_COL_LEFT - 3, y); } } + +static void DownloadObject(IObjectRepository& objRepo, const std::string name, const std::string_view url) +{ + using namespace OpenRCT2::Network; + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = url; + auto response = Http::Do(req); + if (response.status == Http::Status::OK) + { + auto data = (uint8_t*)response.body.data(); + auto dataLen = response.body.size(); + objRepo.AddObjectFromFile(name, data, dataLen); + } + else + { + throw std::runtime_error("Non 200 status"); + } + } + catch (const std::exception&) + { + std::printf(" Failed to download %s\n", name.c_str()); + } +} + +static void window_object_load_error_download_all(rct_window* w) +{ + using namespace OpenRCT2::Network; + + auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); + for (const auto& entry : _invalid_entries) + { + auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + + std::printf("Downloading %s...\n", name.c_str()); + try + { + Http::Request req; + req.method = Http::Method::GET; + req.url = "http://localhost:5000/objects/legacy/" + name; + auto response = Http::Do(req); + if (response.status == Http::Status::OK) + { + auto jresponse = Json::FromString(response.body); + if (jresponse != nullptr) + { + auto objName = json_string_value(json_object_get(jresponse, "name")); + auto downloadLink = json_string_value(json_object_get(jresponse, "download")); + if (downloadLink != nullptr) + { + DownloadObject(objRepo, objName, downloadLink); + } + json_decref(jresponse); + } + } + else + { + std::printf(" %s not found\n", name.c_str()); + } + } + catch (const std::exception&) + { + std::printf(" Failed to query %s\n", name.c_str()); + } + } +} diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 573f5a0861..8154ceb540 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3927,6 +3927,8 @@ enum STR_NETWORK_SPEED_SEC = 6298, + STR_DOWNLOAD_ALL = 6299, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/network/Http.cpp b/src/openrct2/network/Http.cpp index a3cea59fda..6e4b74057d 100644 --- a/src/openrct2/network/Http.cpp +++ b/src/openrct2/network/Http.cpp @@ -160,7 +160,10 @@ namespace OpenRCT2::Network::Http curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type); res.status = static_cast(code); - res.content_type = std::string(content_type); + if (content_type != nullptr) + { + res.content_type = std::string(content_type); + } return res; } diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 68a92d409f..c1bd1d0af5 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -339,6 +339,23 @@ public: } } + void AddObjectFromFile(const std::string_view& objectName, const void* data, size_t dataSize) override + { + utf8 path[MAX_PATH]; + GetPathForNewObject(path, sizeof(path), std::string(objectName).c_str()); + + log_verbose("Adding object: [%s]", objectName); + try + { + File::WriteAllBytes(path, data, dataSize); + ScanObject(path); + } + catch (const std::exception&) + { + Console::Error::WriteLine("Failed saving object: [%s] to '%s'.", objectName, path); + } + } + void ExportPackedObject(IStream* stream) override { auto chunkReader = SawyerChunkReader(stream); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index f358995568..f73f2007bd 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -75,6 +75,7 @@ interface IObjectRepository virtual void UnregisterLoadedObject(const ObjectRepositoryItem* ori, Object* object) abstract; virtual void AddObject(const rct_object_entry* objectEntry, const void* data, size_t dataSize) abstract; + virtual void AddObjectFromFile(const std::string_view& objectName, const void* data, size_t dataSize) abstract; virtual void ExportPackedObject(IStream * stream) abstract; virtual void WritePackedObjects(IStream * stream, std::vector & objects) abstract;