From d0b8d0f3ea393c0a81ef6bf2fcd4cb4ccdd54265 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Thu, 16 Jan 2025 01:12:53 -0500 Subject: [PATCH] libeuropa/io/yatf: Fix YATF 24-bpp support This completely redoes the garbage flags stuff I did before. OOPS. This seems a lot closer to the format, and also exports everything Starfighter has properly. --- hexpat/yatf.hexpat | 2 + include/europa/structs/Yatf.hpp | 23 +++++------ include/europa/util/ImageSurface.hpp | 34 ++++++++++++++++ include/europa/util/UniqueArray.hpp | 4 +- src/libeuropa/io/yatf/Reader.cpp | 60 ++++++++++++++++++++-------- 5 files changed, 92 insertions(+), 31 deletions(-) diff --git a/hexpat/yatf.hexpat b/hexpat/yatf.hexpat index 1becade..5e054d0 100644 --- a/hexpat/yatf.hexpat +++ b/hexpat/yatf.hexpat @@ -17,6 +17,8 @@ namespace europa { struct TexHeader { char magic[4]; // 'YATF' + // FIXME: this is completely wrong + // Flag descriptions: // // 0x1 - unknown? (always pressent) diff --git a/include/europa/structs/Yatf.hpp b/include/europa/structs/Yatf.hpp index 8300ac4..574a9a1 100644 --- a/include/europa/structs/Yatf.hpp +++ b/include/europa/structs/Yatf.hpp @@ -16,23 +16,22 @@ namespace europa::structs { struct [[gnu::packed]] YatfHeader { - constexpr static u32 TextureFlag_Unknown = 0x1; - - /** - * Texture does not have a palette - */ - constexpr static u32 TextureFlag_NoPalette = 0x30000; - - /** - * Texture uses alpha. - */ - constexpr static u32 TextureFlag_UsesAlpha = 0x1000000; + enum class TextureFormat : u8 { + kTextureFormat8Bpp = 0, + kTextureFormatUnknown = 1, // possibly 16bpp? + kTextureFormat24Bpp = 2, + kTextureFormat32Bpp = 3 + }; constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::big>(); u32 magic; - u32 flags; + u16 unkThing; // always 0x1 + + TextureFormat format; + + u8 unkThing2; // flags? // Always zeroed. u32 zero; diff --git a/include/europa/util/ImageSurface.hpp b/include/europa/util/ImageSurface.hpp index b9ee4e1..7d34ac6 100644 --- a/include/europa/util/ImageSurface.hpp +++ b/include/europa/util/ImageSurface.hpp @@ -31,6 +31,40 @@ namespace europa::util { } }; +// Avoid dependency on header +// while still allowing Pixel to be properly packed +#ifdef _MSC_VER + #pragma pack(push, 1) +#endif + + struct [[gnu::packed]] PixelRGB { + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + }; + + struct [[gnu::packed]] Pixel { + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + std::uint8_t a; + + static constexpr Pixel FromPixelRGB(const PixelRGB& rgb) { + return { + .r = rgb.r, + .g = rgb.g, + .b = rgb.b, + .a = 0xff + }; + } + + // FIXME: Implement Pixel::FromRgb565 method for 16bpp + }; + +#ifdef _MSC_VER + #pragma pack(pop) +#endif + /// A RGBA8888 image surface. struct ImageSurface { ImageSurface(); diff --git a/include/europa/util/UniqueArray.hpp b/include/europa/util/UniqueArray.hpp index 52c043d..885cbf3 100644 --- a/include/europa/util/UniqueArray.hpp +++ b/include/europa/util/UniqueArray.hpp @@ -55,11 +55,11 @@ namespace europa::util { } T& operator[](std::size_t index) { - return (*array)[index]; + return (array.get())[index]; } const T& operator[](std::size_t index) const { - return (*array)[index]; + return (array.get())[index]; } T* Data() { diff --git a/src/libeuropa/io/yatf/Reader.cpp b/src/libeuropa/io/yatf/Reader.cpp index 97dc667..f713f95 100644 --- a/src/libeuropa/io/yatf/Reader.cpp +++ b/src/libeuropa/io/yatf/Reader.cpp @@ -11,8 +11,8 @@ // simpler/faster utilities for image buffers. #include +#include #include -#include #include "../StreamUtils.h" #include "europa/structs/Yatf.hpp" @@ -31,27 +31,53 @@ namespace europa::io::yatf { return false; } - surface.Resize({ static_cast(header.width), static_cast(header.height) }); + auto imageSize = util::Size { static_cast(header.width), static_cast(header.height) }; - if(header.flags & structs::YatfHeader::TextureFlag_NoPalette) { - stream.read(reinterpret_cast(surface.GetBuffer()), (header.width * header.height) * 4); - } else { - /* - pixel::RgbaColor palette[256]; - std::vector tempBuffer((header.width * header.height)); + surface.Resize(imageSize); - // NB: sizeof() does pre-multiplication, so it's 100% ok for us to do this. - stream.read(reinterpret_cast(&palette[0]), sizeof(palette)); - stream.read(reinterpret_cast(&tempBuffer[0]), tempBuffer.size()); + using enum structs::YatfHeader::TextureFormat; + switch(header.format) { + case kTextureFormat8Bpp: { + util::Pixel palette[256] {}; + util::UniqueArray palettizedData(imageSize.Linear()); - auto* buffer = image.GetBuffer(); - const auto* data = &tempBuffer[0]; + stream.read(reinterpret_cast(&palette[0]), sizeof(palette)); + stream.read(reinterpret_cast(&palettizedData[0]), imageSize.Linear()); - for(std::size_t i = 0; i < header.width * header.height; ++i) - *(buffer++) = palette[data[i]]; - */ + auto* pDestBuffer = reinterpret_cast(surface.GetBuffer()); - throw std::runtime_error("FIXME: Port this path properly :("); + for(std::size_t y = 0; y < imageSize.height; ++y) { + for(std::size_t x = 0; x < imageSize.width; ++x) { + auto& pp = palettizedData[y * imageSize.width + x]; + auto& dst = pDestBuffer[y * imageSize.width + x]; + dst = palette[static_cast(pp)]; + } + } + } break; + + case kTextureFormat24Bpp: { + util::UniqueArray rgbPixelData(imageSize.Linear()); + stream.read(reinterpret_cast(&rgbPixelData[0]), imageSize.LinearWithStride()); + auto* pDestBuffer = reinterpret_cast(surface.GetBuffer()); + + for(std::size_t y = 0; y < imageSize.height; ++y) { + for(std::size_t x = 0; x < imageSize.width; ++x) { + auto& pp = rgbPixelData[y * imageSize.width + x]; + auto& dst = pDestBuffer[y * imageSize.width + x]; + dst = util::Pixel::FromPixelRGB(pp); + } + } + } break; + + case kTextureFormat32Bpp: + // We can directly read data + stream.read(reinterpret_cast(surface.GetBuffer()), imageSize.LinearWithStride()); + break; + + case kTextureFormatUnknown: + default: + throw std::runtime_error(std::format("Unknown/unsupported texture format {:02x}!", (std::uint16_t)header.format)); + break; } return true;