mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-16 03:23:15 +01:00
Merge pull request #25225 from Gymnasiast/feature/sprite-exportobject
Create `sprite exportobject` command
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
0.4.27 (in development)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#25218] `sprite exportobject` command, which allows extracting images from an object.
|
||||
- Improved: [#2296, #2307] The land tool now takes sloped track and paths into account when modifying land.
|
||||
- Change: [#25161] Revert to the ‘fair ride price’ calculation of vanilla RCT2.
|
||||
- Change: [#25228] [Plugin] The available staff costumes array is now ordered alphabetically by name.
|
||||
- Removed: [#25225] `sprite exportalldat`, replaced with `sprite exportobject`.
|
||||
- Fix: [#24513] Ride/track designs can now be shifted underground as well.
|
||||
- Fix: [#24682] The scenery window isn't enough to accommodate all tool buttons when there are multiple rows of groups/tabs.
|
||||
- Fix: [#24882] Guests are shown with hats and umbrellas whilst clapping.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/String.hpp"
|
||||
#include "drawing/Drawing.h"
|
||||
#include "drawing/ImageImporter.h"
|
||||
#include "object/ObjectFactory.h"
|
||||
#include "object/ObjectLimits.h"
|
||||
#include "object/ObjectManager.h"
|
||||
#include "object/ObjectRepository.h"
|
||||
@@ -35,6 +36,7 @@ namespace OpenRCT2
|
||||
{
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
static int32_t CommandLineForSpriteExportObject(const char** argv, int32_t argc);
|
||||
static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc);
|
||||
|
||||
class SpriteFile
|
||||
@@ -390,66 +392,9 @@ namespace OpenRCT2
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (String::iequals(argv[0], "exportalldat"))
|
||||
if (String::iequals(argv[0], "exportobject"))
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stdout, "usage: sprite exportalldat <DAT identifier> <output directory>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* datName = argv[1];
|
||||
const utf8* outputPath = argv[2];
|
||||
auto context = CreateContext();
|
||||
context->Initialise();
|
||||
|
||||
const ObjectRepositoryItem* ori = ObjectRepositoryFindObjectByName(datName);
|
||||
if (ori == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Could not find the object.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const RCTObjectEntry* entry = &ori->ObjectEntry;
|
||||
const auto* loadedObject = ObjectManagerLoadObject(entry);
|
||||
if (loadedObject == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Unable to load object.\n");
|
||||
return -1;
|
||||
}
|
||||
auto entryIndex = ObjectManagerGetLoadedObjectEntryIndex(loadedObject);
|
||||
ObjectType objectType = entry->GetType();
|
||||
|
||||
auto& objManager = context->GetObjectManager();
|
||||
const auto* const metaObject = objManager.GetLoadedObject(objectType, entryIndex);
|
||||
|
||||
if (!Path::CreateDirectory(outputPath))
|
||||
{
|
||||
fprintf(stderr, "Unable to create directory.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const uint32_t maxIndex = metaObject->GetNumImages();
|
||||
const int32_t numbers = static_cast<int32_t>(std::floor(std::log10(maxIndex) + 1));
|
||||
|
||||
std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used
|
||||
for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++)
|
||||
{
|
||||
oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png";
|
||||
auto path = Path::Combine(outputPath, PopStr(oss));
|
||||
|
||||
const auto& g1 = metaObject->GetImageTable().GetImages()[spriteIndex];
|
||||
if (!SpriteImageExport(g1, path))
|
||||
{
|
||||
fprintf(stderr, "Could not export\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
path = fs::u8path(path).generic_u8string();
|
||||
fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d }", path.c_str(), g1.x_offset, g1.y_offset);
|
||||
fprintf(stdout, (spriteIndex + 1 != maxIndex) ? ",\n" : "\n");
|
||||
}
|
||||
return 1;
|
||||
return CommandLineForSpriteExportObject(argv, argc);
|
||||
}
|
||||
|
||||
if (String::iequals(argv[0], "create"))
|
||||
@@ -626,6 +571,65 @@ namespace OpenRCT2
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int32_t CommandLineForSpriteExportObject(const char** argv, int32_t argc)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stdout, "usage: sprite exportobject <path to object> <output directory>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* objectPath = argv[1];
|
||||
const utf8* outputPath = argv[2];
|
||||
auto context = CreateContext();
|
||||
context->Initialise();
|
||||
|
||||
auto& objectRepository = GetContext()->GetObjectRepository();
|
||||
std::unique_ptr<Object> metaObject = OpenRCT2::ObjectFactory::CreateObjectFromFile(objectRepository, objectPath, true);
|
||||
if (metaObject == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Could not load the object.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!Path::CreateDirectory(outputPath))
|
||||
{
|
||||
fprintf(stderr, "Unable to create output directory.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto* imageTableStart = metaObject->GetImageTable().GetImages();
|
||||
const uint32_t maxIndex = metaObject->GetNumImages();
|
||||
const int32_t numbers = static_cast<int32_t>(std::floor(std::log10(maxIndex) + 1));
|
||||
|
||||
std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used
|
||||
for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++)
|
||||
{
|
||||
oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png";
|
||||
auto path = Path::Combine(outputPath, PopStr(oss));
|
||||
|
||||
const auto& g1 = imageTableStart[spriteIndex];
|
||||
if (g1.width == 0 || g1.height == 0)
|
||||
{
|
||||
fprintf(stdout, "\"\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SpriteImageExport(g1, path))
|
||||
{
|
||||
fprintf(stderr, "Could not export\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
path = fs::u8path(path).generic_u8string();
|
||||
fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d }", path.c_str(), g1.x_offset, g1.y_offset);
|
||||
}
|
||||
|
||||
fprintf(stdout, (spriteIndex + 1 != maxIndex) ? ",\n" : "\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int32_t CommandLineForSpriteCombine(const char** argv, int32_t argc)
|
||||
{
|
||||
if (argc < 4)
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRCT2
|
||||
DefineCommand("details", "<spritefile> [idx]", kSpriteOptions, HandleSprite),
|
||||
DefineCommand("export", "<spritefile> <idx> <output>", kSpriteOptions, HandleSprite),
|
||||
DefineCommand("exportall", "<spritefile> <output directory>", kSpriteOptions, HandleSprite),
|
||||
DefineCommand("exportalldat", "<DAT identifier> <output directory>", kSpriteOptions, HandleSprite),
|
||||
DefineCommand("exportobject", "<DAT identifier> <output directory>", kSpriteOptions, HandleSprite),
|
||||
|
||||
kCommandTableEnd
|
||||
};
|
||||
|
||||
@@ -208,10 +208,6 @@ namespace OpenRCT2
|
||||
{
|
||||
return _stringTable;
|
||||
}
|
||||
ImageTable& GetImageTable()
|
||||
{
|
||||
return _imageTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the image and string tables from a JSON object
|
||||
@@ -344,6 +340,11 @@ namespace OpenRCT2
|
||||
|
||||
uint32_t LoadImages();
|
||||
void UnloadImages();
|
||||
|
||||
ImageTable& GetImageTable()
|
||||
{
|
||||
return _imageTable;
|
||||
}
|
||||
};
|
||||
#ifdef __WARN_SUGGEST_FINAL_TYPES__
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -264,6 +264,28 @@ namespace OpenRCT2::ObjectFactory
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Object> CreateObjectFromFile(IObjectRepository& objectRepository, u8string_view path, bool loadImages)
|
||||
{
|
||||
std::unique_ptr<Object> object;
|
||||
auto extension = Path::GetExtension(path);
|
||||
if (String::iequals(extension, ".json"))
|
||||
{
|
||||
auto pathStr = u8string(path);
|
||||
object = ObjectFactory::CreateObjectFromJsonFile(objectRepository, pathStr, loadImages);
|
||||
}
|
||||
else if (String::iequals(extension, ".parkobj"))
|
||||
{
|
||||
object = ObjectFactory::CreateObjectFromZipFile(objectRepository, path, loadImages);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pathStr = u8string(path);
|
||||
object = ObjectFactory::CreateObjectFromLegacyFile(objectRepository, pathStr.c_str(), loadImages);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
std::unique_ptr<Object> CreateObjectFromLegacyFile(IObjectRepository& objectRepository, const utf8* path, bool loadImages)
|
||||
{
|
||||
LOG_VERBOSE("CreateObjectFromLegacyFile(..., \"%s\")", path);
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::ObjectFactory
|
||||
{
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObjectFromFile(
|
||||
IObjectRepository& objectRepository, u8string_view path, bool loadImages);
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObjectFromLegacyFile(
|
||||
IObjectRepository& objectRepository, const utf8* path, bool loadImages);
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObjectFromLegacyData(
|
||||
|
||||
@@ -98,20 +98,7 @@ namespace OpenRCT2
|
||||
public:
|
||||
std::optional<ObjectRepositoryItem> Create([[maybe_unused]] int32_t language, const std::string& path) const override
|
||||
{
|
||||
std::unique_ptr<Object> object;
|
||||
auto extension = Path::GetExtension(path);
|
||||
if (String::iequals(extension, ".json"))
|
||||
{
|
||||
object = ObjectFactory::CreateObjectFromJsonFile(_objectRepository, path, false);
|
||||
}
|
||||
else if (String::iequals(extension, ".parkobj"))
|
||||
{
|
||||
object = ObjectFactory::CreateObjectFromZipFile(_objectRepository, path, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
object = ObjectFactory::CreateObjectFromLegacyFile(_objectRepository, path.c_str(), false);
|
||||
}
|
||||
std::unique_ptr<Object> object = ObjectFactory::CreateObjectFromFile(_objectRepository, path, false);
|
||||
|
||||
// All official DAT files have a JSON object counterpart. Avoid loading the obsolete .DAT versions,
|
||||
// which can happen if the user copies the official DAT objects to their custom content folder.
|
||||
@@ -271,18 +258,7 @@ namespace OpenRCT2
|
||||
std::unique_ptr<Object> LoadObject(const ObjectRepositoryItem* ori) override
|
||||
{
|
||||
Guard::ArgumentNotNull(ori, GUARD_LINE);
|
||||
|
||||
auto extension = Path::GetExtension(ori->Path);
|
||||
if (String::iequals(extension, ".json"))
|
||||
{
|
||||
return ObjectFactory::CreateObjectFromJsonFile(*this, ori->Path, !gOpenRCT2NoGraphics);
|
||||
}
|
||||
if (String::iequals(extension, ".parkobj"))
|
||||
{
|
||||
return ObjectFactory::CreateObjectFromZipFile(*this, ori->Path, !gOpenRCT2NoGraphics);
|
||||
}
|
||||
|
||||
return ObjectFactory::CreateObjectFromLegacyFile(*this, ori->Path.c_str(), !gOpenRCT2NoGraphics);
|
||||
return ObjectFactory::CreateObjectFromFile(*this, ori->Path, !gOpenRCT2NoGraphics);
|
||||
}
|
||||
|
||||
void RegisterLoadedObject(const ObjectRepositoryItem* ori, std::unique_ptr<Object>&& object) override
|
||||
|
||||
Reference in New Issue
Block a user