EuropaTools/include/europa/structs/Pak.hpp

187 lines
4.6 KiB
C++
Raw Permalink Normal View History

2022-09-04 17:11:14 -04:00
//
// EuropaTools
//
// (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: GPL-3.0-or-later
2022-09-04 17:11:14 -04:00
//
#ifndef EUROPA_STRUCTS_PAK_H
#define EUROPA_STRUCTS_PAK_H
#include <cstdint>
#include <cstring>
#include <europa/structs/ImHexAdapter.hpp>
#include <optional>
#include <variant>
#include <cstdio>
2022-09-04 17:11:14 -04:00
namespace europa::structs {
constexpr static const char VALID_MAGIC[16] = "Europa Packfile";
2022-09-04 17:11:14 -04:00
enum class PakVersion : u16 {
Invalid = 0xffff,
Ver3 = 0x3, ///< Typically used for PMDL files
Ver4 = 0x4,
Ver5 = 0x5
};
struct [[gnu::packed]] PakHeader_Common {
2022-09-04 17:11:14 -04:00
char magic[16]; // "Europa Packfile\0"
/**
* Header size. Doesn't include the magic.
*/
u16 headerSize;
PakVersion version;
2022-09-04 17:11:14 -04:00
bool Valid() const {
return !std::strcmp(magic, VALID_MAGIC);
}
};
2022-09-04 17:11:14 -04:00
template <class Impl, PakVersion Version>
struct [[gnu::packed]] PakHeader_Impl : PakHeader_Common {
constexpr static auto VERSION = Version;
2022-09-04 17:11:14 -04:00
/**
* Get the real header size (including the magic).
*/
[[nodiscard]] constexpr std::size_t RealHeaderSize() const {
return sizeof(magic) + static_cast<std::size_t>(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<const PakHeader_Common*>(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<PakHeader_V3, PakVersion::Ver3> {
using PakHeader_Impl<PakHeader_V3, PakVersion::Ver3>::VERSION;
using PakHeader_Impl<PakHeader_V3, PakVersion::Ver3>::PakHeader_Impl;
using PakHeader_Impl<PakHeader_V3, PakVersion::Ver3>::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{};
2022-09-04 17:11:14 -04:00
};
struct [[gnu::packed]] PakHeader_V4 : public PakHeader_Impl<PakHeader_V4, PakVersion::Ver4> {
using PakHeader_Impl<PakHeader_V4, PakVersion::Ver4>::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;
2022-09-04 17:11:14 -04:00
};
struct [[gnu::packed]] PakHeader_V5 : public PakHeader_Impl<PakHeader_V5, PakVersion::Ver5> {
using PakHeader_Impl<PakHeader_V5, PakVersion::Ver5>::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>;
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!");
2022-09-04 17:11:14 -04:00
} // namespace europa::structs
2022-09-04 17:11:14 -04:00
#endif // EUROPA_STRUCTS_PAK_H