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:
parent
85d9a1deba
commit
9c6448a817
14 changed files with 230 additions and 67 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
52
include/europa/util/ImageSurface.hpp
Normal file
52
include/europa/util/ImageSurface.hpp
Normal 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
|
81
include/europa/util/UniqueArray.hpp
Normal file
81
include/europa/util/UniqueArray.hpp
Normal 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
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
surface.Resize({ static_cast<std::uint16_t>(header.width), static_cast<std::uint16_t>(header.height) });
|
||||
|
||||
void Reader::ReadImage() {
|
||||
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
|
29
src/libeuropa/util/ImageSurface.cpp
Normal file
29
src/libeuropa/util/ImageSurface.cpp
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
std::cout << "Wrote image to " << outPath << '\n';
|
||||
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
15
third_party/CMakeLists.txt
vendored
Normal 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
third_party/libpixel
vendored
1
third_party/libpixel
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 3fff6f6da6d5aee31b3564ee2828947d96e10644
|
1
third_party/lodepng
vendored
Submodule
1
third_party/lodepng
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 0b1d9ccfc2093e5d6620cd9a11d03ee6ff6705f5
|
Loading…
Reference in a new issue