libeuropa,libeuropa/tools: Drop libpixel dependency

libpixel (suprisingly) still compiles, but depends on libraries in a very annoying manner that makes EuropaTools hard to compile. Also, I don't like stb_image_write.

Replace it with some hand-done structs and the simpler/faster/better `lodepng`.
This commit is contained in:
Lily Tsuru 2025-01-15 23:40:41 -05:00
parent 85d9a1deba
commit 9c6448a817
14 changed files with 230 additions and 67 deletions

6
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "third_party/libpixel"]
path = third_party/libpixel
url = https://github.com/modeco80/libpixel.git
[submodule "third_party/lodepng"]
path = third_party/lodepng
url = https://github.com/lvandeve/lodepng

View file

@ -36,14 +36,9 @@ FetchContent_Declare(argparse
GIT_REPOSITORY https://github.com/p-ranav/argparse.git
)
# Future (maybe when libpixel has provisioning for installs :v)
#FetchContent_Declare(libpixel
# GIT_REPOSITORY https://github.com/modeco80/libpixel.git
# )
FetchContent_MakeAvailable(indicators argparse)
add_subdirectory(third_party/libpixel)
add_subdirectory(third_party)
add_subdirectory(src/libeuropa)
add_subdirectory(src/tools)

View file

@ -9,37 +9,25 @@
#ifndef EUROPA_IO_YATFREADER_H
#define EUROPA_IO_YATFREADER_H
#include <pixel/RgbaImage.h>
#include <europa/structs/Yatf.hpp>
#include <iosfwd>
namespace europa::util {
struct ImageSurface;
}
namespace europa::io::yatf {
/// Reader for PS2 Europa .tex (YATF - Yet Another Texture Format) files.
struct Reader {
explicit Reader(std::istream& is);
void InitFromStream(std::istream& is);
void ReadImage();
pixel::RgbaImage& GetImage();
const structs::YatfHeader& GetHeader() const;
[[nodiscard]] bool Invalid() const {
return invalid;
}
bool ReadImage(structs::YatfHeader& header, util::ImageSurface& surface);
private:
std::istream& stream;
bool invalid { false };
structs::YatfHeader header;
pixel::RgbaImage image;
};
} // namespace europa::io
} // namespace europa::io::yatf
#endif // EUROPA_IO_YATFREADER_H

View file

