diff options
| author | Jeod <47716344+JeodC@users.noreply.github.com> | 2026-09-16 16:32:11 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-16 14:32:11 -0600 |
| commit | 72960ca16f4723f96aaf4037b76072c230af8c11 (patch) | |
| tree | 8f97ab5cac5a7917b1f55e605e6740e3a33774f5 /src/factories/pm64 | |
| parent | 2cfc71d27cb22919a12e47e2d2e691c7ba17ddd7 (diff) | |
PM64: Add Charset factory, de-blob bits of sprites, images and tiles for HD texpack use (#261)HEADmain
Diffstat (limited to 'src/factories/pm64')
| -rw-r--r-- | src/factories/pm64/CharsetFactory.cpp | 133 | ||||
| -rw-r--r-- | src/factories/pm64/CharsetFactory.h | 23 | ||||
| -rw-r--r-- | src/factories/pm64/SpriteFactory.cpp | 136 | ||||
| -rw-r--r-- | src/factories/pm64/SpriteFactory.h | 14 | ||||
| -rw-r--r-- | src/factories/pm64/StoryImageFactory.cpp | 37 | ||||
| -rw-r--r-- | src/factories/pm64/TitleDataFactory.cpp | 63 |
6 files changed, 395 insertions, 11 deletions
diff --git a/src/factories/pm64/CharsetFactory.cpp b/src/factories/pm64/CharsetFactory.cpp new file mode 100644 index 0000000..a076f0e --- /dev/null +++ b/src/factories/pm64/CharsetFactory.cpp @@ -0,0 +1,133 @@ +#include "CharsetFactory.h" +#include "Companion.h" +#include <unordered_map> +#include "utils/Decompressor.h" +#include "utils/TextureUtils.h" +#include "spdlog/spdlog.h" +#include <sstream> + +ExportResult PM64CharsetHeaderExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + const auto symbol = GetSafeNode(node, "symbol", entryName); + + if (Companion::Instance->IsOTRMode()) { + write << "static const ALIGN_ASSET(2) char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n"; + return std::nullopt; + } + + write << "extern u8 " << symbol << "[];\n"; + return std::nullopt; +} + +static void RegisterTexture(const std::string& name, uint32_t type, uint32_t w, uint32_t h, const uint8_t* px, + size_t bytes) { + auto writer = LUS::BinaryWriter(); + BaseExporter::WriteHeader(writer, Torch::ResourceType::Texture, 0); + writer.Write(type); + writer.Write(w); + writer.Write(h); + writer.Write((uint32_t)bytes); + writer.Write((char*)px, bytes); + std::stringstream ss; + writer.Finish(ss); + std::string str = ss.str(); + Companion::Instance->RegisterCompanionFile(name, std::vector<char>(str.begin(), str.end())); +} + +static void RegisterCompanionTexture(const std::string& name, uint32_t type, uint32_t w, uint32_t h, const uint8_t* px, + size_t bytes) { + auto writer = LUS::BinaryWriter(); + BaseExporter::WriteHeader(writer, Torch::ResourceType::Texture, 0); + writer.Write(type); + writer.Write(w); + writer.Write(h); + writer.Write((uint32_t)bytes); + writer.Write((char*)px, bytes); + std::stringstream ss; + writer.Finish(ss); + std::string str = ss.str(); + Companion::Instance->RegisterCompanionFile(name, std::vector<char>(str.begin(), str.end())); +} + +static void RegisterImageCompanions(YAML::Node& node, const std::string& entryName, const std::vector<uint8_t>& data) { + std::string base = entryName; + const auto slash = base.rfind('/'); + if (slash != std::string::npos) { + base = base.substr(slash + 1); + } + if (node["img_format"]) { + static const std::unordered_map<std::string, std::pair<TextureType, double>> kFormats = { + { "RGBA32", { TextureType::RGBA32bpp, 4.0 } }, { "RGBA16", { TextureType::RGBA16bpp, 2.0 } }, + { "CI4", { TextureType::Palette4bpp, 0.5 } }, { "CI8", { TextureType::Palette8bpp, 1.0 } }, + { "I4", { TextureType::Grayscale4bpp, 0.5 } }, { "I8", { TextureType::Grayscale8bpp, 1.0 } }, + { "IA4", { TextureType::GrayscaleAlpha4bpp, 0.5 } }, { "IA8", { TextureType::GrayscaleAlpha8bpp, 1.0 } }, + { "IA16", { TextureType::GrayscaleAlpha16bpp, 2.0 } }, + }; + const auto fmt = GetSafeNode<std::string>(node, "img_format"); + const auto w = GetSafeNode<uint32_t>(node, "img_width"); + const auto h = GetSafeNode<uint32_t>(node, "img_height"); + const auto it = kFormats.find(fmt); + if (it != kFormats.end()) { + const size_t bytes = (size_t)(w * h * it->second.second); + if (bytes <= data.size()) { + RegisterCompanionTexture(base + "_img", (uint32_t)it->second.first, w, h, data.data(), bytes); + } + } + } + if (node["tlut_colors"]) { + const auto colors = GetSafeNode<uint32_t>(node, "tlut_colors"); + std::string img = base; + for (const char* suffix : { "_palette", "_pal" }) { + const std::string s = suffix; + if (img.size() > s.size() && img.compare(img.size() - s.size(), s.size(), s) == 0) { + img = img.substr(0, img.size() - s.size()); + break; + } + } + if ((size_t)colors * 2 <= data.size()) { + RegisterCompanionTexture(img + "_img_tlut", (uint32_t)TextureType::RGBA16bpp, colors, 1, data.data(), + (size_t)colors * 2); + } + } +} + +ExportResult PM64CharsetBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, + std::string& entryName, YAML::Node& node, std::string* replacement) { + auto writer = LUS::BinaryWriter(); + auto data = std::static_pointer_cast<RawBuffer>(raw)->mBuffer; + + WriteHeader(writer, Torch::ResourceType::Blob, 0); + writer.Write((uint32_t)data.size()); + writer.Write((char*)data.data(), data.size()); + writer.Finish(write); + + std::string base = entryName; + auto lastSlash = base.rfind('/'); + if (lastSlash != std::string::npos) { + base = base.substr(lastSlash + 1); + } + + RegisterImageCompanions(node, entryName, data); + if (node["glyph_width"]) { + const auto w = GetSafeNode<uint32_t>(node, "glyph_width"); + const auto h = GetSafeNode<uint32_t>(node, "glyph_height"); + const size_t bytes = (size_t)w * h / 2; + for (size_t i = 0; bytes > 0 && (i + 1) * bytes <= data.size(); i++) { + RegisterTexture(base + "_" + std::to_string(i), (uint32_t)TextureType::Palette4bpp, w, h, + data.data() + i * bytes, bytes); + } + } else if (node["palette_stride"]) { + const auto stride = GetSafeNode<uint32_t>(node, "palette_stride"); + for (size_t i = 0; stride > 0 && i * stride + 32 <= data.size(); i++) { + RegisterTexture(base + "_" + std::to_string(i), (uint32_t)TextureType::RGBA16bpp, 16, 1, + data.data() + i * stride, 32); + } + } + + return std::nullopt; +} + +std::optional<std::shared_ptr<IParsedData>> PM64CharsetFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { + auto [_, segment] = Decompressor::AutoDecode(node, buffer); + return std::make_shared<RawBuffer>(segment.data, segment.size); +} diff --git a/src/factories/pm64/CharsetFactory.h b/src/factories/pm64/CharsetFactory.h new file mode 100644 index 0000000..197aaf0 --- /dev/null +++ b/src/factories/pm64/CharsetFactory.h @@ -0,0 +1,23 @@ +#pragma once + +#include "factories/BaseFactory.h" +#include "types/RawBuffer.h" + +class PM64CharsetBinaryExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + +class PM64CharsetHeaderExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + +class PM64CharsetFactory : public BaseFactory { +public: + std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { + return { + REGISTER(Header, PM64CharsetHeaderExporter) + REGISTER(Binary, PM64CharsetBinaryExporter) + }; + } +}; diff --git a/src/factories/pm64/SpriteFactory.cpp b/src/factories/pm64/SpriteFactory.cpp index 14ac420..dc1dd2a 100644 --- a/src/factories/pm64/SpriteFactory.cpp +++ b/src/factories/pm64/SpriteFactory.cpp @@ -1,7 +1,9 @@ #include "SpriteFactory.h" #include "Companion.h" #include "utils/Decompressor.h" +#include "utils/TextureUtils.h" #include "spdlog/spdlog.h" +#include <sstream> #include <unordered_set> // PM64 sprite structure (decompressed, before byte-swap): @@ -147,6 +149,85 @@ static void ByteSwapSpriteData(uint8_t* data, size_t size) { // so the raw ROM byte order must be preserved. } +static uint32_t ReadBE32(const std::vector<uint8_t>& rom, size_t offset) { + return (rom[offset] << 24) | (rom[offset + 1] << 16) | (rom[offset + 2] << 8) | rom[offset + 3]; +} + +// Walk the (already byte-swapped) blob's raster and palette lists. NPC raster +// images sit inside the blob at imageOffset; player sprites keep their images in +// the shared player raster data instead, located through the load descriptor +// table, so those are read from the ROM using the offsets the yaml provides. +static void CollectSpriteImages(PM64SpriteData& sprite, const std::vector<uint8_t>& rom, YAML::Node& node) { + const auto& data = sprite.mBuffer; + const size_t size = data.size(); + if (size < 16) { + return; + } + const uint32_t rastersOffset = reinterpret_cast<const uint32_t*>(data.data())[0]; + const uint32_t palettesOffset = reinterpret_cast<const uint32_t*>(data.data())[1]; + + std::vector<uint32_t> descriptors; + uint32_t imageDataBase = 0, imageDataRom = 0; + const bool player = node["raster_index"].IsDefined(); + if (player) { + const auto index = GetSafeNode<uint32_t>(node, "raster_index"); + const auto header = GetSafeNode<uint32_t>(node, "raster_header"); + const auto sets = GetSafeNode<uint32_t>(node, "raster_sets"); + const auto descs = GetSafeNode<uint32_t>(node, "raster_descriptors"); + imageDataRom = GetSafeNode<uint32_t>(node, "raster_image_data"); + imageDataBase = ReadBE32(rom, header + 8); + const uint32_t first = ReadBE32(rom, sets + index * 4); + const uint32_t last = ReadBE32(rom, sets + (index + 1) * 4); + for (uint32_t i = first; i < last; i++) { + descriptors.push_back(ReadBE32(rom, descs + i * 4)); + } + } + + if (rastersOffset > 0 && rastersOffset < size) { + const uint32_t* list = reinterpret_cast<const uint32_t*>(data.data() + rastersOffset); + for (size_t i = 0; list[i] != 0xFFFFFFFF && reinterpret_cast<const uint8_t*>(&list[i]) < data.data() + size; + i++) { + const uint32_t entry = list[i]; + if (entry == 0 || entry + 8 > size) { + break; + } + PM64SpriteRaster raster; + const uint32_t imageOffset = *reinterpret_cast<const uint32_t*>(data.data() + entry); + raster.width = data[entry + 4]; + raster.height = data[entry + 5]; + const size_t bytes = (size_t)raster.width * raster.height / 2; + if (player) { + if (i >= descriptors.size()) { + break; + } + // upper three nibbles give size / 16, lower five the offset + const uint32_t romOffset = imageDataRom + ((descriptors[i] & 0xFFFFF) - imageDataBase); + const uint32_t descSize = (descriptors[i] >> 16) & 0xFFF0; + // Placeholder entries (back-facing sprites, 255x255 dummies) point at a + // 16-byte stub; leave those without pixels so nothing is emitted for them. + if (romOffset + bytes <= rom.size() && descSize >= bytes) { + raster.pixels.assign(rom.begin() + romOffset, rom.begin() + romOffset + bytes); + } + } else if (imageOffset + bytes <= size) { + raster.pixels.assign(data.begin() + imageOffset, data.begin() + imageOffset + bytes); + } + sprite.rasters.push_back(std::move(raster)); + } + } + + if (palettesOffset > 0 && palettesOffset < size) { + const uint32_t* list = reinterpret_cast<const uint32_t*>(data.data() + palettesOffset); + for (size_t i = 0; list[i] != 0xFFFFFFFF && reinterpret_cast<const uint8_t*>(&list[i]) < data.data() + size; + i++) { + const uint32_t offset = list[i]; + if (offset + 32 > size) { + break; + } + sprite.palettes.emplace_back(data.begin() + offset, data.begin() + offset + 32); + } + } +} + std::optional<std::shared_ptr<IParsedData>> PM64SpriteFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { // Get the offset from YAML auto offset = GetSafeNode<uint32_t>(node, "offset"); @@ -154,6 +235,7 @@ std::optional<std::shared_ptr<IParsedData>> PM64SpriteFactory::parse(std::vector // Check if this is compressed (YAY0) auto compressionType = Decompressor::GetCompressionType(buffer, offset); + std::vector<uint8_t> spriteData; if (compressionType == CompressionType::YAY0) { // Decompress YAY0 data auto decoded = Decompressor::Decode(buffer, offset, CompressionType::YAY0); @@ -163,36 +245,68 @@ std::optional<std::shared_ptr<IParsedData>> PM64SpriteFactory::parse(std::vector } // Create a copy of decompressed data for byte-swapping - std::vector<uint8_t> spriteData(decoded->data, decoded->data + decoded->size); - - // Byte-swap for little-endian - ByteSwapSpriteData(spriteData.data(), spriteData.size()); + spriteData.assign(decoded->data, decoded->data + decoded->size); SPDLOG_DEBUG("PM64:SPRITE parsed at 0x{:X}, decompressed size: {}", offset, spriteData.size()); - - return std::make_shared<RawBuffer>(spriteData); } else { // Uncompressed - just read raw data with size from YAML auto size = GetSafeNode<size_t>(node, "size"); auto [_, segment] = Decompressor::AutoDecode(node, buffer, size); - std::vector<uint8_t> spriteData(segment.data, segment.data + segment.size); - ByteSwapSpriteData(spriteData.data(), spriteData.size()); - - return std::make_shared<RawBuffer>(spriteData); + spriteData.assign(segment.data, segment.data + segment.size); } + + // Byte-swap for little-endian + ByteSwapSpriteData(spriteData.data(), spriteData.size()); + + auto sprite = std::make_shared<PM64SpriteData>(spriteData); + CollectSpriteImages(*sprite, buffer, node); + return sprite; } ExportResult PM64SpriteBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node& node, std::string* replacement) { auto writer = LUS::BinaryWriter(); - auto data = std::static_pointer_cast<RawBuffer>(raw)->mBuffer; + auto sprite = std::static_pointer_cast<PM64SpriteData>(raw); + auto& data = sprite->mBuffer; // Write as Blob type for now - game will load as raw binary WriteHeader(writer, Torch::ResourceType::Blob, 0); writer.Write(static_cast<uint32_t>(data.size())); writer.Write(reinterpret_cast<char*>(data.data()), data.size()); writer.Finish(write); + std::string base = entryName; + auto lastSlash = base.rfind('/'); + if (lastSlash != std::string::npos) { + base = base.substr(lastSlash + 1); + } + + auto companion = [&](const std::string& name, uint32_t type, uint32_t w, uint32_t h, + const std::vector<uint8_t>& px) { + auto texWriter = LUS::BinaryWriter(); + WriteHeader(texWriter, Torch::ResourceType::Texture, 0); + texWriter.Write(type); + texWriter.Write(w); + texWriter.Write(h); + texWriter.Write((uint32_t)px.size()); + texWriter.Write((char*)px.data(), px.size()); + std::stringstream ss; + texWriter.Finish(ss); + std::string str = ss.str(); + Companion::Instance->RegisterCompanionFile(name, std::vector<char>(str.begin(), str.end())); + }; + + for (size_t i = 0; i < sprite->rasters.size(); i++) { + const auto& r = sprite->rasters[i]; + if (r.pixels.empty()) { + continue; + } + companion(base + "_raster_" + std::to_string(i), (uint32_t)TextureType::Palette4bpp, r.width, r.height, + r.pixels); + } + for (size_t i = 0; i < sprite->palettes.size(); i++) { + companion(base + "_pal_" + std::to_string(i), (uint32_t)TextureType::RGBA16bpp, 16, 1, sprite->palettes[i]); + } return std::nullopt; } diff --git a/src/factories/pm64/SpriteFactory.h b/src/factories/pm64/SpriteFactory.h index 2002cce..41e712e 100644 --- a/src/factories/pm64/SpriteFactory.h +++ b/src/factories/pm64/SpriteFactory.h @@ -3,6 +3,20 @@ #include "factories/BaseFactory.h" #include "types/RawBuffer.h" +// One sprite raster split out of the blob so it can be addressed by name. +struct PM64SpriteRaster { + uint8_t width; + uint8_t height; + std::vector<uint8_t> pixels; // CI4 +}; + +struct PM64SpriteData : public RawBuffer { + std::vector<PM64SpriteRaster> rasters; + std::vector<std::vector<uint8_t>> palettes; // 16 x RGBA16, 32 bytes each + + explicit PM64SpriteData(std::vector<uint8_t>& buffer) : RawBuffer(buffer) {} +}; + class PM64SpriteBinaryExporter : public BaseExporter { ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; diff --git a/src/factories/pm64/StoryImageFactory.cpp b/src/factories/pm64/StoryImageFactory.cpp index 79c6700..163c6b9 100644 --- a/src/factories/pm64/StoryImageFactory.cpp +++ b/src/factories/pm64/StoryImageFactory.cpp @@ -1,6 +1,8 @@ #include "StoryImageFactory.h" #include "Companion.h" #include "spdlog/spdlog.h" +#include "utils/TextureUtils.h" +#include <sstream> // PM64 story image factory for intro sequence graphics // Handles CI8 images with palettes and IA8 images without palettes @@ -43,6 +45,21 @@ std::optional<std::shared_ptr<IParsedData>> PM64StoryImageFactory::parse(std::ve return std::make_shared<RawBuffer>(result); } +static void RegisterTexture(const std::string& name, uint32_t type, uint32_t w, uint32_t h, const uint8_t* px, + size_t bytes) { + auto writer = LUS::BinaryWriter(); + BaseExporter::WriteHeader(writer, Torch::ResourceType::Texture, 0); + writer.Write(type); + writer.Write(w); + writer.Write(h); + writer.Write((uint32_t)bytes); + writer.Write((char*)px, bytes); + std::stringstream ss; + writer.Finish(ss); + std::string str = ss.str(); + Companion::Instance->RegisterCompanionFile(name, std::vector<char>(str.begin(), str.end())); +} + ExportResult PM64StoryImageBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node& node, std::string* replacement) { auto writer = LUS::BinaryWriter(); @@ -54,6 +71,26 @@ ExportResult PM64StoryImageBinaryExporter::Export(std::ostream& write, std::shar writer.Write(reinterpret_cast<char*>(data.data()), data.size()); writer.Finish(write); + // The same image and palette as textures the game can address by name + // ("<page>_img", "<page>_tlut"), so replacements can be keyed to them. + const auto width = GetSafeNode<uint32_t>(node, "width"); + const auto height = GetSafeNode<uint32_t>(node, "height"); + const auto hasPalette = GetSafeNode<bool>(node, "has_palette"); + std::string base = entryName; + const auto slash = base.rfind('/'); + if (slash != std::string::npos) { + base = base.substr(slash + 1); + } + const size_t imageSize = (size_t)width * height; + if (data.size() >= imageSize) { + RegisterTexture(base + "_img", + (uint32_t)(hasPalette ? TextureType::Palette8bpp : TextureType::GrayscaleAlpha8bpp), width, + height, data.data(), imageSize); + } + if (hasPalette && data.size() >= imageSize + 512) { + RegisterTexture(base + "_tlut", (uint32_t)TextureType::RGBA16bpp, 256, 1, data.data() + imageSize, 512); + } + return std::nullopt; } diff --git a/src/factories/pm64/TitleDataFactory.cpp b/src/factories/pm64/TitleDataFactory.cpp index b0ad1f9..8e2b09f 100644 --- a/src/factories/pm64/TitleDataFactory.cpp +++ b/src/factories/pm64/TitleDataFactory.cpp @@ -1,5 +1,8 @@ #include "TitleDataFactory.h" #include "Companion.h" +#include <unordered_map> +#include <sstream> +#include "utils/TextureUtils.h" #include "utils/Decompressor.h" #include "spdlog/spdlog.h" @@ -36,6 +39,65 @@ std::optional<std::shared_ptr<IParsedData>> PM64TitleDataFactory::parse(std::vec return std::make_shared<RawBuffer>(result); } +// Writes a Texture resource next to the resource being exported, so image data that +// the game keeps as a blob can also be addressed by name (a replacement keys on it). +static void RegisterCompanionTexture(const std::string& name, uint32_t type, uint32_t w, uint32_t h, const uint8_t* px, + size_t bytes) { + auto writer = LUS::BinaryWriter(); + BaseExporter::WriteHeader(writer, Torch::ResourceType::Texture, 0); + writer.Write(type); + writer.Write(w); + writer.Write(h); + writer.Write((uint32_t)bytes); + writer.Write((char*)px, bytes); + std::stringstream ss; + writer.Finish(ss); + std::string str = ss.str(); + Companion::Instance->RegisterCompanionFile(name, std::vector<char>(str.begin(), str.end())); +} + +static void RegisterImageCompanions(YAML::Node& node, const std::string& entryName, const std::vector<uint8_t>& data) { + std::string base = entryName; + const auto slash = base.rfind('/'); + if (slash != std::string::npos) { + base = base.substr(slash + 1); + } + if (node["img_format"]) { + static const std::unordered_map<std::string, std::pair<TextureType, double>> kFormats = { + { "RGBA32", { TextureType::RGBA32bpp, 4.0 } }, { "RGBA16", { TextureType::RGBA16bpp, 2.0 } }, + { "CI4", { TextureType::Palette4bpp, 0.5 } }, { "CI8", { TextureType::Palette8bpp, 1.0 } }, + { "I4", { TextureType::Grayscale4bpp, 0.5 } }, { "I8", { TextureType::Grayscale8bpp, 1.0 } }, + { "IA4", { TextureType::GrayscaleAlpha4bpp, 0.5 } }, { "IA8", { TextureType::GrayscaleAlpha8bpp, 1.0 } }, + { "IA16", { TextureType::GrayscaleAlpha16bpp, 2.0 } }, + }; + const auto fmt = GetSafeNode<std::string>(node, "img_format"); + const auto w = GetSafeNode<uint32_t>(node, "img_width"); + const auto h = GetSafeNode<uint32_t>(node, "img_height"); + const auto it = kFormats.find(fmt); + if (it != kFormats.end()) { + const size_t bytes = (size_t)(w * h * it->second.second); + if (bytes <= data.size()) { + RegisterCompanionTexture(base + "_img", (uint32_t)it->second.first, w, h, data.data(), bytes); + } + } + } + if (node["tlut_colors"]) { + const auto colors = GetSafeNode<uint32_t>(node, "tlut_colors"); + std::string img = base; + for (const char* suffix : { "_palette", "_pal" }) { + const std::string s = suffix; + if (img.size() > s.size() && img.compare(img.size() - s.size(), s.size(), s) == 0) { + img = img.substr(0, img.size() - s.size()); + break; + } + } + if ((size_t)colors * 2 <= data.size()) { + RegisterCompanionTexture(img + "_img_tlut", (uint32_t)TextureType::RGBA16bpp, colors, 1, data.data(), + (size_t)colors * 2); + } + } +} + ExportResult PM64TitleDataBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node& node, std::string* replacement) { auto writer = LUS::BinaryWriter(); @@ -46,6 +108,7 @@ ExportResult PM64TitleDataBinaryExporter::Export(std::ostream& write, std::share writer.Write(reinterpret_cast<char*>(data.data()), data.size()); writer.Finish(write); + RegisterImageCompanions(node, entryName, data); return std::nullopt; } |
