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 {
char magic[4]; // 'YATF'
// FIXME: this is completely wrong
// Flag descriptions:
//
// 0x1 - unknown? (always pressent)

View file

@ -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;

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.
struct ImageSurface {
ImageSurface();

View file

@ -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() {

View file

@ -11,8 +11,8 @@
// simpler/faster utilities for image buffers.
#include <europa/io/yatf/Reader.hpp>
#include <format>
#include <stdexcept>
#include <vector>
#include "../StreamUtils.h"
#include "europa/structs/Yatf.hpp"
@ -31,27 +31,53 @@ namespace europa::io::yatf {
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) {
stream.read(reinterpret_cast<char*>(surface.GetBuffer()), (header.width * header.height) * 4);
} else {
/*
pixel::RgbaColor palette[256];
std::vector<std::uint8_t> tempBuffer((header.width * header.height));
surface.Resize(imageSize);
using enum structs::YatfHeader::TextureFormat;
switch(header.format) {
case kTextureFormat8Bpp: {
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*>(&tempBuffer[0]), tempBuffer.size());
stream.read(reinterpret_cast<char*>(&palettizedData[0]), imageSize.Linear());
auto* buffer = image.GetBuffer();
const auto* data = &tempBuffer[0];
auto* pDestBuffer = reinterpret_cast<util::Pixel*>(surface.GetBuffer());
for(std::size_t i = 0; i < header.width * header.height; ++i)
*(buffer++) = palette[data[i]];
*/
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<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;