From f9dc66e0b5d319f8a6b32499608b626292d63232 Mon Sep 17 00:00:00 2001 From: erysdren Date: Fri, 9 Oct 2026 15:08:12 -0500 Subject: [PATCH] vpkpp: add support for SiN Reloaded PAKs --- include/vpkpp/format/PAK.h | 3 + src/vpkpp/format/PAK.cpp | 139 ++++++++++++++++++++++++++++++------- 2 files changed, 115 insertions(+), 27 deletions(-) diff --git a/include/vpkpp/format/PAK.h b/include/vpkpp/format/PAK.h index 057b208c9..cae1e41cd 100644 --- a/include/vpkpp/format/PAK.h +++ b/include/vpkpp/format/PAK.h @@ -14,6 +14,8 @@ constexpr auto PAK_SIGNATURE = sourcepp::parser::binary::makeFourCC("PACK"); constexpr uint8_t PAK_SIN_FILENAME_MAX_SIZE = 120; constexpr auto PAK_SIN_SIGNATURE = sourcepp::parser::binary::makeFourCC("SPAK"); +constexpr auto PAK_SIN_RELOADED_SIGNATURE = sourcepp::parser::binary::makeFourCC("SRPK"); + constexpr uint8_t PAK_HROT_FILENAME_MAX_SIZE = 120; constexpr auto PAK_HROT_SIGNATURE = sourcepp::parser::binary::makeFourCC("HROT"); @@ -25,6 +27,7 @@ class PAK : public PackFile { enum class Type { PAK, SIN, + SIN_RELOADED, HROT, }; diff --git a/src/vpkpp/format/PAK.cpp b/src/vpkpp/format/PAK.cpp index 955bccd10..ec612db3b 100644 --- a/src/vpkpp/format/PAK.cpp +++ b/src/vpkpp/format/PAK.cpp @@ -19,6 +19,9 @@ std::unique_ptr PAK::create(const std::string& path, Type type) { case Type::SIN: stream << PAK_SIN_SIGNATURE; break; + case Type::SIN_RELOADED: + stream << PAK_SIN_RELOADED_SIGNATURE; + break; case Type::HROT: stream << PAK_HROT_SIGNATURE; break; @@ -46,6 +49,8 @@ std::unique_ptr PAK::open(const std::string& path, const EntryCallback pak->type = Type::PAK; } else if (signature == PAK_SIN_SIGNATURE) { pak->type = Type::SIN; + } else if (signature == PAK_SIN_RELOADED_SIGNATURE) { + pak->type = Type::SIN_RELOADED; } else if (signature == PAK_HROT_SIGNATURE) { pak->type = Type::HROT; } else { @@ -53,23 +58,57 @@ std::unique_ptr PAK::open(const std::string& path, const EntryCallback return nullptr; } - const auto directoryOffset = reader.read(); - // Directory size / file entry size - const auto fileCount = reader.read() / (sizeof(uint32_t) * 2 + pak->getFilenameLength()); + if (pak->type == Type::SIN_RELOADED) { + const auto reserved = reader.read(); + const auto directoryOffset = reader.read(); + const auto namesOffset = reader.read(); + const auto fileCount = reader.read(); + const auto namesLength = reader.read(); + + // read names buffer + std::string namesBuffer; + reader.seek_in(namesOffset); + reader.read(namesBuffer, namesLength, false); - reader.seek_in(directoryOffset); - for (uint32_t i = 0; i < fileCount; i++) { - Entry entry = createNewEntry(); + // read directory + reader.seek_in(directoryOffset); + for (uint32_t i = 0; i < fileCount; i++) { + Entry entry = createNewEntry(); - auto entryPath = pak->cleanEntryPath(reader.read_string(pak->getFilenameLength())); + const auto filePos = reader.read(); + const auto fileLen = reader.read(); + const auto nameOfs = reader.read(); - entry.offset = reader.read(); - entry.length = reader.read(); + auto entryPath = pak->cleanEntryPath(namesBuffer.c_str() + nameOfs); - pak->entries.emplace(entryPath, entry); + entry.offset = filePos; + entry.length = fileLen; - if (callback) { - callback(entryPath, entry); + pak->entries.emplace(entryPath, entry); + + if (callback) { + callback(entryPath, entry); + } + } + } else { + const auto directoryOffset = reader.read(); + // Directory size / file entry size + const auto fileCount = reader.read() / (sizeof(uint32_t) * 2 + pak->getFilenameLength()); + + reader.seek_in(directoryOffset); + for (uint32_t i = 0; i < fileCount; i++) { + Entry entry = createNewEntry(); + + auto entryPath = pak->cleanEntryPath(reader.read_string(pak->getFilenameLength())); + + entry.offset = reader.read(); + entry.length = reader.read(); + + pak->entries.emplace(entryPath, entry); + + if (callback) { + callback(entryPath, entry); + } } } @@ -115,11 +154,16 @@ bool PAK::bake(const std::string& outputDir_, BakeOptions options, const EntryCa // Read data before overwriting, we don't know if we're writing to ourself std::vector fileData; + std::vector nameData; for (auto& [path, entry] : entriesToBake) { if (auto binData = this->readEntry(path)) { entry->offset = fileData.size(); fileData.insert(fileData.end(), binData->begin(), binData->end()); + + size_t oldSize = nameData.size(); + nameData.resize(oldSize + path.size() + 1); + memcpy(&nameData[oldSize], path.data(), path.size()); } else { entry->offset = 0; entry->length = 0; @@ -133,24 +177,63 @@ bool PAK::bake(const std::string& outputDir_, BakeOptions options, const EntryCa // Signature stream.write(this->getSignature()); - // Offset and size of directory - static constexpr auto HEADER_OFFSET = sizeof(uint32_t) * 3; - stream - .write(HEADER_OFFSET + fileData.size()) - .write(entriesToBake.size() * (sizeof(uint32_t) * 2 + this->getFilenameLength())); + // Header + if (this->type == Type::SIN_RELOADED) { + // Reserved value + // Basically a version identifier, but not used...? + stream.write(0); - // File data - stream.write(fileData); + // Offset of directory + static constexpr auto HEADER_OFFSET = sizeof(uint32_t) * 3 + sizeof(uint64_t) * 2; + stream.write(HEADER_OFFSET + fileData.size()); - // Directory - for (const auto& [path, entry] : entriesToBake) { - stream - .write(path, true, this->getFilenameLength()) - .write(entry->offset + HEADER_OFFSET) - .write(entry->length); + // Offset of names + uint32_t numFiles = entriesToBake.size(); + uint64_t directorySize = (sizeof(uint64_t) + (sizeof(uint32_t) * 2)) * numFiles; + stream.write(HEADER_OFFSET + fileData.size() + directorySize); - if (callback) { - callback(path, *entry); + // Number of files + stream.write(numFiles); + + // Size of names + stream.write(nameData.size()); + + // File data + stream.write(fileData); + + // Directory + for (const auto& [path, entry] : entriesToBake) { + stream + .write(entry->offset + HEADER_OFFSET) + .write(entry->length); + + if (callback) { + callback(path, *entry); + } + } + + // Names + stream.write(nameData); + } else { + // Offset and size of directory + static constexpr auto HEADER_OFFSET = sizeof(uint32_t) * 3; + stream + .write(HEADER_OFFSET + fileData.size()) + .write(entriesToBake.size() * (sizeof(uint32_t) * 2 + this->getFilenameLength())); + + // File data + stream.write(fileData); + + // Directory + for (const auto& [path, entry] : entriesToBake) { + stream + .write(path, true, this->getFilenameLength()) + .write(entry->offset + HEADER_OFFSET) + .write(entry->length); + + if (callback) { + callback(path, *entry); + } } } } @@ -180,6 +263,8 @@ uint32_t PAK::getSignature() const { return PAK_SIGNATURE; case Type::SIN: return PAK_SIN_SIGNATURE; + case Type::SIN_RELOADED: + return PAK_SIN_RELOADED_SIGNATURE; case Type::HROT: return PAK_HROT_SIGNATURE; }