From 934e87e0991299f32f14e4207fd35599e455e3fe Mon Sep 17 00:00:00 2001 From: NotNite Date: Wed, 26 Aug 2026 11:46:54 -0400 Subject: [PATCH 1/2] vpkpp: add support for Godot pck v3/v4 PCK v3 is present in Godot 4.5 and 4.6. It is always relative (unlike v2) and has a new directory offset, which skips the reserved space after the header. PCK v4 (Godot 4.7+)'s only change is an encrypted directory salt stored after `dirOffset`, but since encrypted packs aren't supported anyways, I didn't bother adding it. --- src/vpkpp/format/PCK.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vpkpp/format/PCK.cpp b/src/vpkpp/format/PCK.cpp index 00fda41a5..c85f7a76d 100644 --- a/src/vpkpp/format/PCK.cpp +++ b/src/vpkpp/format/PCK.cpp @@ -72,8 +72,8 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback } reader.read(pck->header.packVersion); - if (pck->header.packVersion < 1 || pck->header.packVersion > 2) { - // We don't support v3 yet + if (pck->header.packVersion < 1 || pck->header.packVersion > 4) { + // Unknown version return nullptr; } @@ -92,13 +92,18 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback // File directory is encrypted return nullptr; } - if (pck->header.flags & FLAG_DIR_RELATIVE_FILE_DATA) { + if (pck->header.flags & FLAG_DIR_RELATIVE_FILE_DATA || pck->header.packVersion >= 3) { extraEntryContentsOffset += pck->startOffset; pck->header.flags = static_cast(pck->header.flags & ~FLAG_DIR_RELATIVE_FILE_DATA); } - // Reserved - reader.skip_in(16); + if (pck->header.packVersion >= 3) { + auto dirOffset = reader.read(); + reader.seek_in(dirOffset); + } else { + // Reserved + reader.skip_in(16); + } // Directory auto fileCount = reader.read(); From ce97247bf456726e14ef191ad298d163fe84cb88 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Sun, 30 Aug 2026 16:38:42 -0700 Subject: [PATCH 2/2] vtfpp: clean up PCK file --- src/vpkpp/format/PCK.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/vpkpp/format/PCK.cpp b/src/vpkpp/format/PCK.cpp index c85f7a76d..29dd1ad77 100644 --- a/src/vpkpp/format/PCK.cpp +++ b/src/vpkpp/format/PCK.cpp @@ -1,3 +1,5 @@ +// ReSharper disable CppRedundantQualifier + #include #include @@ -14,7 +16,7 @@ constexpr int PCK_DIRECTORY_STRING_PADDING = 4; constexpr int PCK_FILE_DATA_PADDING = 16; std::unique_ptr PCK::create(const std::string& path, uint32_t version, uint32_t godotMajorVersion, uint32_t godotMinorVersion, uint32_t godotPatchVersion) { - if (version != 1 && version != 2) { + if (version < 1 || version > 4) { return nullptr; } @@ -53,18 +55,18 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback FileStream reader{pck->fullFilePath}; reader.seek_in(0); - if (auto signature = reader.read(); signature != PCK_SIGNATURE) { + if (reader.read() != PCK_SIGNATURE) { // PCK might be embedded reader.seek_in(sizeof(uint32_t), std::ios::end); - if (auto endSignature = reader.read(); endSignature != PCK_SIGNATURE) { + if (reader.read() != PCK_SIGNATURE) { return nullptr; } reader.seek_in(-static_cast(sizeof(uint32_t) + sizeof(uint64_t)), std::ios::cur); - auto distanceIntoFile = reader.read(); + const auto distanceIntoFile = reader.read(); reader.seek_in(-static_cast(distanceIntoFile + sizeof(uint64_t)), std::ios::cur); - if (auto startSignature = reader.read(); startSignature != PCK_SIGNATURE) { + if (reader.read() != PCK_SIGNATURE) { return nullptr; } @@ -98,7 +100,7 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback } if (pck->header.packVersion >= 3) { - auto dirOffset = reader.read(); + const auto dirOffset = reader.read(); reader.seek_in(dirOffset); } else { // Reserved @@ -106,7 +108,7 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback } // Directory - auto fileCount = reader.read(); + const auto fileCount = reader.read(); for (uint32_t i = 0; i < fileCount; i++) { Entry entry = createNewEntry(); @@ -140,8 +142,8 @@ std::unique_ptr PCK::open(const std::string& path, const EntryCallback } std::optional> PCK::readEntry(const std::string& path_) const { - auto path = this->cleanEntryPath(path_); - auto entry = this->findEntry(path); + const auto path = this->cleanEntryPath(path_); + const auto entry = this->findEntry(path); if (!entry) { return std::nullopt; } @@ -163,7 +165,7 @@ std::optional> PCK::readEntry(const std::string& path_) c return stream.read_bytes(entry->length); } -void PCK::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options) { +void PCK::addEntryInternal(Entry& entry, const std::string&, std::vector& buffer, EntryOptions) { entry.length = buffer.size(); const auto md5 = crypto::computeMD5(buffer); @@ -173,10 +175,15 @@ void PCK::addEntryInternal(Entry& entry, const std::string& path, std::vectorheader.packVersion > 2) { + // Currently unsupported + return false; + } + // Get the proper file output folder - std::string outputDir = this->getBakeOutputDir(outputDir_); - std::string outputPath = outputDir + '/' + this->getFilename(); + const std::string outputDir = this->getBakeOutputDir(outputDir_); + const std::string outputPath = outputDir + '/' + this->getFilename(); // Reconstruct data for ease of access std::vector> entriesToBake; @@ -249,8 +256,8 @@ bool PCK::bake(const std::string& outputDir_, BakeOptions options, const EntryCa this->dataOffset += sizeof(uint32_t) + // Path length entryPath.length() + padding + // Path - (sizeof(std::size_t) * 2) + // Offset, Length - (sizeof(std::byte) * 16); // MD5 + sizeof(std::size_t) * 2 + // Offset, Length + sizeof(std::byte) * 16; // MD5 if (this->header.packVersion > 1) { this->dataOffset += sizeof(uint32_t); // Flags @@ -318,7 +325,7 @@ uint32_t PCK::getVersion() const { } void PCK::setVersion(uint32_t version) { - if (version == 1 || version == 2) { + if (version >= 1 || version <= 4) { this->header.packVersion = version; } }