From 9c6448a817c47e12b11bbf6b70b4c54241e75ef7 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Wed, 15 Jan 2025 23:40:41 -0500 Subject: [PATCH] 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`. --- .gitmodules | 6 +-- CMakeLists.txt | 7 +-- include/europa/io/yatf/Reader.hpp | 24 +++------ include/europa/structs/Yatf.hpp | 2 +- include/europa/util/ImageSurface.hpp | 52 ++++++++++++++++++ include/europa/util/UniqueArray.hpp | 81 ++++++++++++++++++++++++++++ src/libeuropa/CMakeLists.txt | 10 ++-- src/libeuropa/io/yatf/Reader.cpp | 35 ++++++------ src/libeuropa/util/ImageSurface.cpp | 29 ++++++++++ src/tools/CMakeLists.txt | 3 +- src/tools/texdump.cpp | 31 ++++++----- third_party/CMakeLists.txt | 15 ++++++ third_party/libpixel | 1 - third_party/lodepng | 1 + 14 files changed, 230 insertions(+), 67 deletions(-) create mode 100644 include/europa/util/ImageSurface.hpp create mode 100644 include/europa/util/UniqueArray.hpp create mode 100644 src/libeuropa/util/ImageSurface.cpp create mode 100644 third_party/CMakeLists.txt delete mode 160000 third_party/libpixel create mode 160000 third_party/lodepng diff --git a/.gitmodules b/.gitmodules index e6bcc4f..5e37d17 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8da2ca2..8e292fb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/europa/io/yatf/Reader.hpp b/include/europa/io/yatf/Reader.hpp index 1ed91de..60d804d 100644 --- a/include/europa/io/yatf/Reader.hpp +++ b/include/europa/io/yatf/Reader.hpp @@ -9,37 +9,25 @@ #ifndef EUROPA_IO_YATFREADER_H #define EUROPA_IO_YATFREADER_H -#include - #include #include +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 diff --git a/include/europa/structs/Yatf.hpp b/include/europa/structs/Yatf.hpp index faa4c09..8300ac4 100644 --- a/include/europa/structs/Yatf.hpp +++ b/include/europa/structs/Yatf.hpp @@ -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; diff --git a/include/europa/util/ImageSurface.hpp b/include/europa/util/ImageSurface.hpp new file mode 100644 index 0000000..b9ee4e1 --- /dev/null +++ b/include/europa/util/ImageSurface.hpp @@ -0,0 +1,52 @@ +// +// EuropaTools +// +// (C) 2021-2025 modeco80 +// +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include +#include + +namespace europa::util { + + struct Size { + std::uint16_t width; + std::uint16_t height; + + constexpr std::size_t Linear() const { + return static_cast(width) * static_cast(height); + } + + template + 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 imageBuffer; + Size size; + }; + +} // namespace europa::util \ No newline at end of file diff --git a/include/europa/util/UniqueArray.hpp b/include/europa/util/UniqueArray.hpp new file mode 100644 index 0000000..52c043d --- /dev/null +++ b/include/europa/util/UniqueArray.hpp @@ -0,0 +1,81 @@ +// +// EuropaTools +// +// (C) 2021-2025 modeco80 +// +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace europa::util { + + /// A little ergonomic wrapper over + /// std::unique_ptr for buffers + /// that need to track their size as well. + template + 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(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 array {}; + std::size_t size {}; + }; +} // namespace europa::util \ No newline at end of file diff --git a/src/libeuropa/CMakeLists.txt b/src/libeuropa/CMakeLists.txt index 958ed15..4b77462 100644 --- a/src/libeuropa/CMakeLists.txt +++ b/src/libeuropa/CMakeLists.txt @@ -1,12 +1,15 @@ # # EuropaTools # -# (C) 2021-2022 modeco80 +# (C) 2021-2025 modeco80 # # 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 - ) diff --git a/src/libeuropa/io/yatf/Reader.cpp b/src/libeuropa/io/yatf/Reader.cpp index 010f807..97dc667 100644 --- a/src/libeuropa/io/yatf/Reader.cpp +++ b/src/libeuropa/io/yatf/Reader.cpp @@ -11,30 +11,32 @@ // simpler/faster utilities for image buffers. #include +#include #include #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(is); + bool Reader::ReadImage(structs::YatfHeader& header, util::ImageSurface& surface) { + header = impl::ReadStreamType(stream); - if(!header.IsValid()) - invalid = true; - } + if(!header.IsValid()) { + return false; + } + + surface.Resize({ static_cast(header.width), static_cast(header.height) }); - void Reader::ReadImage() { if(header.flags & structs::YatfHeader::TextureFlag_NoPalette) { - image.Resize({ static_cast(header.width), static_cast(header.height) }); - stream.read(reinterpret_cast(image.GetBuffer()), (header.width * header.height) * sizeof(pixel::RgbaColor)); + stream.read(reinterpret_cast(surface.GetBuffer()), (header.width * header.height) * 4); } else { + /* pixel::RgbaColor palette[256]; std::vector tempBuffer((header.width * header.height)); @@ -42,22 +44,17 @@ namespace europa::io::yatf { stream.read(reinterpret_cast(&palette[0]), sizeof(palette)); stream.read(reinterpret_cast(&tempBuffer[0]), tempBuffer.size()); - image.Resize({ static_cast(header.width), static_cast(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 \ No newline at end of file diff --git a/src/libeuropa/util/ImageSurface.cpp b/src/libeuropa/util/ImageSurface.cpp new file mode 100644 index 0000000..3139df7 --- /dev/null +++ b/src/libeuropa/util/ImageSurface.cpp @@ -0,0 +1,29 @@ +#include + +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 \ No newline at end of file diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 70ec275..e869065 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -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 diff --git a/src/tools/texdump.cpp b/src/tools/texdump.cpp index 648a29f..a923b9e 100644 --- a/src/tools/texdump.cpp +++ b/src/tools/texdump.cpp @@ -6,20 +6,25 @@ // SPDX-License-Identifier: MIT // -#include +#include #include #include #include #include +#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(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; } \ No newline at end of file diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 0000000..d78013a --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# EuropaTools +# +# (C) 2021-2025 modeco80 +# +# SPDX-License-Identifier: MIT +# + +add_library(lodepng + lodepng/lodepng.cpp +) + +target_include_directories(lodepng PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/lodepng +) \ No newline at end of file diff --git a/third_party/libpixel b/third_party/libpixel deleted file mode 160000 index 3fff6f6..0000000 --- a/third_party/libpixel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3fff6f6da6d5aee31b3564ee2828947d96e10644 diff --git a/third_party/lodepng b/third_party/lodepng new file mode 160000 index 0000000..0b1d9cc --- /dev/null +++ b/third_party/lodepng @@ -0,0 +1 @@ +Subproject commit 0b1d9ccfc2093e5d6620cd9a11d03ee6ff6705f5