2015-11-14 17:36:32 +01:00
|
|
|
/*****************************************************************************
|
2020-07-21 15:04:34 +02:00
|
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
2015-11-14 17:36:32 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2015-11-14 17:36:32 +01:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2015-11-14 17:36:32 +01:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2017-01-16 23:46:45 +01:00
|
|
|
#pragma warning(disable : 4706) // assignment within conditional expression
|
|
|
|
|
2017-12-12 14:52:57 +01:00
|
|
|
#include "CmdlineSprite.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
|
2020-05-23 14:34:20 +02:00
|
|
|
#include "Context.h"
|
2018-06-22 23:25:16 +02:00
|
|
|
#include "OpenRCT2.h"
|
2018-05-07 19:45:15 +02:00
|
|
|
#include "core/Imaging.h"
|
2020-08-13 00:14:00 +02:00
|
|
|
#include "core/Json.hpp"
|
2018-05-09 20:33:08 +02:00
|
|
|
#include "drawing/Drawing.h"
|
|
|
|
#include "drawing/ImageImporter.h"
|
2020-05-23 14:34:20 +02:00
|
|
|
#include "object/ObjectLimits.h"
|
|
|
|
#include "object/ObjectManager.h"
|
|
|
|
#include "object/ObjectRepository.h"
|
2017-06-25 19:49:04 +02:00
|
|
|
#include "platform/platform.h"
|
2017-12-13 13:02:24 +01:00
|
|
|
#include "util/Util.h"
|
2020-05-23 14:34:20 +02:00
|
|
|
#include "world/Entrance.h"
|
|
|
|
#include "world/Scenery.h"
|
2015-05-19 01:26:31 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
#include <cmath>
|
|
|
|
#include <cstring>
|
|
|
|
|
2020-05-23 13:02:08 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
# include "core/String.hpp"
|
|
|
|
#endif
|
|
|
|
|
2018-05-09 20:33:08 +02:00
|
|
|
using namespace OpenRCT2::Drawing;
|
2015-05-31 20:35:40 +02:00
|
|
|
|
2016-06-02 23:22:08 +02:00
|
|
|
#pragma pack(push, 1)
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
struct rct_sprite_file_header
|
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
uint32_t num_entries;
|
|
|
|
uint32_t total_size;
|
2018-02-14 09:42:26 +01:00
|
|
|
};
|
2015-05-19 01:26:31 +02:00
|
|
|
|
2016-06-03 10:54:44 +02:00
|
|
|
assert_struct_size(rct_sprite_file_header, 8);
|
2016-06-02 20:40:29 +02:00
|
|
|
|
2016-06-02 23:22:08 +02:00
|
|
|
#pragma pack(pop)
|
2015-05-19 01:26:31 +02:00
|
|
|
|
2017-10-09 17:13:14 +02:00
|
|
|
static rct_sprite_file_header spriteFileHeader;
|
2018-06-22 23:25:16 +02:00
|
|
|
static rct_g1_element* spriteFileEntries;
|
|
|
|
static uint8_t* spriteFileData;
|
2015-05-19 01:26:31 +02:00
|
|
|
|
2017-04-01 20:57:30 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
static FILE* fopen_utf8(const char* path, const char* mode)
|
2017-04-01 20:57:30 +02:00
|
|
|
{
|
2019-07-23 00:11:12 +02:00
|
|
|
auto pathW = String::ToWideChar(path);
|
|
|
|
auto modeW = String::ToWideChar(mode);
|
2019-07-22 23:13:08 +02:00
|
|
|
auto file = _wfopen(pathW.c_str(), modeW.c_str());
|
2017-04-01 20:57:30 +02:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2018-07-21 16:17:06 +02:00
|
|
|
# define fopen fopen_utf8
|
2017-04-01 20:57:30 +02:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2016-07-13 00:46:51 +02:00
|
|
|
static void sprite_entries_make_absolute()
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
for (uint32_t i = 0; i < spriteFileHeader.num_entries; i++)
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileEntries[i].offset += reinterpret_cast<uintptr_t>(spriteFileData);
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2016-07-13 00:46:51 +02:00
|
|
|
static void sprite_entries_make_relative()
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2018-06-20 17:28:51 +02:00
|
|
|
for (uint32_t i = 0; i < spriteFileHeader.num_entries; i++)
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileEntries[i].offset -= reinterpret_cast<uintptr_t>(spriteFileData);
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
static bool sprite_file_open(const utf8* path)
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
FILE* file = fopen(path, "rb");
|
2018-01-04 06:58:44 +01:00
|
|
|
if (file == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (fread(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (spriteFileHeader.num_entries > 0)
|
|
|
|
{
|
2020-07-17 06:23:05 +02:00
|
|
|
std::unique_ptr<rct_g1_element_32bit[]> openElements = std::make_unique<rct_g1_element_32bit[]>(
|
|
|
|
spriteFileHeader.num_entries);
|
2018-06-22 23:25:16 +02:00
|
|
|
if (openElements == nullptr)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-17 06:23:05 +02:00
|
|
|
if (fread(openElements.get(), spriteFileHeader.num_entries * sizeof(rct_g1_element_32bit), 1, file) != 1)
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileData = static_cast<uint8_t*>(malloc(spriteFileHeader.total_size));
|
2018-06-22 23:25:16 +02:00
|
|
|
if (fread(spriteFileData, spriteFileHeader.total_size, 1, file) != 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
free(spriteFileData);
|
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t entryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element);
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileEntries = static_cast<rct_g1_element*>(malloc(entryTableSize));
|
2018-06-22 23:25:16 +02:00
|
|
|
for (uint32_t i = 0; i < spriteFileHeader.num_entries; i++)
|
|
|
|
{
|
|
|
|
rct_g1_element_32bit* inElement = &openElements[i];
|
|
|
|
rct_g1_element* outElement = &spriteFileEntries[i];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
outElement->offset = reinterpret_cast<uint8_t*>(
|
|
|
|
(static_cast<uintptr_t>(inElement->offset) + reinterpret_cast<uintptr_t>(spriteFileData)));
|
2017-06-06 23:24:18 +02:00
|
|
|
outElement->width = inElement->width;
|
|
|
|
outElement->height = inElement->height;
|
|
|
|
outElement->x_offset = inElement->x_offset;
|
|
|
|
outElement->y_offset = inElement->y_offset;
|
|
|
|
outElement->flags = inElement->flags;
|
|
|
|
outElement->zoomed_offset = inElement->zoomed_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
return true;
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
static bool sprite_file_save(const char* path)
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
FILE* file = fopen(path, "wb");
|
2018-01-04 06:58:44 +01:00
|
|
|
if (file == nullptr)
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (fwrite(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (spriteFileHeader.num_entries > 0)
|
|
|
|
{
|
2020-07-17 06:23:05 +02:00
|
|
|
std::unique_ptr<rct_g1_element_32bit[]> saveElements = std::make_unique<rct_g1_element_32bit[]>(
|
|
|
|
spriteFileHeader.num_entries);
|
2018-06-22 23:25:16 +02:00
|
|
|
if (saveElements == nullptr)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
for (uint32_t i = 0; i < spriteFileHeader.num_entries; i++)
|
|
|
|
{
|
|
|
|
rct_g1_element* inElement = &spriteFileEntries[i];
|
|
|
|
rct_g1_element_32bit* outElement = &saveElements[i];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
outElement->offset = static_cast<uint32_t>(
|
|
|
|
(reinterpret_cast<uintptr_t>(inElement->offset) - reinterpret_cast<uintptr_t>(spriteFileData)));
|
2017-06-06 23:24:18 +02:00
|
|
|
outElement->width = inElement->width;
|
|
|
|
outElement->height = inElement->height;
|
|
|
|
outElement->x_offset = inElement->x_offset;
|
|
|
|
outElement->y_offset = inElement->y_offset;
|
|
|
|
outElement->flags = inElement->flags;
|
|
|
|
outElement->zoomed_offset = inElement->zoomed_offset;
|
|
|
|
}
|
|
|
|
|
2020-07-17 06:23:05 +02:00
|
|
|
if (fwrite(saveElements.get(), spriteFileHeader.num_entries * sizeof(rct_g1_element_32bit), 1, file) != 1)
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (fwrite(spriteFileData, spriteFileHeader.total_size, 1, file) != 1)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
return true;
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2016-07-13 00:46:51 +02:00
|
|
|
static void sprite_file_close()
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
SafeFree(spriteFileEntries);
|
|
|
|
SafeFree(spriteFileData);
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 14:34:20 +02:00
|
|
|
static bool sprite_file_export(rct_g1_element* spriteHeader, const char* outPath)
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
rct_drawpixelinfo dpi;
|
2018-06-22 23:25:16 +02:00
|
|
|
uint8_t* pixels;
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t pixelBufferSize;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
pixelBufferSize = spriteHeader->width * spriteHeader->height;
|
2020-02-16 07:25:30 +01:00
|
|
|
std::unique_ptr<uint8_t[]> pixelBuffer(new uint8_t[pixelBufferSize]);
|
|
|
|
pixels = pixelBuffer.get();
|
2018-12-15 22:23:31 +01:00
|
|
|
std::fill_n(pixels, pixelBufferSize, 0x00);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
dpi.bits = pixels;
|
|
|
|
dpi.x = 0;
|
|
|
|
dpi.y = 0;
|
|
|
|
dpi.width = spriteHeader->width;
|
|
|
|
dpi.height = spriteHeader->height;
|
|
|
|
dpi.pitch = 0;
|
|
|
|
dpi.zoom_level = 0;
|
|
|
|
|
2020-05-31 15:46:23 +02:00
|
|
|
DrawSpriteArgs args(
|
|
|
|
&dpi, ImageId(), PaletteMap::GetDefault(), *spriteHeader, 0, 0, spriteHeader->width, spriteHeader->height, pixels);
|
|
|
|
gfx_sprite_to_buffer(args);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-08 22:41:41 +02:00
|
|
|
auto const pixels8 = dpi.bits;
|
|
|
|
auto const pixelsLen = (dpi.width + dpi.pitch) * dpi.height;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Image image;
|
|
|
|
image.Width = dpi.width;
|
|
|
|
image.Height = dpi.height;
|
|
|
|
image.Depth = 8;
|
|
|
|
image.Stride = dpi.width + dpi.pitch;
|
2020-05-27 21:12:01 +02:00
|
|
|
image.Palette = std::make_unique<GamePalette>(StandardPalette);
|
2018-06-20 17:28:51 +02:00
|
|
|
image.Pixels = std::vector<uint8_t>(pixels8, pixels8 + pixelsLen);
|
2018-05-08 22:41:41 +02:00
|
|
|
Imaging::WriteToFile(outPath, image, IMAGE_FORMAT::PNG);
|
2017-06-06 23:24:18 +02:00
|
|
|
return true;
|
2018-05-08 22:41:41 +02:00
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to write png: %s", e.what());
|
2017-06-06 23:24:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
static std::optional<ImageImporter::ImportResult> sprite_file_import(
|
|
|
|
const char* path, int16_t x_offset, int16_t y_offset, bool keep_palette, bool forceBmp, int32_t mode)
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2018-05-08 22:41:41 +02:00
|
|
|
try
|
|
|
|
{
|
2018-05-09 21:42:58 +02:00
|
|
|
auto format = IMAGE_FORMAT::PNG_32;
|
2018-07-12 17:56:49 +02:00
|
|
|
auto flags = ImageImporter::IMPORT_FLAGS::NONE;
|
|
|
|
|
|
|
|
if (!forceBmp)
|
|
|
|
{
|
2020-05-23 13:02:08 +02:00
|
|
|
flags = ImageImporter::IMPORT_FLAGS::RLE;
|
2018-07-12 17:56:49 +02:00
|
|
|
}
|
|
|
|
|
2018-05-09 21:42:58 +02:00
|
|
|
if (keep_palette)
|
|
|
|
{
|
|
|
|
format = IMAGE_FORMAT::PNG;
|
2020-05-23 13:02:08 +02:00
|
|
|
flags = static_cast<ImageImporter::IMPORT_FLAGS>(flags | ImageImporter::IMPORT_FLAGS::KEEP_PALETTE);
|
2018-05-09 21:42:58 +02:00
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-05-09 20:33:08 +02:00
|
|
|
ImageImporter importer;
|
2018-05-09 21:42:58 +02:00
|
|
|
auto image = Imaging::ReadFromFile(path, format);
|
2017-11-03 22:46:40 +01:00
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
return importer.Import(image, x_offset, y_offset, flags, static_cast<ImageImporter::IMPORT_MODE>(mode));
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2018-05-09 20:33:08 +02:00
|
|
|
catch (const std::exception& e)
|
2017-10-03 23:50:32 +02:00
|
|
|
{
|
2018-05-09 20:33:08 +02:00
|
|
|
fprintf(stderr, "%s\n", e.what());
|
2020-07-02 19:04:49 +02:00
|
|
|
return std::nullopt;
|
2017-06-06 23:24:18 +02:00
|
|
|
}
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
int32_t cmdline_for_sprite(const char** argv, int32_t argc)
|
2015-05-19 01:26:31 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
gOpenRCT2Headless = true;
|
|
|
|
if (argc == 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (_strcmpi(argv[0], "details") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 2)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stdout, "usage: sprite details <spritefile> [idx]\n");
|
|
|
|
return -1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else if (argc == 2)
|
|
|
|
{
|
|
|
|
const char* spriteFilePath = argv[1];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to open input sprite file.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("sprites: %u\n", spriteFileHeader.num_entries);
|
|
|
|
printf("data size: %u\n", spriteFileHeader.total_size);
|
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char* spriteFilePath = argv[1];
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t spriteIndex = atoi(argv[2]);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to open input sprite file.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
if (spriteIndex < 0 || spriteIndex >= static_cast<int32_t>(spriteFileHeader.num_entries))
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
sprite_file_close();
|
|
|
|
fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
rct_g1_element* g1 = &spriteFileEntries[spriteIndex];
|
2017-06-06 23:24:18 +02:00
|
|
|
printf("width: %d\n", g1->width);
|
|
|
|
printf("height: %d\n", g1->height);
|
|
|
|
printf("x offset: %d\n", g1->x_offset);
|
|
|
|
printf("y offset: %d\n", g1->y_offset);
|
|
|
|
printf("data offset: %p\n", g1->offset);
|
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
return 1;
|
|
|
|
}
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else if (_strcmpi(argv[0], "export") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 4)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stdout, "usage: sprite export <spritefile> <idx> <output>\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* spriteFilePath = argv[1];
|
2018-06-20 17:28:51 +02:00
|
|
|
int32_t spriteIndex = atoi(argv[2]);
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* outputPath = argv[3];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to open input sprite file.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
if (spriteIndex < 0 || spriteIndex >= static_cast<int32_t>(spriteFileHeader.num_entries))
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-23 14:34:20 +02:00
|
|
|
rct_g1_element* spriteHeader = &spriteFileEntries[spriteIndex];
|
|
|
|
if (!sprite_file_export(spriteHeader, outputPath))
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Could not export\n");
|
|
|
|
sprite_file_close();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else if (_strcmpi(argv[0], "exportall") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 3)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stdout, "usage: sprite exportall <spritefile> <output directory>\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* spriteFilePath = argv[1];
|
2017-06-06 23:24:18 +02:00
|
|
|
char outputPath[MAX_PATH];
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to open input sprite file.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
safe_strcpy(outputPath, argv[2], MAX_PATH);
|
|
|
|
path_end_with_separator(outputPath, MAX_PATH);
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!platform_ensure_directory_exists(outputPath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to create directory.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
int32_t maxIndex = static_cast<int32_t>(spriteFileHeader.num_entries);
|
|
|
|
int32_t numbers = static_cast<int32_t>(std::floor(std::log(maxIndex)));
|
2017-06-06 23:24:18 +02:00
|
|
|
size_t pathLen = strlen(outputPath);
|
|
|
|
|
2020-04-17 21:36:25 +02:00
|
|
|
if (pathLen >= static_cast<size_t>(MAX_PATH - numbers - 5))
|
2018-06-22 23:25:16 +02:00
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Path too long.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
for (int32_t x = 0; x < numbers; x++)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
outputPath[pathLen + x] = '0';
|
|
|
|
}
|
|
|
|
safe_strcpy(outputPath + pathLen + numbers, ".png", MAX_PATH - pathLen - numbers);
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
for (int32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++)
|
|
|
|
{
|
2017-10-03 20:19:20 +02:00
|
|
|
if (spriteIndex % 100 == 99)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
// Status indicator
|
|
|
|
printf("\r%d / %d, %d%%", spriteIndex, maxIndex, spriteIndex / maxIndex);
|
|
|
|
}
|
|
|
|
|
2020-05-23 14:34:20 +02:00
|
|
|
rct_g1_element* spriteHeader = &spriteFileEntries[spriteIndex];
|
|
|
|
if (!sprite_file_export(spriteHeader, outputPath))
|
2017-10-03 20:19:20 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Could not export\n");
|
|
|
|
sprite_file_close();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-06-06 23:24:18 +02:00
|
|
|
// Add to the index at the end of the file name
|
2018-06-22 23:25:16 +02:00
|
|
|
char* counter = outputPath + pathLen + numbers - 1;
|
2017-06-06 23:24:18 +02:00
|
|
|
(*counter)++;
|
2017-10-03 20:19:20 +02:00
|
|
|
while (*counter > '9')
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
*counter = '0';
|
|
|
|
counter--;
|
|
|
|
(*counter)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
2020-05-23 14:34:20 +02:00
|
|
|
else if (_strcmpi(argv[0], "exportalldat") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 3)
|
|
|
|
{
|
2020-05-23 18:32:41 +02:00
|
|
|
fprintf(stdout, "usage: sprite exportalldat <DAT identifier> <output directory>\n");
|
2020-05-23 14:34:20 +02:00
|
|
|
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);
|
2020-11-09 11:53:29 +01:00
|
|
|
ObjectType objectType = entry->GetType();
|
2020-05-23 14:34:20 +02:00
|
|
|
|
|
|
|
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<int32_t>(metaObject->GetNumImages());
|
|
|
|
int32_t imagesOffset = 0;
|
|
|
|
switch (objectType)
|
|
|
|
{
|
2020-11-09 10:36:37 +01:00
|
|
|
case ObjectType::Ride:
|
2020-05-23 14:34:20 +02:00
|
|
|
{
|
|
|
|
auto rideEntry = get_ride_entry(entryIndex);
|
|
|
|
imagesOffset = rideEntry->images_offset;
|
|
|
|
break;
|
|
|
|
}
|
2020-11-09 10:36:37 +01:00
|
|
|
case ObjectType::SmallScenery:
|
|
|
|
case ObjectType::LargeScenery:
|
|
|
|
case ObjectType::Walls:
|
|
|
|
case ObjectType::Banners:
|
|
|
|
case ObjectType::PathBits:
|
2020-05-23 14:34:20 +02:00
|
|
|
{
|
|
|
|
auto obj = objManager.GetLoadedObject(objectType, entryIndex);
|
|
|
|
if (obj != nullptr)
|
|
|
|
{
|
|
|
|
auto sceneryEntry = static_cast<rct_scenery_entry*>(obj->GetLegacyData());
|
|
|
|
imagesOffset = sceneryEntry->image;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-11-09 10:36:37 +01:00
|
|
|
case ObjectType::Paths:
|
2020-05-23 14:34:20 +02:00
|
|
|
{
|
|
|
|
auto pathEntry = get_path_surface_entry(entryIndex);
|
|
|
|
imagesOffset = pathEntry->image;
|
|
|
|
break;
|
|
|
|
}
|
2020-11-09 10:36:37 +01:00
|
|
|
case ObjectType::SceneryGroup:
|
2020-05-23 14:34:20 +02:00
|
|
|
{
|
|
|
|
auto sceneryGroupEntry = get_scenery_group_entry(entryIndex);
|
|
|
|
imagesOffset = sceneryGroupEntry->image;
|
|
|
|
break;
|
|
|
|
}
|
2020-11-09 10:36:37 +01:00
|
|
|
case ObjectType::ParkEntrance:
|
2020-05-23 14:34:20 +02:00
|
|
|
{
|
|
|
|
auto obj = objManager.GetLoadedObject(objectType, entryIndex);
|
|
|
|
if (obj != nullptr)
|
|
|
|
{
|
|
|
|
auto entranceEnty = static_cast<rct_entrance_type*>(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<int32_t>(std::floor(std::log(maxIndex))));
|
|
|
|
size_t pathLen = strlen(outputPath);
|
|
|
|
|
|
|
|
if (pathLen >= static_cast<size_t>(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<rct_g1_element*>(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;
|
|
|
|
}
|
2018-06-22 23:25:16 +02:00
|
|
|
else if (_strcmpi(argv[0], "create") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 2)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "usage: sprite create <spritefile>\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* spriteFilePath = argv[1];
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
spriteFileHeader.num_entries = 0;
|
|
|
|
spriteFileHeader.total_size = 0;
|
|
|
|
sprite_file_save(spriteFilePath);
|
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else if (_strcmpi(argv[0], "append") == 0)
|
|
|
|
{
|
|
|
|
if (argc != 3 && argc != 5)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "usage: sprite append <spritefile> <input> [<x offset> <y offset>]\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* spriteFilePath = argv[1];
|
|
|
|
const char* imagePath = argv[2];
|
2018-06-20 17:28:51 +02:00
|
|
|
int16_t x_offset = 0;
|
|
|
|
int16_t y_offset = 0;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (argc == 5)
|
|
|
|
{
|
2018-06-22 23:25:16 +02:00
|
|
|
char* endptr;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
x_offset = strtol(argv[3], &endptr, 0);
|
|
|
|
if (*endptr != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "X offset must be an integer\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
y_offset = strtol(argv[4], &endptr, 0);
|
|
|
|
if (*endptr != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Y offset must be an integer\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
auto importResult = sprite_file_import(imagePath, x_offset, y_offset, false, false, gSpriteMode);
|
|
|
|
if (importResult == std::nullopt)
|
2017-06-06 23:24:18 +02:00
|
|
|
return -1;
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stderr, "Unable to open input sprite file.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spriteFileHeader.num_entries++;
|
2020-07-02 19:04:49 +02:00
|
|
|
spriteFileHeader.total_size += static_cast<uint32_t>(importResult->Buffer.size());
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileEntries = static_cast<rct_g1_element*>(
|
|
|
|
realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
sprite_entries_make_relative();
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileData = static_cast<uint8_t*>(realloc(spriteFileData, spriteFileHeader.total_size));
|
2017-06-06 23:24:18 +02:00
|
|
|
sprite_entries_make_absolute();
|
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
spriteFileEntries[spriteFileHeader.num_entries - 1] = importResult->Element;
|
|
|
|
|
|
|
|
const auto& buffer = importResult->Buffer;
|
|
|
|
std::memcpy(spriteFileData + (spriteFileHeader.total_size - buffer.size()), buffer.data(), buffer.size());
|
2018-07-21 11:50:45 +02:00
|
|
|
spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData
|
2020-07-02 19:04:49 +02:00
|
|
|
+ (spriteFileHeader.total_size - buffer.size());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (!sprite_file_save(spriteFilePath))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else if (_strcmpi(argv[0], "build") == 0)
|
|
|
|
{
|
|
|
|
if (argc < 3)
|
|
|
|
{
|
2017-06-06 23:24:18 +02:00
|
|
|
fprintf(stdout, "usage: sprite build <spritefile> <sprite description file> [silent]\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-22 23:25:16 +02:00
|
|
|
const char* spriteFilePath = argv[1];
|
|
|
|
const char* spriteDescriptionPath = argv[2];
|
2017-06-06 23:24:18 +02:00
|
|
|
char* directoryPath = path_get_directory(spriteDescriptionPath);
|
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
json_t jsonSprites = Json::ReadFromFile(spriteDescriptionPath);
|
|
|
|
if (jsonSprites.is_null())
|
2018-09-18 13:21:24 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to read sprite description file: %s\n", spriteDescriptionPath);
|
|
|
|
return -1;
|
|
|
|
}
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
if (!jsonSprites.is_array())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Error: expected array\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool silent = (argc >= 4 && strcmp(argv[3], "silent") == 0);
|
|
|
|
|
|
|
|
spriteFileHeader.num_entries = 0;
|
|
|
|
spriteFileHeader.total_size = 0;
|
|
|
|
sprite_file_save(spriteFilePath);
|
|
|
|
|
|
|
|
fprintf(stdout, "Building: %s\n", spriteFilePath);
|
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
json_t sprite_description;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
// Note: jsonSprite is deliberately left non-const: json_t behaviour changes when const
|
|
|
|
for (auto& [jsonKey, jsonSprite] : jsonSprites.items())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2020-08-13 00:14:00 +02:00
|
|
|
if (!jsonSprite.is_object())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2020-08-13 00:14:00 +02:00
|
|
|
fprintf(stderr, "Error: expected object for sprite %s\n", jsonKey.c_str());
|
2017-06-06 23:24:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
json_t path = jsonSprite["path"];
|
|
|
|
if (!path.is_string())
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2020-08-13 00:14:00 +02:00
|
|
|
fprintf(stderr, "Error: no path provided for sprite %s\n", jsonKey.c_str());
|
2017-06-06 23:24:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2020-08-13 00:14:00 +02:00
|
|
|
std::string strPath = Json::GetString(path);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
json_t x_offset = jsonSprite["x_offset"];
|
|
|
|
json_t y_offset = jsonSprite["y_offset"];
|
|
|
|
|
|
|
|
bool keep_palette = Json::GetString(jsonSprite["palette"]) == "keep";
|
|
|
|
bool forceBmp = !jsonSprite["palette"].is_null() && Json::GetBoolean(jsonSprite["forceBmp"]);
|
2018-07-12 17:56:49 +02:00
|
|
|
|
2020-08-13 00:14:00 +02:00
|
|
|
auto imagePath = platform_get_absolute_path(strPath.c_str(), directoryPath);
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
auto importResult = sprite_file_import(
|
2020-08-13 00:14:00 +02:00
|
|
|
imagePath.c_str(), Json::GetNumber<int16_t>(x_offset), Json::GetNumber<int16_t>(y_offset), keep_palette,
|
|
|
|
forceBmp, gSpriteMode);
|
2020-07-02 19:04:49 +02:00
|
|
|
if (importResult == std::nullopt)
|
2017-06-06 23:24:18 +02:00
|
|
|
{
|
2019-07-22 23:13:08 +02:00
|
|
|
fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath.c_str());
|
2017-06-06 23:24:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sprite_file_open(spriteFilePath))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spriteFileHeader.num_entries++;
|
2020-07-02 19:04:49 +02:00
|
|
|
spriteFileHeader.total_size += static_cast<uint32_t>(importResult->Buffer.size());
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileEntries = static_cast<rct_g1_element*>(
|
|
|
|
realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)));
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
sprite_entries_make_relative();
|
2020-04-17 21:36:25 +02:00
|
|
|
spriteFileData = static_cast<uint8_t*>(realloc(spriteFileData, spriteFileHeader.total_size));
|
2017-06-06 23:24:18 +02:00
|
|
|
sprite_entries_make_absolute();
|
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
spriteFileEntries[spriteFileHeader.num_entries - 1] = importResult->Element;
|
2017-06-06 23:24:18 +02:00
|
|
|
|
2020-07-02 19:04:49 +02:00
|
|
|
const auto& buffer = importResult->Buffer;
|
|
|
|
std::memcpy(spriteFileData + (spriteFileHeader.total_size - buffer.size()), buffer.data(), buffer.size());
|
|
|
|
spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData
|
|
|
|
+ (spriteFileHeader.total_size - buffer.size());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
if (!sprite_file_save(spriteFilePath))
|
|
|
|
{
|
2019-07-22 23:13:08 +02:00
|
|
|
fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath.c_str());
|
2017-06-06 23:24:18 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!silent)
|
2019-07-22 23:13:08 +02:00
|
|
|
fprintf(stdout, "Added: %s\n", imagePath.c_str());
|
2017-06-06 23:24:18 +02:00
|
|
|
|
|
|
|
sprite_file_close();
|
|
|
|
}
|
|
|
|
|
|
|
|
free(directoryPath);
|
|
|
|
|
|
|
|
fprintf(stdout, "Finished\n");
|
|
|
|
return 1;
|
2018-06-22 23:25:16 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-21 17:45:26 +02:00
|
|
|
fprintf(stderr, "Unknown sprite command.\n");
|
2017-06-06 23:24:18 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2015-05-19 01:26:31 +02:00
|
|
|
}
|