@ -28,7 +28,7 @@ namespace europa::structs {
*/
constexpr static u32 TextureFlag_UsesAlpha = 0x1000000;
constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::little>();
constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::big>();
u32 magic;

View file

@ -0,0 +1,52 @@
//
// EuropaTools
//
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: MIT
//
#pragma once
#include <cstddef>
#include <cstdint>
#include <europa/util/UniqueArray.hpp>
namespace europa::util {
struct Size {
std::uint16_t width;
std::uint16_t height;
constexpr std::size_t Linear() const {
return static_cast<std::size_t>(width) * static_cast<std::size_t>(height);
}
template <class T>
constexpr std::size_t LinearWithStride() const {
// We don't cast here since decltype(sizeof(...)) should
// always be size_t or a size_t bit-compatible type.
// (The C++ standard pretty much enforces #1 however)
return Linear() * sizeof(T);
}
};
/// A RGBA8888 image surface.
struct ImageSurface {
ImageSurface();
explicit ImageSurface(Size size);
Size GetSize() const;
std::uint32_t* GetBuffer();
std::uint32_t const* GetBuffer() const;
void Resize(Size newSize);
private:
UniqueArray<std::uint32_t> imageBuffer;
Size size;
};
} // namespace europa::util

View file

@ -0,0 +1,81 @@
//
// EuropaTools
//
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: MIT
//
#pragma once
#include <cstddef>
#include <memory>
namespace europa::util {
/// A little ergonomic wrapper over
/// std::unique_ptr<T[]> for buffers
/// that need to track their size as well.
template <class T>
struct UniqueArray final {
UniqueArray() = default;
explicit UniqueArray(std::size_t size) {
Resize(size);
}
UniqueArray(const UniqueArray&) = delete;
UniqueArray(UniqueArray&& move) {
array = std::move(move.array);
size = move.size;
// invalidate moved-from array to default state
move.array = nullptr;
move.size = 0;
}
UniqueArray& operator=(UniqueArray&& move) {
array = std::move(move.array);
size = move.size;
// invalidate moved-from array to default state
move.array = nullptr;
move.size = 0;
}
void Resize(std::size_t size) {
this->array = std::make_unique<T[]>(size);
this->size = size;
}
/// Clears this array.
void Clear() {
this->array.reset();
this->size = 0;
}
T& operator[](std::size_t index) {
return (*array)[index];
}
const T& operator[](std::size_t index) const {
return (*array)[index];
}
T* Data() {
return array.get();
}
const T* Data() const {
return array.get();
}
std::size_t Size() const {
return size;
}
private:
std::unique_ptr<T[]> array {};
std::size_t size {};
};
} // namespace europa::util

View file

@ -1,12 +1,15 @@
#
# EuropaTools
#
# (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
# (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
#
# SPDX-License-Identifier: MIT
#
add_library(europa
# Utility
util/ImageSurface.cpp
# Implementation details
io/StreamUtils.cpp
@ -21,8 +24,3 @@ add_library(europa
target_include_directories(europa PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_compile_features(europa PUBLIC cxx_std_20)
# Projects which libeuropa depends on
target_link_libraries(europa PUBLIC
pixel::libpixel
)

View file

@ -11,30 +11,32 @@
// simpler/faster utilities for image buffers.
#include <europa/io/yatf/Reader.hpp>
#include <stdexcept>
#include <vector>
#include "../StreamUtils.h"
#include "europa/structs/Yatf.hpp"
#include "europa/util/ImageSurface.hpp"
namespace europa::io::yatf {
Reader::Reader(std::istream& is)
: stream(is) {
InitFromStream(stream);
}
void Reader::InitFromStream(std::istream& is) {
// Read the image header.
header = impl::ReadStreamType<structs::YatfHeader>(is);
bool Reader::ReadImage(structs::YatfHeader& header, util::ImageSurface& surface) {
header = impl::ReadStreamType<structs::YatfHeader>(stream);
if(!header.IsValid())
invalid = true;
if(!header.IsValid()) {
return false;
}
void Reader::ReadImage() {
surface.Resize({ static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) });
if(header.flags & structs::YatfHeader::TextureFlag_NoPalette) {
image.Resize({ static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) });
stream.read(reinterpret_cast<char*>(image.GetBuffer()), (header.width * header.height) * sizeof(pixel::RgbaColor));
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));
@ -42,22 +44,17 @@ namespace europa::io::yatf {
stream.read(reinterpret_cast<char*>(&palette[0]), sizeof(palette));
stream.read(reinterpret_cast<char*>(&tempBuffer[0]), tempBuffer.size());
image.Resize({ static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) });
auto* buffer = image.GetBuffer();
const auto* data = &tempBuffer[0];
for(std::size_t i = 0; i < header.width * header.height; ++i)
*(buffer++) = palette[data[i]];
}
*/
throw std::runtime_error("FIXME: Port this path properly :(");
}
pixel::RgbaImage& Reader::GetImage() {
return image;
}
const structs::YatfHeader& Reader::GetHeader() const {
return header;
return true;
}
} // namespace europa::io::yatf

View file

@ -0,0 +1,29 @@
#include <europa/util/ImageSurface.hpp>
namespace europa::util {
ImageSurface::ImageSurface() {
size = Size { .width = 0, .height = 0 };
}
ImageSurface::ImageSurface(Size size) {
Resize(size);
}
Size ImageSurface::GetSize() const {
return size;
}
void ImageSurface::Resize(Size newSize) {
imageBuffer.Resize(newSize.Linear());
size = newSize;
}
std::uint32_t* ImageSurface::GetBuffer() {
return imageBuffer.Data();
}
std::uint32_t const* ImageSurface::GetBuffer() const {
return imageBuffer.Data();
}
} // namespace europa::util

View file

@ -8,13 +8,14 @@
add_subdirectory(eupak)
# Most of these utilities are being merged into eupak.
add_executable(texdump texdump.cpp)
target_link_libraries(texdump PUBLIC
europa
lodepng
)
europa_target(texdump)
add_executable(jsfscramble jsfscramble.cpp)
target_link_libraries(jsfscramble PUBLIC
europa

View file

@ -6,20 +6,25 @@
// SPDX-License-Identifier: MIT
//
#include <pixel/ImageWriter.h>
#include <lodepng.h>
#include <europa/io/yatf/Reader.hpp>
#include <filesystem>
#include <fstream>
#include <iostream>
#include "europa/structs/Yatf.hpp"
#include "europa/util/ImageSurface.hpp"
namespace eio = europa::io;
namespace eutil = europa::util;
namespace yatf = eio::yatf;
namespace fs = std::filesystem;
int main(int argc, char** argv) {
// TODO: use argparse, probably!
if(argc != 2) {
std::cout << "Usage: " << argv[0] << " [path to Europa PAK file]";
std::cout << "Usage: " << argv[0] << " [path to PS2 Europa .tex file]\n";
return 1;
}
@ -32,22 +37,24 @@ int main(int argc, char** argv) {
yatf::Reader reader(ifs);
if(reader.Invalid()) {
europa::structs::YatfHeader yatfHeader;
eutil::ImageSurface surface;
if(!reader.ReadImage(yatfHeader, surface)) {
std::cout << "Invalid YATF file \"" << argv[1] << "\"\n";
return 1;
}
reader.ReadImage();
pixel::ImageWriter writer {};
auto outPath = fs::path(argv[1]).replace_extension(".png");
auto size = surface.GetSize();
writer.SetImage(reader.GetImage());
writer.WritePng(outPath);
if(auto res = lodepng::encode(outPath.string(), reinterpret_cast<std::uint8_t*>(surface.GetBuffer()), size.width, size.height, LCT_RGBA, 8); res == 0) {
std::cout << "Wrote image to " << outPath << '\n';
return 0;
} else {
std::cout << "Error encoding PNG: " << lodepng_error_text(res) << "\n";
return 1;
}
return 0;
}

15
third_party/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,15 @@
#
# EuropaTools
#
# (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
#
# SPDX-License-Identifier: MIT
#
add_library(lodepng
lodepng/lodepng.cpp
)
target_include_directories(lodepng PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/lodepng
)

@ -1 +0,0 @@
Subproject commit 3fff6f6da6d5aee31b3564ee2828947d96e10644

1
third_party/lodepng vendored Submodule

@ -0,0 +1 @@
Subproject commit 0b1d9ccfc2093e5d6620cd9a11d03ee6ff6705f5