diff --git a/.gitignore b/.gitignore index 3afaca1..6a54d2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -/.idea -cmake-build-* +/.cache build/ # swap diff --git a/include/europa/io/PakFile.hpp b/include/europa/io/PakFile.hpp index 83b9d40..464f479 100644 --- a/include/europa/io/PakFile.hpp +++ b/include/europa/io/PakFile.hpp @@ -21,28 +21,81 @@ namespace europa::io { struct PakFile { using DataType = std::vector; + template + void InitAs(const T& value) { + toc = value; + } + + void InitAs(structs::PakVersion version) { + switch(version) { + case structs::PakVersion::Ver3: + toc = structs::PakHeader_V3::TocEntry{}; + break; + case structs::PakVersion::Ver4: + toc = structs::PakHeader_V4::TocEntry{}; + break; + case structs::PakVersion::Ver5: + toc = structs::PakHeader_V5::TocEntry{}; + break; + } + } + /** * Get the file data. */ - [[nodiscard]] const DataType& GetData() const; + [[nodiscard]] const DataType& GetData() const { + return data; + } /** * Get the TOC entry responsible. */ - [[nodiscard]] const structs::PakTocEntry& GetTOCEntry() const; + template + [[nodiscard]] const T& GetTOCEntry() const { + return std::get(toc); + } - void SetData(DataType&& data); + void SetData(DataType&& data) { + this->data = std::move(data); + } - structs::PakTocEntry& GetTOCEntry(); + std::uint32_t GetOffset() const { + std::uint32_t size{}; - void FillTOCEntry(); + std::visit([&](auto& entry) { + size = entry.offset; + }, toc); + + return size; + } + + std::uint32_t GetSize() const { + std::uint32_t size{}; + + std::visit([&](auto& entry) { + size = entry.size; + }, toc); + + return size; + } + + void FillTOCEntry() { + std::visit([&](auto& entry) { + entry.size = static_cast(data.size()); + }, toc); + } + + template + void Visit(const Cb& cb) { + std::visit(cb, toc); + } private: friend PakReader; friend PakWriter; std::vector data; - structs::PakTocEntry tocData; + structs::PakTocEntryVariant toc; }; } // namespace europa::io diff --git a/include/europa/io/PakReader.hpp b/include/europa/io/PakReader.hpp index 8917ecd..9059c2e 100644 --- a/include/europa/io/PakReader.hpp +++ b/include/europa/io/PakReader.hpp @@ -15,11 +15,14 @@ #include #include +#include + namespace europa::io { struct PakReader { using MapType = std::unordered_map; + explicit PakReader(std::istream& is); void ReadData(); @@ -39,13 +42,17 @@ namespace europa::io { const MapType& GetFiles() const; // implement in cpp later, lazy and just wanna get this out :vvv - const structs::PakHeader& GetHeader() const { return header; } + const structs::PakHeaderVariant& GetHeader() const { return header; } private: + template + void ReadData_Impl(); + std::istream& stream; bool invalid { false }; - structs::PakHeader header {}; + structs::PakVersion version; + structs::PakHeaderVariant header {}; MapType files; }; diff --git a/include/europa/io/PakWriter.hpp b/include/europa/io/PakWriter.hpp index a87ab98..df425ec 100644 --- a/include/europa/io/PakWriter.hpp +++ b/include/europa/io/PakWriter.hpp @@ -14,6 +14,7 @@ #include #include #include +#include "europa/structs/Pak.hpp" namespace europa::io { @@ -23,9 +24,11 @@ namespace europa::io { struct PakWriter { using FlattenedType = std::pair; - void Init(structs::PakHeader::Version version); + //void Init(structs::PakHeader::Version version); - const structs::PakHeader& GetHeader() const { return pakHeader; } + //const HeaderType& GetHeader() const { return pakHeader; } + + void SetVersion(structs::PakVersion version); /** * Write the resulting archive to the given output stream. @@ -33,7 +36,12 @@ namespace europa::io { void Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink); private: - structs::PakHeader pakHeader {}; + + template + void WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, bool sectorAligned = true); + + structs::PakVersion version{}; + //HeaderType pakHeader {}; }; } // namespace europa::io diff --git a/include/europa/structs/Pak.hpp b/include/europa/structs/Pak.hpp index 475fe1d..5e9627f 100644 --- a/include/europa/structs/Pak.hpp +++ b/include/europa/structs/Pak.hpp @@ -12,17 +12,23 @@ #include #include #include +#include +#include + +#include namespace europa::structs { - struct [[gnu::packed]] PakHeader { - constexpr static const char VALID_MAGIC[16] = "Europa Packfile"; + constexpr static const char VALID_MAGIC[16] = "Europa Packfile"; - enum class Version : u16 { - Ver4 = 0x4, - Ver5 = 0x5 - }; + enum class PakVersion : u16 { + Invalid = 0xffff, + Ver3 = 0x3, ///< Typically used for PMDL files + Ver4 = 0x4, + Ver5 = 0x5 + }; + struct [[gnu::packed]] PakHeader_Common { char magic[16]; // "Europa Packfile\0" /** @@ -30,7 +36,91 @@ namespace europa::structs { */ u16 headerSize; - Version version; + PakVersion version; + + bool Valid() const { + return !std::strcmp(magic, VALID_MAGIC); + } + }; + + template + struct [[gnu::packed]] PakHeader_Impl : PakHeader_Common { + constexpr static auto VERSION = Version; + + /** + * Get the real header size (including the magic). + */ + [[nodiscard]] constexpr std::size_t RealHeaderSize() const { + return sizeof(magic) + static_cast(headerSize); + } + + constexpr static u16 HeaderSize() { + return sizeof(Impl) - (sizeof(VALID_MAGIC) - 1); + } + + PakHeader_Impl() { + // clear any junk + memset(this, 0, sizeof(PakHeader_Impl)); + + version = Version; + + // Copy important things & set proper header size. + std::memcpy(&magic[0], &VALID_MAGIC[0], sizeof(VALID_MAGIC)); + headerSize = HeaderSize(); + } + + explicit PakHeader_Impl(const PakHeader_Common& header) { + memcpy(&magic[0], &header.magic[0], sizeof(header.magic)); + version = header.version; + headerSize = header.headerSize; + } + + [[nodiscard]] bool Valid() const noexcept { + // Magic must match. + if(!reinterpret_cast(this)->Valid()) + return false; + + // Check header size. + if(headerSize != HeaderSize() && headerSize != HeaderSize() + 1) + return false; + + return version == Version; + } + }; + + struct [[gnu::packed]] PakHeader_V3 : public PakHeader_Impl { + using PakHeader_Impl::VERSION; + using PakHeader_Impl::PakHeader_Impl; + using PakHeader_Impl::Valid; + + struct [[gnu::packed]] TocEntry { + u32 offset; + u32 size; + u32 creationUnixTime; // junk on these v3 files + u16 junk; + }; + + u32 tocOffset; + + u32 tocSize; + + u32 fileCount; + + u32 creationUnixTime; + + // Zeroes. + u32 reservedPad{}; + }; + + struct [[gnu::packed]] PakHeader_V4 : public PakHeader_Impl { + using PakHeader_Impl::PakHeader_Impl; + + struct [[gnu::packed]] TocEntry { + u32 offset; + u32 size; + u32 creationUnixTime; + }; + u8 pad; u32 tocOffset; @@ -43,66 +133,53 @@ namespace europa::structs { // Zeroes. u32 reservedPad; - - /** - * Get the real header size (including the magic). - */ - [[nodiscard]] constexpr std::size_t RealHeaderSize() const { - return sizeof(magic) + static_cast(headerSize); - } - - /** - * Initialize this header (used when writing). - */ - void Init(Version ver) noexcept { - // clear any junk - memset(this, 0, sizeof(PakHeader)); - - // Copy important things. - std::memcpy(&magic[0], &VALID_MAGIC[0], sizeof(VALID_MAGIC)); - - // Set proper header size. - headerSize = sizeof(PakHeader) - (sizeof(PakHeader::VALID_MAGIC) - 1); - - // Set archive version - version = ver; - } - - [[nodiscard]] bool Valid() const noexcept { - // Magic must match. - if(std::strcmp(magic, VALID_MAGIC) != 0) - return false; - - // Check header size. - if(headerSize != sizeof(PakHeader) - (sizeof(PakHeader::VALID_MAGIC) - 1)) - return false; - - using enum Version; - - // Version must match ones we support, - // otherwise it's invalid. - switch(version) { - case Ver4: - case Ver5: - return true; - - default: - return false; - } - } }; - // A Toc entry (without string. Needs to be read in separately) - struct [[gnu::packed]] PakTocEntry { - u32 offset; - u32 size; + struct [[gnu::packed]] PakHeader_V5 : public PakHeader_Impl { + using PakHeader_Impl::PakHeader_Impl; + + struct [[gnu::packed]] TocEntry { + u32 offset; + u32 size; + u32 creationUnixTime; + }; + + u8 pad; + + u32 tocOffset; + + u32 tocSize; + + u32 fileCount; + u32 creationUnixTime; + + // Zeroes. + u32 reservedPad; }; + using PakHeaderVariant = std::variant< + structs::PakHeader_V3, + structs::PakHeader_V4, + structs::PakHeader_V5>; - static_assert(sizeof(PakHeader) == 0x29, "PakHeader wrong size!!"); - static_assert(sizeof(PakHeader) - (sizeof(PakHeader::VALID_MAGIC) - 1) == 0x1a, "PakHeader::headerSize will be invalid when writing archives."); - static_assert(sizeof(PakTocEntry) == 0xc, "PakTocEntry wrong size!"); + using PakTocEntryVariant = std::variant< + structs::PakHeader_V3::TocEntry, + structs::PakHeader_V4::TocEntry, + structs::PakHeader_V5::TocEntry>; + + static_assert(sizeof(PakHeader_V3) == 0x28, "PakHeader_V3 wrong size"); + // TODO: their format really seems to be wrong, 0x19 is proper, but some v3 archives have 0x1a header size + // ??? very weird + //static_assert(sizeof(PakHeader_V3) - (sizeof(VALID_MAGIC) - 1) == 0x1a, "PakHeader_V3::headerSize will be invalid when writing archives."); + static_assert(sizeof(PakHeader_V4) == 0x29, "PakHeader_V4 wrong size!!"); + static_assert(sizeof(PakHeader_V4) - (sizeof(VALID_MAGIC) - 1) == 0x1a, "PakHeader_V4::headerSize will be invalid when writing archives."); + static_assert(sizeof(PakHeader_V5) == 0x29, "PakHeader_V5 wrong size!!"); + static_assert(sizeof(PakHeader_V5) - (sizeof(VALID_MAGIC) - 1) == 0x1a, "PakHeader_V5::headerSize will be invalid when writing archives."); + + static_assert(sizeof(PakHeader_V3::TocEntry) == 0xe, "V3 TocEntry wrong size!"); + static_assert(sizeof(PakHeader_V4::TocEntry) == 0xc, "V4 PakTocEntry wrong size!"); + static_assert(sizeof(PakHeader_V5::TocEntry) == 0xc, "V5 PakTocEntry wrong size!"); } // namespace europa::structs diff --git a/include/europa/structs/Yatf.hpp b/include/europa/structs/Yatf.hpp index 1a51c9f..23d3a54 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::big>(); + constexpr static auto ValidMagic = util::FourCC<"YATF", std::endian::little>(); u32 magic; diff --git a/src/libeuropa/CMakeLists.txt b/src/libeuropa/CMakeLists.txt index ba11aeb..9352503 100644 --- a/src/libeuropa/CMakeLists.txt +++ b/src/libeuropa/CMakeLists.txt @@ -11,7 +11,6 @@ add_library(europa io/StreamUtils.cpp # Pak IO - io/PakFile.cpp io/PakReader.cpp io/PakWriter.cpp diff --git a/src/libeuropa/io/PakFile.cpp b/src/libeuropa/io/PakFile.cpp deleted file mode 100644 index 28839d7..0000000 --- a/src/libeuropa/io/PakFile.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// EuropaTools -// -// (C) 2021-2022 modeco80 -// -// SPDX-License-Identifier: LGPL-3.0-or-later -// - -#include - -namespace europa::io { - - const PakFile::DataType& PakFile::GetData() const { - return data; - } - - const structs::PakTocEntry& PakFile::GetTOCEntry() const { - return tocData; - } - - structs::PakTocEntry& PakFile::GetTOCEntry() { - return tocData; - } - - void PakFile::SetData(PakFile::DataType&& newData) { - data = std::move(newData); - } - - void PakFile::FillTOCEntry() { - tocData.size = static_cast(data.size()); - } - -} // namespace europa::io \ No newline at end of file diff --git a/src/libeuropa/io/PakReader.cpp b/src/libeuropa/io/PakReader.cpp index 13e9fa2..9dea982 100644 --- a/src/libeuropa/io/PakReader.cpp +++ b/src/libeuropa/io/PakReader.cpp @@ -14,27 +14,83 @@ namespace europa::io { +/* + inline std::optional GetPakHeader(const PakHeader_Common& common_header) { + switch(common_header.version) { + case PakVersion::Ver3: + return PakHeader_V3(common_header); + + case PakVersion::Ver4: + return PakHeader_V4(common_header); + + case PakVersion::Ver5: + return PakHeader_V5(common_header); + + case PakVersion::Invalid: + default: + return std::nullopt; + } + } + */ + + + PakReader::PakReader(std::istream& is) : stream(is) { } - void PakReader::ReadData() { - header = impl::ReadStreamType(stream); + template + void PakReader::ReadData_Impl() { + auto header_type = impl::ReadStreamType(stream); - if(!header.Valid()) { + if(!header_type.Valid()) { invalid = true; return; } + + bool isStreams{false}; + if(header_type.tocOffset > 0x17000000) + isStreams = true; + // Read the archive TOC - stream.seekg(header.tocOffset, std::istream::beg); - for(auto i = 0; i < header.fileCount; ++i) { - // The first part of the TOC entry is a VLE string, + stream.seekg(header_type.tocOffset, std::istream::beg); + for(auto i = 0; i < header_type.fileCount; ++i) { + // The first part of the TOC entry is always a VLE string, // which we don't store inside the type (because we can't) // // Read this in first. auto filename = impl::ReadPString(stream); - files[filename].GetTOCEntry() = impl::ReadStreamType(stream); + files[filename].InitAs(impl::ReadStreamType(stream)); + + if(isStreams) + files[filename].Visit([&](auto& tocEntry) { + tocEntry.creationUnixTime = impl::ReadStreamType(stream); + }); + + } + + header = header_type; + } + + void PakReader::ReadData() { + auto commonHeader = impl::ReadStreamType(stream); + stream.seekg(0, std::istream::beg); + + std::cout << "picking version " << (int)commonHeader.version << '\n'; + + switch(commonHeader.version) { + case structs::PakVersion::Ver3: + ReadData_Impl(); + break; + case structs::PakVersion::Ver4: + ReadData_Impl(); + break; + case structs::PakVersion::Ver5: + ReadData_Impl(); + break; + default: + return; } } @@ -51,11 +107,10 @@ namespace europa::io { if(!fileObject.data.empty()) return; - auto& toc = fileObject.GetTOCEntry(); - fileObject.data.resize(toc.size); + fileObject.data.resize(fileObject.GetSize()); - stream.seekg(toc.offset, std::istream::beg); - stream.read(reinterpret_cast(&fileObject.data[0]), toc.size); + stream.seekg(fileObject.GetOffset(), std::istream::beg); + stream.read(reinterpret_cast(&fileObject.data[0]), fileObject.GetSize()); } PakReader::MapType& PakReader::GetFiles() { diff --git a/src/libeuropa/io/PakWriter.cpp b/src/libeuropa/io/PakWriter.cpp index 3dd30c2..3ec796c 100644 --- a/src/libeuropa/io/PakWriter.cpp +++ b/src/libeuropa/io/PakWriter.cpp @@ -12,12 +12,13 @@ #include #include "StreamUtils.h" +#include "europa/structs/Pak.hpp" namespace europa::io { - void PakWriter::Init(structs::PakHeader::Version version) { + void PakWriter::SetVersion(structs::PakVersion version) { // for now. - pakHeader.Init(version); + this->version = version; } // move to a util/ header @@ -27,28 +28,47 @@ namespace europa::io { return (-value) & alignment - 1; } - // TODO: - // - Composable operations (WriteTOC, WriteFile, WriteHeader) + void PakWriter::Write(std::ostream &os, std::vector &&vec, PakProgressReportSink &sink) { + switch(version) { + case structs::PakVersion::Ver3: + WriteImpl(os, std::move(vec), sink); + break; + case structs::PakVersion::Ver4: + WriteImpl(os, std::move(vec), sink); + break; + case structs::PakVersion::Ver5: + WriteImpl(os, std::move(vec), sink); + break; + } + } - void PakWriter::Write(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink) { + template + void PakWriter::WriteImpl(std::ostream& os, std::vector&& vec, PakProgressReportSink& sink, bool sectorAligned) { std::vector sortedFiles = std::move(vec); + T pakHeader{}; + // Sort the flattened array by file size, the biggest first. // Doesn't seem to help (neither does name length) std::ranges::sort(sortedFiles, std::greater{}, [](const FlattenedType& elem) { - return elem.second.GetTOCEntry().size; + return elem.second.GetSize(); }); // Leave space for the header - os.seekp(sizeof(structs::PakHeader), std::ostream::beg); + os.seekp(sizeof(T), std::ostream::beg); // Version 5 paks seem to have an additional bit of reserved data // (which is all zeros.) - if(pakHeader.version == structs::PakHeader::Version::Ver5) { + if(T::VERSION == structs::PakVersion::Ver5) { os.seekp(6, std::ostream::cur); } + //os.seekp( + // AlignBy(os.tellp(), 2048), + // std::istream::beg + //); + // Write file data for(auto& [filename, file] : sortedFiles) { sink.OnEvent({ @@ -56,8 +76,17 @@ namespace europa::io { filename }); - file.GetTOCEntry().offset = os.tellp(); - os.write(reinterpret_cast(file.GetData().data()), file.GetData().size()); + + file.Visit([&](auto& tocEntry) { + tocEntry.offset = os.tellp(); + }); + + os.write(reinterpret_cast(file.GetData().data()), file.GetSize()); + + //os.seekp( + // AlignBy(os.tellp(), 2048), + // std::istream::beg + //); // Flush on file writing os.flush(); @@ -84,7 +113,10 @@ namespace europa::io { os.put(c); os.put('\0'); - impl::WriteStreamType(os, file.GetTOCEntry()); + file.Visit([&](auto& tocEntry) { + impl::WriteStreamType(os, tocEntry); + }); + } @@ -92,7 +124,6 @@ namespace europa::io { PakProgressReportSink::PakEvent::Type::FillInHeader }); - // Fill out the rest of the header. pakHeader.fileCount = sortedFiles.size(); pakHeader.tocSize = static_cast(os.tellp()) - (pakHeader.tocOffset - 1); diff --git a/src/libeuropa/io/StreamUtils.cpp b/src/libeuropa/io/StreamUtils.cpp index 054aeff..9b20b01 100644 --- a/src/libeuropa/io/StreamUtils.cpp +++ b/src/libeuropa/io/StreamUtils.cpp @@ -7,6 +7,7 @@ // #include "StreamUtils.h" +#include namespace europa::io::impl { @@ -64,4 +65,4 @@ namespace europa::io::impl { } } -} // namespace europa::io::impl \ No newline at end of file +} // namespace europa::io::impl diff --git a/src/tools/eupak/Utils.hpp b/src/tools/eupak/Utils.hpp index 02ec797..d21ac6b 100644 --- a/src/tools/eupak/Utils.hpp +++ b/src/tools/eupak/Utils.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace eupak { diff --git a/src/tools/eupak/main.cpp b/src/tools/eupak/main.cpp index bdf21cb..f06fc65 100644 --- a/src/tools/eupak/main.cpp +++ b/src/tools/eupak/main.cpp @@ -53,7 +53,7 @@ int main(int argc, char** argv) { createParser.add_argument("-V","--archive-version") .default_value("starfighter") - .help(R"(Output archive version. Either "starfighter" or "jedistarfighter".)") + .help(R"(Output archive version. Either "pmdl", "starfighter" or "jedistarfighter".)") .metavar("VERSION"); createParser.add_argument("output") @@ -123,16 +123,18 @@ int main(int argc, char** argv) { if(createParser.is_used("--archive-version")) { const auto& versionStr = createParser.get("--archive-version"); - if(versionStr == "starfighter") { - args.pakVersion = europa::structs::PakHeader::Version::Ver4; + if(versionStr == "pmdl") { + args.pakVersion = europa::structs::PakVersion::Ver3; + } else if(versionStr == "starfighter") { + args.pakVersion = europa::structs::PakVersion::Ver4; } else if(versionStr == "jedistarfighter") { - args.pakVersion = europa::structs::PakHeader::Version::Ver5; + args.pakVersion = europa::structs::PakVersion::Ver5; } else { std::cout << "Error: Invalid version \"" << versionStr << "\"\n" << createParser; return 1; } } else { - args.pakVersion = europa::structs::PakHeader::Version::Ver4; + args.pakVersion = europa::structs::PakVersion::Ver4; } diff --git a/src/tools/eupak/tasks/CreateTask.cpp b/src/tools/eupak/tasks/CreateTask.cpp index 4fd4376..381e2a8 100644 --- a/src/tools/eupak/tasks/CreateTask.cpp +++ b/src/tools/eupak/tasks/CreateTask.cpp @@ -78,7 +78,7 @@ namespace eupak::tasks { int CreateTask::Run(Arguments&& args) { europa::io::PakWriter writer; - writer.Init(args.pakVersion); + writer.SetVersion(args.pakVersion); auto currFile = 0; auto fileCount = 0; @@ -143,9 +143,10 @@ namespace eupak::tasks { ifs.read(reinterpret_cast(&pakData[0]), pakData.size()); file.SetData(std::move(pakData)); - file.FillTOCEntry(); - file.GetTOCEntry().creationUnixTime = static_cast(lastModified.time_since_epoch().count()); + file.InitAs(args.pakVersion); + + //file.GetTOCEntry().creationUnixTime = static_cast(lastModified.time_since_epoch().count()); files.emplace_back(std::make_pair(relativePathName, std::move(file))); progress.tick(); diff --git a/src/tools/eupak/tasks/CreateTask.hpp b/src/tools/eupak/tasks/CreateTask.hpp index a65bb8c..5259780 100644 --- a/src/tools/eupak/tasks/CreateTask.hpp +++ b/src/tools/eupak/tasks/CreateTask.hpp @@ -22,7 +22,7 @@ namespace eupak::tasks { fs::path outputFile; bool verbose; - europa::structs::PakHeader::Version pakVersion; + europa::structs::PakVersion pakVersion; }; int Run(Arguments&& args); diff --git a/src/tools/eupak/tasks/ExtractTask.cpp b/src/tools/eupak/tasks/ExtractTask.cpp index 532ab6e..9bf94b9 100644 --- a/src/tools/eupak/tasks/ExtractTask.cpp +++ b/src/tools/eupak/tasks/ExtractTask.cpp @@ -80,7 +80,7 @@ namespace eupak::tasks { std::cerr << "Extracting file \"" << filename << "\"...\n"; } - ofs.write(reinterpret_cast(file.GetData().data()), static_cast(file.GetTOCEntry().size)); + ofs.write(reinterpret_cast(file.GetData().data()), static_cast(file.GetSize())); ofs.flush(); progress.tick(); } diff --git a/src/tools/eupak/tasks/InfoTask.cpp b/src/tools/eupak/tasks/InfoTask.cpp index fab0a9d..3a5486c 100644 --- a/src/tools/eupak/tasks/InfoTask.cpp +++ b/src/tools/eupak/tasks/InfoTask.cpp @@ -36,23 +36,35 @@ namespace eupak::tasks { return 1; } - std::string version = "Version 4 (Starfighter)"; + std::visit([&](auto& header){ + std::string version; + if constexpr(std::decay_t::VERSION == europa::structs::PakVersion::Ver3) + version = "Version 3 (PMDL)"; + else if constexpr(std::decay_t::VERSION == europa::structs::PakVersion::Ver4) + version = "Version 4 (Starfighter)"; + else if constexpr(std::decay_t::VERSION == europa::structs::PakVersion::Ver5) + version = "Version 5 (Jedi Starfighter)"; - if(reader.GetHeader().version == europa::structs::PakHeader::Version::Ver5) - version = "Version 5 (Jedi Starfighter)"; - std::cout << "Archive " << args.inputPath << ":\n"; - std::cout << " Created: " << FormatUnixTimestamp(reader.GetHeader().creationUnixTime, DATE_FORMAT) << '\n'; - std::cout << " Version: " << version << '\n'; - std::cout << " Size: " << FormatUnit(reader.GetHeader().tocOffset + reader.GetHeader().tocSize) << '\n'; - std::cout << " File Count: " << reader.GetHeader().fileCount << " files\n"; + std::cout << "Archive " << args.inputPath << ":\n"; + std::cout << " Created: " << FormatUnixTimestamp(header.creationUnixTime, DATE_FORMAT) << '\n'; + std::cout << " Version: " << version << '\n'; + std::cout << " Size: " << FormatUnit(header.tocOffset + header.tocSize) << '\n'; + std::cout << " File Count: " << header.fileCount << " files\n"; + + }, reader.GetHeader()); + // Print a detailed file list if verbose. if(args.verbose) { for(auto& [ filename, file ] : reader.GetFiles()) { std::cout << "File \"" << filename << "\":\n"; - std::cout << " Created: " << FormatUnixTimestamp(file.GetTOCEntry().creationUnixTime, DATE_FORMAT) << '\n'; - std::cout << " Size: " << FormatUnit(file.GetTOCEntry().size) << '\n'; + file.Visit([&](auto& tocEntry) { + + std::cout << " Created: " << FormatUnixTimestamp(tocEntry.creationUnixTime, DATE_FORMAT) << '\n'; + std::cout << " Size: " << FormatUnit(tocEntry.size) << '\n'; + + }); } }