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.
This commit is contained in:
Lily Tsuru 2025-01-16 01:12:53 -05:00
parent 9c6448a817
commit d0b8d0f3ea
5 changed files with 92 additions and 31 deletions

View file

@ -17,6 +17,8 @@ namespace europa {
struct TexHeader { struct TexHeader {
char magic[4]; // 'YATF' char magic[4]; // 'YATF'
// FIXME: this is completely wrong
// Flag descriptions: // Flag descriptions:
// //
// 0x1 - unknown? (always pressent) // 0x1 - unknown? (always pressent)

View file

@ -16,23 +16,22 @@
namespace europa::structs { namespace europa::structs {
struct [[gnu::packed]] YatfHeader { struct [[gnu::packed]] YatfHeader {
constexpr static u32 TextureFlag_Unknown = 0x1; enum class TextureFormat : u8 {
kTextureFormat8Bpp = 0,
/** kTextureFormatUnknown = 1, // possibly 16bpp?
* Texture does not have a palette kTextureFormat24Bpp = 2,
*/ kTextureFormat32Bpp = 3
constexpr static u32 TextureFlag_NoPalette = 0x30000; };
/**
* Texture uses alpha.
*/
constexpr static u32 TextureFlag_UsesAlpha = 0x1000000;
constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::big>(); constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::big>();
u32 magic; u32 magic;
u32 flags; u16 unkThing; // always 0x1
TextureFormat format;
u8 unkThing2; // flags?
// Always zeroed. // Always zeroed.
u32 zero; u32 zero;

View file

@ -31,6 +31,40 @@ namespace europa::util {
} }
}; };
// Avoid dependency on <pshpack*> 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. /// A RGBA8888 image surface.
struct ImageSurface { struct ImageSurface {
ImageSurface(); ImageSurface();

View file

@ -55,11 +55,11 @@ namespace europa::util {
} }
T& operator[](std::size_t index) { T& operator[](std::size_t index) {
return (*array)[index]; return (array.get())[index];
} }
const T& operator[](std::size_t index) const { const T& operator[](std::size_t index) const {
return (*array)[index]; return (array.get())[index];
} }
T* Data() { T* Data() {

View file

@ -11,8 +11,8 @@
// simpler/faster utilities for image buffers. // simpler/faster utilities for image buffers.
#include <europa/io/yatf/Reader.hpp> #include <europa/io/yatf/Reader.hpp>
#include <format>
#include <stdexcept> #include <stdexcept>
#include <vector>
#include "../StreamUtils.h" #include "../StreamUtils.h"
#include "europa/structs/Yatf.hpp" #include "europa/structs/Yatf.hpp"
@ -31,27 +31,53 @@ namespace europa::io::yatf {
return false; return false;
} }
surface.Resize({ static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) }); auto imageSize = util::Size { static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) };
if(header.flags & structs::YatfHeader::TextureFlag_NoPalette) { surface.Resize(imageSize);
stream.read(reinterpret_cast<char*>(surface.GetBuffer()), (header.width * header.height) * 4);
} else { using enum structs::YatfHeader::TextureFormat;
/* switch(header.format) {
pixel::RgbaColor palette[256]; case kTextureFormat8Bpp: {
std::vector<std::uint8_t> tempBuffer((header.width * header.height)); util::Pixel palette[256] {};
util::UniqueArray<std::uint8_t> palettizedData(imageSize.Linear());
// NB: sizeof() does pre-multiplication, so it's 100% ok for us to do this.
stream.read(reinterpret_cast<char*>(&palette[0]), sizeof(palette)); stream.read(reinterpret_cast<char*>(&palette[0]), sizeof(palette));
stream.read(reinterpret_cast<char*>(&tempBuffer[0]), tempBuffer.size()); stream.read(reinterpret_cast<char*>(&palettizedData[0]), imageSize.Linear());
auto* buffer = image.GetBuffer(); auto* pDestBuffer = reinterpret_cast<util::Pixel*>(surface.GetBuffer());
const auto* data = &tempBuffer[0];
for(std::size_t i = 0; i < header.width * header.height; ++i) for(std::size_t y = 0; y < imageSize.height; ++y) {
*(buffer++) = palette[data[i]]; 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<std::size_t>(pp)];
}
}
} break;
throw std::runtime_error("FIXME: Port this path properly :("); case kTextureFormat24Bpp: {
util::UniqueArray<util::PixelRGB> rgbPixelData(imageSize.Linear());
stream.read(reinterpret_cast<char*>(&rgbPixelData[0]), imageSize.LinearWithStride<util::PixelRGB>());
auto* pDestBuffer = reinterpret_cast<util::Pixel*>(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<char*>(surface.GetBuffer()), imageSize.LinearWithStride<util::Pixel>());
break;
case kTextureFormatUnknown:
default:
throw std::runtime_error(std::format("Unknown/unsupported texture format {:02x}!", (std::uint16_t)header.format));
break;
} }
return true; return true;