From d5e60e0e719b31c5a0c1732d37bbacf98d5c2952 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sat, 23 May 2020 14:34:20 +0200 Subject: [PATCH] Create command to export images from .DAT file --- distribution/changelog.txt | 1 + src/openrct2/CmdlineSprite.cpp | 165 +++++++++++++++++++++++- src/openrct2/cmdline/SpriteCommands.cpp | 14 +- src/openrct2/object/Object.h | 5 + 4 files changed, 174 insertions(+), 11 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index e54c93a6bd..472f4e156b 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -7,6 +7,7 @@ - Feature: [#11231] Change shortcut window list order to be more intuitive, and split it into logical sections. - Feature: [#11306] Path additions are now kept when replacing the path. - Feature: [#11320] Support for custom JavaScript plugins. +- Feature: [#11788] Command to extract images from a .DAT file. - Change: [#11209] Warn when user is running OpenRCT2 through Wine. - Change: [#11358] Switch copy and paste button positions in tile inspector. - Change: [#11449] Remove complete circuit requirement from Air Powered Vertical Coaster (for RCT1 parity). diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index e6722fd2df..3111339d2b 100644 --- a/src/openrct2/CmdlineSprite.cpp +++ b/src/openrct2/CmdlineSprite.cpp @@ -11,12 +11,18 @@ #include "CmdlineSprite.h" +#include "Context.h" #include "OpenRCT2.h" #include "core/Imaging.h" #include "drawing/Drawing.h" #include "drawing/ImageImporter.h" +#include "object/ObjectLimits.h" +#include "object/ObjectManager.h" +#include "object/ObjectRepository.h" #include "platform/platform.h" #include "util/Util.h" +#include "world/Entrance.h" +#include "world/Scenery.h" #include #include @@ -196,14 +202,12 @@ static void sprite_file_close() SafeFree(spriteFileData); } -static bool sprite_file_export(int32_t spriteIndex, const char* outPath) +static bool sprite_file_export(rct_g1_element* spriteHeader, const char* outPath) { - rct_g1_element* spriteHeader; rct_drawpixelinfo dpi; uint8_t* pixels; int32_t pixelBufferSize; - spriteHeader = &spriteFileEntries[spriteIndex]; pixelBufferSize = spriteHeader->width * spriteHeader->height; std::unique_ptr pixelBuffer(new uint8_t[pixelBufferSize]); pixels = pixelBuffer.get(); @@ -371,7 +375,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - if (!sprite_file_export(spriteIndex, outputPath)) + rct_g1_element* spriteHeader = &spriteFileEntries[spriteIndex]; + if (!sprite_file_export(spriteHeader, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); @@ -431,7 +436,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) printf("\r%d / %d, %d%%", spriteIndex, maxIndex, spriteIndex / maxIndex); } - if (!sprite_file_export(spriteIndex, outputPath)) + rct_g1_element* spriteHeader = &spriteFileEntries[spriteIndex]; + if (!sprite_file_export(spriteHeader, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); @@ -452,6 +458,155 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) sprite_file_close(); return 1; } + else if (_strcmpi(argv[0], "exportalldat") == 0) + { + if (argc < 3) + { + fprintf(stdout, "usage: sprite exportall \n"); + return -1; + } + + char datName[DAT_NAME_LENGTH + 1] = { 0 }; + std::fill_n(datName, DAT_NAME_LENGTH, ' '); + int32_t i = 0; + for (const char* ch = argv[1]; *ch != '\0' && i < DAT_NAME_LENGTH; ch++) + { + datName[i++] = *ch; + } + + auto context = OpenRCT2::CreateContext(); + context->Initialise(); + + const ObjectRepositoryItem* ori = object_repository_find_object_by_name(datName); + if (ori == nullptr) + { + fprintf(stderr, "Could not find the object.\n"); + return -1; + } + + const rct_object_entry* entry = &ori->ObjectEntry; + void* loadedObject = object_manager_load_object(entry); + if (loadedObject == nullptr) + { + fprintf(stderr, "Unable to load object.\n"); + return -1; + } + auto entryIndex = object_manager_get_loaded_object_entry_index(loadedObject); + uint8_t objectType = entry->GetType(); + + auto& objManager = context->GetObjectManager(); + auto metaObject = objManager.GetLoadedObject(objectType, entryIndex); + + char outputPath[MAX_PATH]; + safe_strcpy(outputPath, argv[2], MAX_PATH); + path_end_with_separator(outputPath, MAX_PATH); + + if (!platform_ensure_directory_exists(outputPath)) + { + fprintf(stderr, "Unable to create directory.\n"); + return -1; + } + + int32_t maxIndex = static_cast(metaObject->GetNumImages()); + int32_t imagesOffset = 0; + switch (objectType) + { + case OBJECT_TYPE_RIDE: + { + auto rideEntry = get_ride_entry(entryIndex); + imagesOffset = rideEntry->images_offset; + break; + } + case OBJECT_TYPE_SMALL_SCENERY: + case OBJECT_TYPE_LARGE_SCENERY: + case OBJECT_TYPE_WALLS: + case OBJECT_TYPE_BANNERS: + case OBJECT_TYPE_PATH_BITS: + { + auto obj = objManager.GetLoadedObject(objectType, entryIndex); + if (obj != nullptr) + { + auto sceneryEntry = static_cast(obj->GetLegacyData()); + imagesOffset = sceneryEntry->image; + } + break; + } + case OBJECT_TYPE_PATHS: + { + auto pathEntry = get_path_surface_entry(entryIndex); + imagesOffset = pathEntry->image; + break; + } + case OBJECT_TYPE_SCENERY_GROUP: + { + auto sceneryGroupEntry = get_scenery_group_entry(entryIndex); + imagesOffset = sceneryGroupEntry->image; + break; + } + case OBJECT_TYPE_PARK_ENTRANCE: + { + auto obj = objManager.GetLoadedObject(objectType, entryIndex); + if (obj != nullptr) + { + auto entranceEnty = static_cast(obj->GetLegacyData()); + imagesOffset = entranceEnty->image_id; + } + break; + } + default: + { + fprintf(stderr, "Cannot extract images from this type of object.\n"); + return -1; + } + } + + int32_t numDigits = std::max(1, static_cast(std::floor(std::log(maxIndex)))); + size_t pathLen = strlen(outputPath); + + if (pathLen >= static_cast(MAX_PATH - numDigits - 5)) + { + fprintf(stderr, "Path too long.\n"); + return -1; + } + + for (int32_t x = 0; x < numDigits; x++) + { + outputPath[pathLen + x] = '0'; + } + safe_strcpy(outputPath + pathLen + numDigits, ".png", MAX_PATH - pathLen - numDigits); + + for (int32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) + { + const rct_g1_element* g1 = gfx_get_g1_element(spriteIndex + imagesOffset); + if (g1 == nullptr) + { + fprintf(stderr, "Could not load image metadata\n"); + return -1; + } + + if (!sprite_file_export(const_cast(g1), outputPath)) + { + fprintf(stderr, "Could not export\n"); + sprite_file_close(); + return -1; + } + + fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d },\n", outputPath, g1->x_offset, g1->y_offset); + + // Add to the index at the end of the file name + char* counter = outputPath + pathLen + numDigits - 1; + (*counter)++; + while (*counter > '9') + { + *counter = '0'; + counter--; + (*counter)++; + } + } + + metaObject->Unload(); + return 1; + } else if (_strcmpi(argv[0], "create") == 0) { if (argc < 2) diff --git a/src/openrct2/cmdline/SpriteCommands.cpp b/src/openrct2/cmdline/SpriteCommands.cpp index 35c5c1c46c..e98031f2de 100644 --- a/src/openrct2/cmdline/SpriteCommands.cpp +++ b/src/openrct2/cmdline/SpriteCommands.cpp @@ -32,12 +32,14 @@ static exitcode_t HandleSprite(CommandLineArgEnumerator *argEnumerator); const CommandLineCommand CommandLine::SpriteCommands[] { // Main commands - DefineCommand("append", " [x_offset y_offset]", SpriteOptions, HandleSprite), - DefineCommand("build", " [silent]", SpriteOptions, HandleSprite), - DefineCommand("create", "", SpriteOptions, HandleSprite), - DefineCommand("details", " [idx]", SpriteOptions, HandleSprite), - DefineCommand("export", " ", SpriteOptions, HandleSprite), - DefineCommand("exportall", " ", SpriteOptions, HandleSprite), + DefineCommand("append", " [x_offset y_offset]", SpriteOptions, HandleSprite), + DefineCommand("build", " [silent]", SpriteOptions, HandleSprite), + DefineCommand("create", "", SpriteOptions, HandleSprite), + DefineCommand("details", " [idx]", SpriteOptions, HandleSprite), + DefineCommand("export", " ", SpriteOptions, HandleSprite), + DefineCommand("exportall", " ", SpriteOptions, HandleSprite), + DefineCommand("exportalldat", " ", SpriteOptions, HandleSprite), + CommandTableEnd }; // clang-format on diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index b0f85f9e3f..dc2158750a 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -252,6 +252,11 @@ public: rct_object_entry GetScgWallsHeader(); rct_object_entry GetScgPathXHeader(); rct_object_entry CreateHeader(const char name[9], uint32_t flags, uint32_t checksum); + + uint32_t GetNumImages() const + { + return GetImageTable().GetCount(); + } }; #ifdef __WARN_SUGGEST_FINAL_TYPES__ # pragma GCC diagnostic pop