From 8d2bd00d6c3371872a5b663202e14821e64dc6ad Mon Sep 17 00:00:00 2001 From: craftablescience Date: Mon, 19 Aug 2024 22:33:48 -0400 Subject: [PATCH 01/10] feat(vpkpp): switch to storing entries in a trie, remove GRP support --- .gitmodules | 3 + ext/_ext.cmake | 6 + ext/hat-trie | 1 + ext/miniz | 2 +- include/sourcepp/String.h | 2 +- include/vpkpp/Entry.h | 42 +- include/vpkpp/PackFile.h | 85 ++-- include/vpkpp/PackFileType.h | 1 - include/vpkpp/format/BSP.h | 4 +- include/vpkpp/format/FPX.h | 4 +- include/vpkpp/format/GCF.h | 4 +- include/vpkpp/format/GMA.h | 8 +- include/vpkpp/format/GRP.h | 31 -- include/vpkpp/format/PAK.h | 8 +- include/vpkpp/format/PCK.h | 8 +- include/vpkpp/format/VPK.h | 22 +- include/vpkpp/format/VPK_VTMB.h | 14 +- include/vpkpp/format/ZIP.h | 10 +- src/sourcepp/String.cpp | 2 +- src/vpkpp/Entry.cpp | 25 -- src/vpkpp/PackFile.cpp | 391 ++++++++---------- src/vpkpp/_vpkpp.cmake | 5 +- src/vpkpp/format/BSP.cpp | 25 +- src/vpkpp/format/FPX.cpp | 27 +- src/vpkpp/format/GCF.cpp | 90 ++-- src/vpkpp/format/GMA.cpp | 90 ++-- src/vpkpp/format/GRP.cpp | 175 -------- src/vpkpp/format/PAK.cpp | 78 ++-- src/vpkpp/format/PCK.cpp | 95 ++--- src/vpkpp/format/VPK.cpp | 248 ++++++----- src/vpkpp/format/VPK_VTMB.cpp | 86 ++-- src/vpkpp/format/ZIP.cpp | 130 ++---- .../format/example/ExamplePackFileImpl.cpp | 119 ++---- .../format/example/ExamplePackFileImpl.h | 10 +- 34 files changed, 660 insertions(+), 1191 deletions(-) create mode 160000 ext/hat-trie delete mode 100644 include/vpkpp/format/GRP.h delete mode 100644 src/vpkpp/Entry.cpp delete mode 100644 src/vpkpp/format/GRP.cpp diff --git a/.gitmodules b/.gitmodules index b5170937d..1ec7d94fc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "ext/minizip-ng"] path = ext/minizip-ng url = https://github.com/zlib-ng/minizip-ng +[submodule "ext/hat-trie"] + path = ext/hat-trie + url = https://github.com/Tessil/hat-trie diff --git a/ext/_ext.cmake b/ext/_ext.cmake index 91cd9d746..22f41da34 100644 --- a/ext/_ext.cmake +++ b/ext/_ext.cmake @@ -21,6 +21,12 @@ if(NOT TARGET cryptopp::cryptopp) endif() +# hat-trie +if(NOT TARGET tsl::hat_trie) + add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/hat-trie") +endif() + + # ice add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ice") diff --git a/ext/hat-trie b/ext/hat-trie new file mode 160000 index 000000000..906e6abd1 --- /dev/null +++ b/ext/hat-trie @@ -0,0 +1 @@ +Subproject commit 906e6abd1e7063f1dacd3a6b270aa654b525eb0a diff --git a/ext/miniz b/ext/miniz index 8573fd7cd..bf7a1f0a5 160000 --- a/ext/miniz +++ b/ext/miniz @@ -1 +1 @@ -Subproject commit 8573fd7cd6f49b262a0ccc447f3c6acfc415e556 +Subproject commit bf7a1f0a5aa1deae9cab2d73b5ef9edec41b877c diff --git a/include/sourcepp/String.h b/include/sourcepp/String.h index a9fc5f93c..edf7b570b 100644 --- a/include/sourcepp/String.h +++ b/include/sourcepp/String.h @@ -52,7 +52,7 @@ void toUpper(std::string& input); [[nodiscard]] std::string generateUUIDv4(); -[[nodiscard]] std::string padNumber(int number, int width, char pad = '0'); +[[nodiscard]] std::string padNumber(int64_t number, int width, char pad = '0'); void normalizeSlashes(std::string& path, bool stripSlashPrefix = false, bool stripSlashSuffix = true); diff --git a/include/vpkpp/Entry.h b/include/vpkpp/Entry.h index e7bb24be1..cc0190eb7 100644 --- a/include/vpkpp/Entry.h +++ b/include/vpkpp/Entry.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -16,44 +15,33 @@ class Entry { friend class PackFile; public: - /// Path to this entry (e.g. "materials/cable.vmt") - std::string path; /// Format-specific flags (PCK: File flags, VPK: Internal parser state) uint32_t flags = 0; + + /// Which external archive this entry is in. + /// If this file format does not support external archives, this will always be 0 + uint32_t archiveIndex = 0; + /// Length in bytes (in formats with compression, this is the uncompressed length) uint64_t length = 0; - /// Offset, format-specific meaning - may be unused - uint64_t offset = 0; - /// If the format supports compression, this is the compressed length + + /// If the format supports compression, this is the compressed length. /// If compression is not supported or unused, this will remain 0 uint64_t compressedLength = 0; - /// Which external archive this entry is in - /// If this file format does not support external archives, this will always be 0 - uint16_t archiveIndex = 0; + + /// Offset, format-specific meaning - 0 if unused, or if the offset genuinely is 0 + uint64_t offset = 0; + + /// Format-specific (PCK: MD5 hash, VPK: Preloaded data) + std::vector extraData; + /// CRC32 checksum - 0 if unused. /// Note that for GCF, this is actually an index into a checksum array and NOT a checksum uint32_t crc32 = 0; + /// Used to check if entry is saved to disk bool unbaked = false; - /// PCK - Each file has a 16-byte MD5 hash - std::array pck_md5{}; - - /// VPK - Preloaded data - std::vector vpk_preloadedData; - - /// Returns the parent directory's path (e.g. "materials/cable.vmt" -> "materials") - [[nodiscard]] std::string getParentPath() const; - - /// Returns the filename (e.g. "materials/cable.vmt" -> "cable.vmt") - [[nodiscard]] std::string getFilename() const; - - /// Returns the file stem (e.g. "materials/cable.vmt" -> "cable") - [[nodiscard]] std::string getStem() const; - - /// Returns the file extension without a period (e.g. "materials/cable.vmt" -> "vmt") - [[nodiscard]] std::string getExtension() const; - private: /// The data attached to the unbaked entry, or the path to the file containing the unbaked entry's data std::variant> unbakedData; diff --git a/include/vpkpp/PackFile.h b/include/vpkpp/PackFile.h index 688843e7d..c13fbe0f2 100644 --- a/include/vpkpp/PackFile.h +++ b/include/vpkpp/PackFile.h @@ -10,6 +10,7 @@ #include #include +#include #include "Attribute.h" #include "Entry.h" @@ -21,24 +22,28 @@ namespace vpkpp { // Executable extensions - mostly just for Godot exports constexpr std::string_view EXECUTABLE_EXTENSION0 = ".exe"; // - Windows constexpr std::string_view EXECUTABLE_EXTENSION1 = ".bin"; // - Linux + Godot 3 and below (and Generic) -constexpr std::string_view EXECUTABLE_EXTENSION2 = ".x86"; // | Godot 4 (32-bit) -constexpr std::string_view EXECUTABLE_EXTENSION3 = ".x86_32"; // | Godot 4 (32-bit) -constexpr std::string_view EXECUTABLE_EXTENSION4 = ".x86_64"; // | Godot 4 (64-bit) +constexpr std::string_view EXECUTABLE_EXTENSION2 = ".x86_64"; // | Godot 4 (64-bit) class PackFile { public: + /// Accepts the entry's path and metadata + using EntryCallback = std::function; + + /// Accepts the entry's path and metadata + using EntryPredicate = std::function; + + using EntryTrie = tsl::htrie_map; + PackFile(const PackFile& other) = delete; PackFile& operator=(const PackFile& other) = delete; + PackFile(PackFile&& other) noexcept = default; PackFile& operator=(PackFile&& other) noexcept = default; virtual ~PackFile() = default; - // Accepts the entry parent directory and the entry metadata - using Callback = std::function; - /// Open a generic pack file. The parser is selected based on the file extension - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); /// Get the file type of the pack file [[nodiscard]] PackFileType getType() const; @@ -51,7 +56,7 @@ class PackFile { return false; } - /// Verify the checksums of each file, if a file fails the check its filename will be added to the vector + /// Verify the checksums of each file, if a file fails the check its path will be added to the vector /// If there is no checksum ability in the format, it will return an empty vector [[nodiscard]] virtual std::vector verifyEntryChecksums() const; @@ -75,57 +80,63 @@ class PackFile { } /// Check if an entry exists given the file path - [[nodiscard]] bool hasEntry(const std::string& filename_, bool includeUnbaked = true) const; + [[nodiscard]] bool hasEntry(const std::string& path, bool includeUnbaked = true) const; /// Try to find an entry given the file path - [[nodiscard]] std::optional findEntry(const std::string& filename_, bool includeUnbaked = true) const; + [[nodiscard]] std::optional findEntry(const std::string& path_, bool includeUnbaked = true) const; /// Try to read the entry's data to a bytebuffer - [[nodiscard]] virtual std::optional> readEntry(const Entry& entry) const = 0; + [[nodiscard]] virtual std::optional> readEntry(const std::string& path_) const = 0; /// Try to read the entry's data to a string - [[nodiscard]] std::optional readEntryText(const Entry& entry) const; + [[nodiscard]] std::optional readEntryText(const std::string& path) const; [[nodiscard]] virtual constexpr bool isReadOnly() const noexcept { return false; } /// Add a new entry from a file path - the first parameter is the path in the PackFile, the second is the path on disk - void addEntry(const std::string& filename_, const std::string& pathToFile, EntryOptions options_); + void addEntry(const std::string& entryPath, const std::string& filepath, EntryOptions options_); /// Add a new entry from a buffer - void addEntry(const std::string& filename_, std::vector&& buffer, EntryOptions options_); + void addEntry(const std::string& path, std::vector&& buffer, EntryOptions options_); /// Add a new entry from a buffer - void addEntry(const std::string& filename_, const std::byte* buffer, uint64_t bufferLen, EntryOptions options_); + void addEntry(const std::string& path, const std::byte* buffer, uint64_t bufferLen, EntryOptions options_); /// Remove an entry - virtual bool removeEntry(const std::string& filename_); + virtual bool removeEntry(const std::string& path_); + + /// Remove a directory + virtual std::size_t removeDirectory(const std::string& dirName_); /// If output folder is unspecified, it will overwrite the original - virtual bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) = 0; + virtual bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) = 0; /// Extract the given entry to disk at the given file path - bool extractEntry(const Entry& entry, const std::string& filePath) const; // NOLINT(*-use-nodiscard) + bool extractEntry(const std::string& entryPath, const std::string& filepath) const; // NOLINT(*-use-nodiscard) /// Extract the given directory to disk under the given output directory - bool extractDirectory(const std::string& dir, const std::string& outputDir) const; // NOLINT(*-use-nodiscard) + bool extractDirectory(const std::string& dir_, const std::string& outputDir) const; // NOLINT(*-use-nodiscard) /// Extract the contents of the pack file to disk at the given directory bool extractAll(const std::string& outputDir, bool createUnderPackFileDir = true) const; // NOLINT(*-use-nodiscard) /// Extract the contents of the pack file to disk at the given directory - only entries which match the predicate are extracted - bool extractAll(const std::string& outputDir, const std::function& predicate) const; // NOLINT(*-use-nodiscard) + bool extractAll(const std::string& outputDir, const EntryPredicate& predicate) const; // NOLINT(*-use-nodiscard) /// Get entries saved to disk - [[nodiscard]] const std::unordered_map>& getBakedEntries() const; + [[nodiscard]] const EntryTrie& getBakedEntries() const; /// Get entries that have been added but not yet baked - [[nodiscard]] const std::unordered_map>& getUnbakedEntries() const; + [[nodiscard]] const EntryTrie& getUnbakedEntries() const; /// Get the number of entries in the pack file [[nodiscard]] std::size_t getEntryCount(bool includeUnbaked = true) const; + /// Run a callback for each entry in the pack file + void runForAllEntries(const EntryCallback& operation, bool includeUnbaked = true) const; + /// /home/user/pak01_dir.vpk [[nodiscard]] std::string_view getFilepath() const; @@ -151,7 +162,7 @@ class PackFile { [[nodiscard]] virtual explicit operator std::string() const; /// On Windows, some characters and file names are invalid - this escapes the given entry path - [[nodiscard]] static std::string escapeEntryPath(const std::string& path); + [[nodiscard]] static std::string escapeEntryPathForWrite(const std::string& path); /// Returns a list of supported extensions, e.g. {".vpk", ".bsp"} [[nodiscard]] static std::vector getSupportedFileTypes(); @@ -159,9 +170,11 @@ class PackFile { protected: PackFile(std::string fullFilePath_, PackFileOptions options_); + void runForAllEntries(const std::function& operation, bool includeUnbaked = true); + [[nodiscard]] std::vector verifyEntryChecksumsUsingCRC32() const; - virtual Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) = 0; + virtual void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) = 0; [[nodiscard]] std::string getBakeOutputDir(const std::string& outputDir) const; @@ -169,25 +182,21 @@ class PackFile { void setFullFilePath(const std::string& outputDir); - [[nodiscard]] static std::pair splitFilenameAndParentDir(const std::string& filename); + [[nodiscard]] std::string cleanEntryPath(const std::string& path) const; [[nodiscard]] static Entry createNewEntry(); - [[nodiscard]] static const std::variant>& getEntryUnbakedData(const Entry& entry); - - [[nodiscard]] static bool isEntryUnbakedUsingByteBuffer(const Entry& entry); - - [[nodiscard]] std::optional> readUnbakedEntry(const Entry& entry) const; + [[nodiscard]] static std::optional> readUnbakedEntry(const Entry& entry); std::string fullFilePath; PackFileType type = PackFileType::UNKNOWN; PackFileOptions options; - std::unordered_map> entries; - std::unordered_map> unbakedEntries; + EntryTrie entries; + EntryTrie unbakedEntries; - using FactoryFunction = std::function(const std::string& path, PackFileOptions options, const Callback& callback)>; + using FactoryFunction = std::function(const std::string& path, PackFileOptions options, const EntryCallback& callback)>; static std::unordered_map>& getOpenExtensionRegistry(); @@ -203,11 +212,11 @@ class PackFileReadOnly : public PackFile { [[nodiscard]] explicit operator std::string() const override; protected: - PackFileReadOnly(std::string fullFilePath_, PackFileOptions options_); + PackFileReadOnly([[maybe_unused]] [[maybe_unused]] std::string fullFilePath_, PackFileOptions options_); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) final; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) final; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) final; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) final; }; } // namespace vpkpp @@ -222,6 +231,4 @@ class PackFileReadOnly : public PackFile { #define VPKPP_REGISTER_PACKFILE_OPEN_EXECUTABLE(function) \ static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable0TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION0, function); \ static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable1TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION1, function); \ - static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable2TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION2, function); \ - static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable3TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION3, function); \ - static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable4TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION4, function); + static inline const FactoryFunction& VPKPP_HELPER_UNIQUE_NAME(packFileOpenExecutable2TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION2, function) diff --git a/include/vpkpp/PackFileType.h b/include/vpkpp/PackFileType.h index 9037e92b0..2d9454926 100644 --- a/include/vpkpp/PackFileType.h +++ b/include/vpkpp/PackFileType.h @@ -8,7 +8,6 @@ enum class PackFileType { FPX, GCF, GMA, - GRP, PAK, PCK, VPK, diff --git a/include/vpkpp/format/BSP.h b/include/vpkpp/format/BSP.h index 90d8f1802..2781c7694 100644 --- a/include/vpkpp/format/BSP.h +++ b/include/vpkpp/format/BSP.h @@ -14,13 +14,13 @@ constexpr std::string_view BSP_EXTENSION = ".bsp"; class BSP : public ZIP, private bsppp::BSP { public: /// Open a BSP file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool isCaseSensitive() const noexcept override { return false; } - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] explicit operator std::string() const override; diff --git a/include/vpkpp/format/FPX.h b/include/vpkpp/format/FPX.h index 8f611b7be..6a639de2a 100644 --- a/include/vpkpp/format/FPX.h +++ b/include/vpkpp/format/FPX.h @@ -11,12 +11,12 @@ constexpr std::string_view FPX_EXTENSION = ".fpx"; class FPX : public VPK { public: /// Open a directory VPK file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); protected: FPX(const std::string& fullFilePath_, PackFileOptions options_); - [[nodiscard]] static std::unique_ptr openInternal(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr openInternal(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); private: using VPK::generateKeyPairFiles; diff --git a/include/vpkpp/format/GCF.h b/include/vpkpp/format/GCF.h index 4874d43ed..3c86c6dc7 100644 --- a/include/vpkpp/format/GCF.h +++ b/include/vpkpp/format/GCF.h @@ -110,7 +110,7 @@ class GCF : public PackFileReadOnly { #pragma pack(pop) public: - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool hasEntryChecksums() const override { return true; @@ -118,7 +118,7 @@ class GCF : public PackFileReadOnly { [[nodiscard]] std::vector verifyEntryChecksums() const override; - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; [[nodiscard]] std::vector getSupportedEntryAttributes() const override; diff --git a/include/vpkpp/format/GMA.h b/include/vpkpp/format/GMA.h index c8719bc75..d0b85d187 100644 --- a/include/vpkpp/format/GMA.h +++ b/include/vpkpp/format/GMA.h @@ -25,7 +25,7 @@ class GMA : public PackFile { public: /// Open a GMA file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool hasEntryChecksums() const override { return true; @@ -37,9 +37,9 @@ class GMA : public PackFile { [[nodiscard]] bool verifyPackFileChecksum() const override; - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::vector getSupportedEntryAttributes() const override; @@ -48,7 +48,7 @@ class GMA : public PackFile { protected: GMA(const std::string& fullFilePath_, PackFileOptions options_); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; Header header{}; diff --git a/include/vpkpp/format/GRP.h b/include/vpkpp/format/GRP.h deleted file mode 100644 index d68f15f52..000000000 --- a/include/vpkpp/format/GRP.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "../PackFile.h" - -namespace vpkpp { - -constexpr int8_t GRP_FILENAME_MAX_SIZE = 12; -constexpr std::string_view GRP_SIGNATURE = "KenSilverman"; -constexpr std::string_view GRP_EXTENSION = ".grp"; - -class GRP : public PackFile { -public: - /// Open a GRP file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); - - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; - - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; - - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; - -protected: - GRP(const std::string& fullFilePath_, PackFileOptions options_); - - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; - -private: - VPKPP_REGISTER_PACKFILE_OPEN(GRP_EXTENSION, &GRP::open); -}; - -} // namespace vpkpp diff --git a/include/vpkpp/format/PAK.h b/include/vpkpp/format/PAK.h index ecc306fc5..68e3486f6 100644 --- a/include/vpkpp/format/PAK.h +++ b/include/vpkpp/format/PAK.h @@ -13,18 +13,18 @@ constexpr std::string_view PAK_EXTENSION = ".pak"; class PAK : public PackFile { public: /// Open a PAK file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::vector getSupportedEntryAttributes() const override; protected: PAK(const std::string& fullFilePath_, PackFileOptions options_); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; private: VPKPP_REGISTER_PACKFILE_OPEN(PAK_EXTENSION, &PAK::open); diff --git a/include/vpkpp/format/PCK.h b/include/vpkpp/format/PCK.h index 780abdf9f..5137f17b2 100644 --- a/include/vpkpp/format/PCK.h +++ b/include/vpkpp/format/PCK.h @@ -28,15 +28,15 @@ class PCK : public PackFile { public: /// Open a PCK file (potentially embedded in an executable) - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool isCaseSensitive() const noexcept override { return true; } - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::vector getSupportedEntryAttributes() const override; @@ -45,7 +45,7 @@ class PCK : public PackFile { protected: PCK(const std::string& fullFilePath_, PackFileOptions options_); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; Header header{}; diff --git a/include/vpkpp/format/VPK.h b/include/vpkpp/format/VPK.h index 719eac416..2db26279b 100644 --- a/include/vpkpp/format/VPK.h +++ b/include/vpkpp/format/VPK.h @@ -55,24 +55,24 @@ class VPK : public PackFile { struct FreedChunk { uint64_t offset; uint64_t length; - uint16_t archiveIndex; + uint32_t archiveIndex; }; public: // Accepts the full entry path (parent directory + filename), returns saveToDir and preloadBytes - using EntryCreationCallback = std::function(const std::string& fullEntryPath)>; + using EntryCreationCallback = std::function(const std::string& path)>; /// Create a new directory VPK file - must end in "_dir.vpk"! This is not enforced but STRONGLY recommended static std::unique_ptr createEmpty(const std::string& path, PackFileOptions options = {}); /// Create a new directory VPK file from a directory, the contents of the directory will be present in the root VPK directory. (See above comment) - static std::unique_ptr createFromDirectory(const std::string& vpkPath, const std::string& contentPath, bool saveToDir = false, PackFileOptions options = {}, const Callback& bakeCallback = nullptr); + static std::unique_ptr createFromDirectory(const std::string& vpkPath, const std::string& contentPath, bool saveToDir = false, PackFileOptions options = {}, const EntryCallback& bakeCallback = nullptr); /// Create a new directory VPK file from a directory, the contents of the directory will be present in the root VPK directory. Each entry's properties is determined by a callback. (See above comment) - static std::unique_ptr createFromDirectoryProcedural(const std::string& vpkPath, const std::string& contentPath, const EntryCreationCallback& creationCallback, PackFileOptions options = {}, const Callback& bakeCallback = nullptr); + static std::unique_ptr createFromDirectoryProcedural(const std::string& vpkPath, const std::string& contentPath, const EntryCreationCallback& creationCallback, PackFileOptions options = {}, const EntryCallback& bakeCallback = nullptr); /// Open a directory VPK file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool hasEntryChecksums() const override { return true; @@ -88,11 +88,13 @@ class VPK : public PackFile { [[nodiscard]] bool verifyPackFileSignature() const override; - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; bool removeEntry(const std::string& filename_) override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + std::size_t removeDirectory(const std::string& dirName_) override; + + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::string getTruncatedFilestem() const override; @@ -120,13 +122,13 @@ class VPK : public PackFile { protected: VPK(const std::string& fullFilePath_, PackFileOptions options_); - [[nodiscard]] static std::unique_ptr openInternal(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr openInternal(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; [[nodiscard]] uint32_t getHeaderLength() const; - int numArchives = -1; + uint32_t numArchives = -1; uint32_t currentlyFilledChunkSize = 0; std::vector freedChunks; diff --git a/include/vpkpp/format/VPK_VTMB.h b/include/vpkpp/format/VPK_VTMB.h index a50806596..8268351ed 100644 --- a/include/vpkpp/format/VPK_VTMB.h +++ b/include/vpkpp/format/VPK_VTMB.h @@ -12,11 +12,11 @@ constexpr std::string_view VPK_VTMB_EXTENSION = ".vpk"; class VPK_VTMB : public PackFile { public: /// Open Vampire: The Masquerade - Bloodlines VPK files - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::string getTruncatedFilestem() const override; @@ -25,12 +25,12 @@ class VPK_VTMB : public PackFile { protected: VPK_VTMB(const std::string& fullFilePath_, PackFileOptions options_); - void openNumbered(uint16_t archiveIndex, const std::string& path, const Callback& callback); + void openNumbered(uint32_t archiveIndex, const std::string& path, const EntryCallback& callback); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; - std::vector knownArchives; - uint16_t currentArchive = 0; + std::vector knownArchives; + uint32_t currentArchive = 0; private: VPKPP_REGISTER_PACKFILE_OPEN(VPK_VTMB_EXTENSION, &VPK_VTMB::open); diff --git a/include/vpkpp/format/ZIP.h b/include/vpkpp/format/ZIP.h index a0eafb11d..be833dfa4 100644 --- a/include/vpkpp/format/ZIP.h +++ b/include/vpkpp/format/ZIP.h @@ -12,7 +12,7 @@ class ZIP : public PackFile { ~ZIP() override; /// Open a ZIP file - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); [[nodiscard]] constexpr bool hasEntryChecksums() const override { return true; @@ -24,9 +24,9 @@ class ZIP : public PackFile { return true; } - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; [[nodiscard]] std::vector getSupportedEntryAttributes() const override; @@ -39,9 +39,9 @@ class ZIP : public PackFile { protected: ZIP(const std::string& fullFilePath_, PackFileOptions options_); - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; - bool bakeTempZip(const std::string& writeZipPath, const Callback& callback); + bool bakeTempZip(const std::string& writeZipPath, const EntryCallback& callback); bool openZIP(std::string_view path); diff --git a/src/sourcepp/String.cpp b/src/sourcepp/String.cpp index 31e98597d..c014d7767 100644 --- a/src/sourcepp/String.cpp +++ b/src/sourcepp/String.cpp @@ -153,7 +153,7 @@ std::string string::generateUUIDv4() { return out; } -std::string string::padNumber(int number, int width, char pad) { +std::string string::padNumber(int64_t number, int width, char pad) { auto numStr = std::to_string(number); return std::string(width - std::min(width, numStr.length()), pad) + numStr; } diff --git a/src/vpkpp/Entry.cpp b/src/vpkpp/Entry.cpp deleted file mode 100644 index 254d37888..000000000 --- a/src/vpkpp/Entry.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#include - -using namespace vpkpp; - -std::string Entry::getParentPath() const { - return std::filesystem::path{this->path}.parent_path().string(); -} - -std::string Entry::getFilename() const { - return std::filesystem::path{this->path}.filename().string(); -} - -std::string Entry::getStem() const { - return std::filesystem::path{this->path}.stem().string(); -} - -std::string Entry::getExtension() const { - auto ext = std::filesystem::path{this->path}.extension().string(); - if (!ext.empty() && ext.at(0) == '.') { - ext = ext.substr(1); - } - return ext; -} diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index fddc465ae..fa180f458 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -74,21 +73,21 @@ void replace(std::string& line, const std::string& oldString, const std::string& } } -void fixFilePathForWindows(std::string& filePath) { +void fixFilePathForWindows(std::string& path) { // Remove invalid characters - ::replace(filePath, "<", "_LT_"); - ::replace(filePath, "<", "_LT_"); - ::replace(filePath, ">", "_GT_"); - ::replace(filePath, ":", "_COLON_"); - ::replace(filePath, "\"", "_QUOT_"); - ::replace(filePath, "|", "_BAR_"); - ::replace(filePath, "?", "_QMARK_"); - ::replace(filePath, "*", "_AST_"); - - std::filesystem::path path{filePath}; - auto filename = path.filename().string(); - auto extension = path.extension().string(); - auto stem = path.stem().string(); + ::replace(path, "<", "_"); + ::replace(path, "<", "_"); + ::replace(path, ">", "_"); + ::replace(path, ":", "_"); + ::replace(path, "\"", "_"); + ::replace(path, "|", "_"); + ::replace(path, "?", "_"); + ::replace(path, "*", "_"); + + std::filesystem::path filePath{path}; + auto filename = filePath.filename().string(); + auto extension = filePath.extension().string(); + auto stem = filePath.stem().string(); string::toUpper(stem); // Replace bad filenames @@ -115,9 +114,10 @@ void fixFilePathForWindows(std::string& filePath) { // Files cannot end with a period - weird if (extension == ".") { filename.pop_back(); + filename += '_'; } - filePath = (path.parent_path() / filename).string(); + path = (filePath.parent_path() / filename).string(); } #endif @@ -128,7 +128,7 @@ PackFile::PackFile(std::string fullFilePath_, PackFileOptions options_) : fullFilePath(std::move(fullFilePath_)) , options(options_) {} -std::unique_ptr PackFile::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr PackFile::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { auto extension = std::filesystem::path{path}.extension().string(); string::toLower(extension); const auto& registry = PackFile::getOpenExtensionRegistry(); @@ -170,37 +170,25 @@ bool PackFile::verifyPackFileSignature() const { return true; } -bool PackFile::hasEntry(const std::string& filename_, bool includeUnbaked) const { - return static_cast(this->findEntry(filename_, includeUnbaked)); +bool PackFile::hasEntry(const std::string& path, bool includeUnbaked) const { + return static_cast(this->findEntry(path, includeUnbaked)); } -std::optional PackFile::findEntry(const std::string& filename_, bool includeUnbaked) const { - auto filename = filename_; - string::normalizeSlashes(filename); - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - if (this->entries.contains(dir)) { - for (const Entry& entry : this->entries.at(dir)) { - if (entry.path == filename) { - return entry; - } - } +std::optional PackFile::findEntry(const std::string& path_, bool includeUnbaked) const { + auto path = this->cleanEntryPath(path_); + if (auto it = this->entries.find(path); it != this->entries.end()) { + return *it; } - if (includeUnbaked && this->unbakedEntries.contains(dir)) { - for (const Entry& unbakedEntry : this->unbakedEntries.at(dir)) { - if (unbakedEntry.path == filename) { - return unbakedEntry; - } + if (includeUnbaked) { + if (auto it = this->unbakedEntries.find(path); it != this->unbakedEntries.end()) { + return *it; } } return std::nullopt; } -std::optional PackFile::readEntryText(const Entry& entry) const { - auto bytes = this->readEntry(entry); +std::optional PackFile::readEntryText(const std::string& path) const { + auto bytes = this->readEntry(path); if (!bytes) { return std::nullopt; } @@ -213,23 +201,24 @@ std::optional PackFile::readEntryText(const Entry& entry) const { return out; } -void PackFile::addEntry(const std::string& filename_, const std::string& pathToFile, EntryOptions options_) { +void PackFile::addEntry(const std::string& entryPath, const std::string& filepath, EntryOptions options_) { if (this->isReadOnly()) { return; } - auto buffer = fs::readFileBuffer(pathToFile); + auto buffer = fs::readFileBuffer(filepath); Entry entry{}; entry.unbaked = true; entry.unbakedUsingByteBuffer = false; - entry.unbakedData = pathToFile; + entry.unbakedData = filepath; - Entry& finalEntry = this->addEntryInternal(entry, filename_, buffer, options_); - finalEntry.unbakedData = pathToFile; + auto path = this->cleanEntryPath(entryPath); + this->addEntryInternal(entry, path, buffer, options_); + this->unbakedEntries.emplace(path, entry); } -void PackFile::addEntry(const std::string& filename_, std::vector&& buffer, EntryOptions options_) { +void PackFile::addEntry(const std::string& path, std::vector&& buffer, EntryOptions options_) { if (this->isReadOnly()) { return; } @@ -238,70 +227,67 @@ void PackFile::addEntry(const std::string& filename_, std::vector&& b entry.unbaked = true; entry.unbakedUsingByteBuffer = true; - Entry& finalEntry = this->addEntryInternal(entry, filename_, buffer, options_); - finalEntry.unbakedData = std::move(buffer); + auto path_ = this->cleanEntryPath(path); + this->addEntryInternal(entry, path_, buffer, options_); + entry.unbakedData = std::move(buffer); + this->unbakedEntries.emplace(path_, entry); } -void PackFile::addEntry(const std::string& filename_, const std::byte* buffer, uint64_t bufferLen, EntryOptions options_) { +void PackFile::addEntry(const std::string& path, const std::byte* buffer, uint64_t bufferLen, EntryOptions options_) { std::vector data; if (buffer && bufferLen > 0) { data.resize(bufferLen); std::memcpy(data.data(), buffer, bufferLen); } - this->addEntry(filename_, std::move(data), options_); + this->addEntry(path, std::move(data), options_); } -bool PackFile::removeEntry(const std::string& filename_) { +bool PackFile::removeEntry(const std::string& path_) { if (this->isReadOnly()) { return false; } - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); + auto path = this->cleanEntryPath(path_); + if (this->entries.find(path) != this->entries.end()) { + this->entries.erase(path); + return true; } - auto [dir, name] = splitFilenameAndParentDir(filename); - - // Check unbaked entries first - if (this->unbakedEntries.contains(dir)) { - for (auto& [preexistingDir, unbakedEntryVec] : this->unbakedEntries) { - if (preexistingDir != dir) { - continue; - } - for (auto it = unbakedEntryVec.begin(); it != unbakedEntryVec.end(); ++it) { - if (it->path == filename) { - unbakedEntryVec.erase(it); - return true; - } - } - } + if (this->unbakedEntries.find(path) != this->unbakedEntries.end()) { + this->unbakedEntries.erase(path); + return true; } + return false; +} - // If it's not in regular entries either you can't remove it! - if (!this->entries.contains(dir)) { +std::size_t PackFile::removeDirectory(const std::string& dirName_) { + if (this->isReadOnly()) { return false; } - for (auto it = this->entries.at(dir).begin(); it != this->entries.at(dir).end(); ++it) { - if (it->path == filename) { - this->entries.at(dir).erase(it); - return true; - } + auto dirName = this->cleanEntryPath(dirName_); + dirName += '/'; + if (dirName == "/") { + const auto size = this->getEntryCount(); + this->entries.clear(); + this->unbakedEntries.clear(); + return size; } - return false; + std::size_t count = this->entries.erase_prefix(dirName); + count += this->unbakedEntries.erase_prefix(dirName); + return count; } -bool PackFile::extractEntry(const Entry& entry, const std::string& filePath) const { - if (filePath.empty()) { +bool PackFile::extractEntry(const std::string& entryPath, const std::string& filepath) const { + if (filepath.empty()) { return false; } - auto data = this->readEntry(entry); + auto data = this->readEntry(entryPath); if (!data) { return false; } - FileStream stream{filePath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT}; + FileStream stream{filepath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT}; if (!stream) { return false; } @@ -310,48 +296,28 @@ bool PackFile::extractEntry(const Entry& entry, const std::string& filePath) con return true; } -bool PackFile::extractDirectory(const std::string& dir, const std::string& outputDir) const { - if (dir.empty() || dir == "/") { +bool PackFile::extractDirectory(const std::string& dir_, const std::string& outputDir) const { + auto dir = this->cleanEntryPath(dir_); + dir += '/'; + if (dir == "/") { return this->extractAll(outputDir, false); } - auto testDir = dir; - if (testDir.starts_with('/')) { - testDir = testDir.substr(1); - } - if (testDir.ends_with('/')) { - testDir.pop_back(); - } - auto outputDirPath = std::filesystem::path{outputDir} / std::filesystem::path{testDir}.filename(); + auto outputDirPath = std::filesystem::path{outputDir} / std::filesystem::path{dir}.filename(); bool noneFailed = true; - for (const auto& [dir_, bakedEntries_] : this->getBakedEntries()) { - if (!dir_.starts_with(testDir)) { - continue; - } - for (const auto& entry : bakedEntries_) { - std::string entryPath = entry.path.substr(testDir.length() + 1); -#ifdef _WIN32 - ::fixFilePathForWindows(entryPath); -#endif - if (!this->extractEntry(entry, (outputDirPath / entryPath).string())) { - noneFailed = false; - } - } - } - for (const auto& [dir_, unbakedEntries_] : this->getUnbakedEntries()) { - if (!dir_.starts_with(testDir)) { - continue; + this->runForAllEntries([this, &dir, &outputDirPath, &noneFailed](const std::string& path, const Entry& entry) { + if (!path.starts_with(dir)) { + return; } - for (const auto& entry : unbakedEntries_) { - std::string entryPath = entry.path.substr(testDir.length() + 1); + + std::string outputPath = path.substr(dir.length()); #ifdef _WIN32 - ::fixFilePathForWindows(entryPath); + ::fixFilePathForWindows(outputPath); #endif - if (!this->extractEntry(entry, (outputDirPath / entryPath).string())) { - noneFailed = false; - } + if (!this->extractEntry(path, (outputDirPath / outputPath).string())) { + noneFailed = false; } - } + }); return noneFailed; } @@ -365,53 +331,31 @@ bool PackFile::extractAll(const std::string& outputDir, bool createUnderPackFile outputDirPath /= this->getTruncatedFilestem(); } bool noneFailed = true; - for (const auto& [dir, bakedEntries_] : this->getBakedEntries()) { - for (const auto& entry : bakedEntries_) { - std::string entryPath = entry.path; -#ifdef _WIN32 - ::fixFilePathForWindows(entryPath); -#endif - if (!this->extractEntry(entry, (outputDirPath / entryPath).string())) { - noneFailed = false; - } - } - } - for (const auto& [dir, unbakedEntries_] : this->getUnbakedEntries()) { - for (const auto& entry : unbakedEntries_) { - std::string entryPath = entry.path; + this->runForAllEntries([this, &outputDirPath, &noneFailed](const std::string& path, const Entry& entry) { + std::string entryPath = path; #ifdef _WIN32 - ::fixFilePathForWindows(entryPath); + ::fixFilePathForWindows(entryPath); #endif - if (!this->extractEntry(entry, (outputDirPath / entryPath).string())) { - noneFailed = false; - } + if (!this->extractEntry(path, (outputDirPath / entryPath).string())) { + noneFailed = false; } - } + }); return noneFailed; } -bool PackFile::extractAll(const std::string& outputDir, const std::function& predicate) const { +bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& predicate) const { if (outputDir.empty() || !predicate) { return false; } // Get list of paths - std::vector saveEntries; - for (const auto& [dir, bakedEntries_] : this->getBakedEntries()) { - for (const auto& entry : bakedEntries_) { - if (predicate(entry)) { - saveEntries.push_back(entry); - } - } - } - for (const auto& [dir, unbakedEntries_] : this->getUnbakedEntries()) { - for (const auto& entry : unbakedEntries_) { - if (predicate(entry)) { - saveEntries.push_back(entry); - } + std::vector saveEntryPaths; + this->runForAllEntries([&predicate, &saveEntryPaths](const std::string& path, const Entry& entry) { + if (predicate(path, entry)) { + saveEntryPaths.push_back(path); } - } - if (saveEntries.empty()) { + }); + if (saveEntryPaths.empty()) { return false; } @@ -419,9 +363,9 @@ bool PackFile::extractAll(const std::string& outputDir, const std::function rootDirList; { std::vector> pathSplits; - pathSplits.reserve(saveEntries.size()); - for (const auto& entry : saveEntries) { - pathSplits.push_back(::splitPath(entry.path)); + pathSplits.reserve(saveEntryPaths.size()); + for (const auto& path : saveEntryPaths) { + pathSplits.push_back(::splitPath(path)); } while (true) { bool allTheSame = true; @@ -450,37 +394,61 @@ bool PackFile::extractAll(const std::string& outputDir, const std::functionextractEntry(entry, (outputDirPath / entryPath).string())) { + if (!this->extractEntry(entryPath, (outputDirPath / entryPath).string())) { noneFailed = false; } } return noneFailed; } -const std::unordered_map>& PackFile::getBakedEntries() const { +const PackFile::EntryTrie& PackFile::getBakedEntries() const { return this->entries; } -const std::unordered_map>& PackFile::getUnbakedEntries() const { +const PackFile::EntryTrie& PackFile::getUnbakedEntries() const { return this->unbakedEntries; } std::size_t PackFile::getEntryCount(bool includeUnbaked) const { std::size_t count = 0; - for (const auto& [directory, entries_] : this->entries) { - count += entries_.size(); + count += this->entries.size(); + if (includeUnbaked) { + count += this->unbakedEntries.size(); + } + return count; +} + +void PackFile::runForAllEntries(const EntryCallback& operation, bool includeUnbaked) const { + std::string key; + for (auto entry = this->entries.cbegin(); entry != this->entries.cend(); ++entry) { + entry.key(key); + operation(key, entry.value()); } if (includeUnbaked) { - for (const auto& [directory, entries_] : this->unbakedEntries) { - count += entries_.size(); + for (auto entry = this->unbakedEntries.cbegin(); entry != this->unbakedEntries.cend(); ++entry) { + entry.key(key); + operation(key, entry.value()); + } + } +} + +void PackFile::runForAllEntries(const std::function& operation, bool includeUnbaked) { + std::string key; + for (auto entry = this->entries.begin(); entry != this->entries.end(); ++entry) { + entry.key(key); + operation(key, entry.value()); + } + if (includeUnbaked) { + for (auto entry = this->unbakedEntries.begin(); entry != this->unbakedEntries.end(); ++entry) { + entry.key(key); + operation(key, entry.value()); } } - return count; } std::string_view PackFile::getFilepath() const { @@ -516,7 +484,7 @@ PackFile::operator std::string() const { return this->getTruncatedFilename(); } -std::string PackFile::escapeEntryPath(const std::string& path) { +std::string PackFile::escapeEntryPathForWrite(const std::string& path) { #ifdef _WIN32 auto copy = path; ::fixFilePathForWindows(copy); @@ -537,26 +505,15 @@ std::vector PackFile::getSupportedFileTypes() { std::vector PackFile::verifyEntryChecksumsUsingCRC32() const { std::vector out; - for (const auto& [dir, entryList] : this->entries) { - for (const auto& entry : entryList) { - if (!entry.crc32) { - continue; - } - if (auto data = this->readEntry(entry); !data || crypto::computeCRC32(*data) != entry.crc32) { - out.push_back(entry.path); - } + this->runForAllEntries([this, &out](const std::string& path, const Entry& entry) { + if (!entry.crc32) { + return; } - } - for (const auto& [dir, entryList] : this->unbakedEntries) { - for (const auto& entry : entryList) { - if (!entry.crc32) { - continue; - } - if (auto data = this->readEntry(entry); !data || crypto::computeCRC32(*data) != entry.crc32) { - out.push_back(entry.path); - } + auto data = this->readEntry(path); + if (!data || crypto::computeCRC32(*data) != entry.crc32) { + out.push_back(path); } - } + }, false); // Don't include unbaked since we probably calculate those manually on write return out; } @@ -577,20 +534,17 @@ std::string PackFile::getBakeOutputDir(const std::string& outputDir) const { } void PackFile::mergeUnbakedEntries() { - for (auto& [dir, unbakedEntriesAndData] : this->unbakedEntries) { - for (Entry& unbakedEntry : unbakedEntriesAndData) { - if (!this->entries.contains(dir)) { - this->entries[dir] = {}; - } + std::string key; + for (auto entry = this->unbakedEntries.begin(); entry != this->unbakedEntries.end(); ++entry) { + entry.key(key); - unbakedEntry.unbaked = false; + entry->unbaked = false; - // Clear any data that might be stored in it - unbakedEntry.unbakedUsingByteBuffer = false; - unbakedEntry.unbakedData = ""; + // Clear any data that might be stored in it + entry->unbakedUsingByteBuffer = false; + entry->unbakedData = ""; - this->entries.at(dir).push_back(unbakedEntry); - } + this->entries.insert(key, *entry); } this->unbakedEntries.clear(); } @@ -600,49 +554,32 @@ void PackFile::setFullFilePath(const std::string& outputDir) { this->fullFilePath = outputDir + '/' + this->getFilename(); } -std::pair PackFile::splitFilenameAndParentDir(const std::string& filename) { - auto name = filename; - string::normalizeSlashes(name); - - auto lastSeparator = name.rfind('/'); - auto dir = lastSeparator != std::string::npos ? name.substr(0, lastSeparator) : ""; - name = filename.substr(lastSeparator + 1); - - return {dir, name}; +std::string PackFile::cleanEntryPath(const std::string& path) const { + auto path_ = path; + string::normalizeSlashes(path_, true); + if (!this->isCaseSensitive()) { + string::toLower(path_); + } + return path; } Entry PackFile::createNewEntry() { return {}; } -const std::variant>& PackFile::getEntryUnbakedData(const Entry& entry) { - return entry.unbakedData; -} - -bool PackFile::isEntryUnbakedUsingByteBuffer(const Entry& entry) { - return entry.unbakedUsingByteBuffer; -} - -std::optional> PackFile::readUnbakedEntry(const Entry& entry) const { +std::optional> PackFile::readUnbakedEntry(const Entry& entry) { if (!entry.unbaked) { return std::nullopt; } // Get the stored data - for (const auto& [unbakedEntryDir, unbakedEntryList] : this->unbakedEntries) { - for (const auto& unbakedEntry : unbakedEntryList) { - if (unbakedEntry.path == entry.path) { - std::vector unbakedData; - if (isEntryUnbakedUsingByteBuffer(unbakedEntry)) { - unbakedData = std::get>(getEntryUnbakedData(unbakedEntry)); - } else { - unbakedData = fs::readFileBuffer(std::get(getEntryUnbakedData(unbakedEntry))); - } - return unbakedData; - } - } + std::vector unbakedData; + if (entry.unbakedUsingByteBuffer) { + unbakedData = std::get>(entry.unbakedData); + } else { + unbakedData = fs::readFileBuffer(std::get(entry.unbakedData)); } - return std::nullopt; + return unbakedData; } std::unordered_map>& PackFile::getOpenExtensionRegistry() { @@ -667,10 +604,10 @@ PackFileReadOnly::operator std::string() const { return PackFile::operator std::string() + " (Read-Only)"; } -Entry& PackFileReadOnly::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - return entry; // Stubbed +void PackFileReadOnly::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { + // Stubbed } -bool PackFileReadOnly::bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) { +bool PackFileReadOnly::bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) { return false; // Stubbed } diff --git a/src/vpkpp/_vpkpp.cmake b/src/vpkpp/_vpkpp.cmake index efd93eb86..32a025ab1 100644 --- a/src/vpkpp/_vpkpp.cmake +++ b/src/vpkpp/_vpkpp.cmake @@ -1,11 +1,10 @@ add_pretty_parser(vpkpp - DEPS bsppp cryptopp::cryptopp kvpp MINIZIP::minizip + DEPS bsppp cryptopp::cryptopp kvpp MINIZIP::minizip tsl::hat_trie PRECOMPILED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/BSP.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/FPX.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/GCF.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/GMA.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/GRP.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/PAK.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/PCK.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/VPK.h" @@ -22,11 +21,9 @@ add_pretty_parser(vpkpp "${CMAKE_CURRENT_LIST_DIR}/format/FPX.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GCF.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GMA.cpp" - "${CMAKE_CURRENT_LIST_DIR}/format/GRP.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/PAK.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/PCK.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/VPK.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/VPK_VTMB.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/ZIP.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Entry.cpp" "${CMAKE_CURRENT_LIST_DIR}/PackFile.cpp") diff --git a/src/vpkpp/format/BSP.cpp b/src/vpkpp/format/BSP.cpp index 410a31695..bae055007 100644 --- a/src/vpkpp/format/BSP.cpp +++ b/src/vpkpp/format/BSP.cpp @@ -24,7 +24,7 @@ BSP::BSP(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::BSP; } -std::unique_ptr BSP::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr BSP::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -75,38 +75,25 @@ std::unique_ptr BSP::open(const std::string& path, PackFileOptions opt continue; } - Entry entry = createNewEntry(); - entry.path = fileInfo->filename; - string::normalizeSlashes(entry.path, true); - if (!bsp->isCaseSensitive()) { - string::toLower(entry.path); - } + auto entryPath = bsp->cleanEntryPath(fileInfo->filename); + Entry entry = createNewEntry(); entry.flags = fileInfo->compression_method; entry.length = fileInfo->uncompressed_size; entry.compressedLength = fileInfo->compressed_size; entry.crc32 = fileInfo->crc; - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - string::normalizeSlashes(parentDir, true); - if (!bsp->isCaseSensitive()) { - string::toLower(parentDir); - } - - if (!bsp->entries.contains(parentDir)) { - bsp->entries[parentDir] = {}; - } - bsp->entries[parentDir].push_back(entry); + bsp->entries.emplace(entryPath, entry); if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } return packFile; } -bool BSP::bake(const std::string& outputDir_, const Callback& callback) { +bool BSP::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); diff --git a/src/vpkpp/format/FPX.cpp b/src/vpkpp/format/FPX.cpp index b7cd897d8..697736c40 100644 --- a/src/vpkpp/format/FPX.cpp +++ b/src/vpkpp/format/FPX.cpp @@ -11,7 +11,7 @@ FPX::FPX(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::FPX; } -std::unique_ptr FPX::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr FPX::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { auto fpx = FPX::openInternal(path, options, callback); if (!fpx && path.length() > 8) { // If it just tried to load a numbered archive, let's try to load the directory FPX @@ -22,7 +22,7 @@ std::unique_ptr FPX::open(const std::string& path, PackFileOptions opt return fpx; } -std::unique_ptr FPX::openInternal(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr FPX::openInternal(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -64,9 +64,6 @@ std::unique_ptr FPX::openInternal(const std::string& path, PackFileOpt } else { fullDir = directory; } - if (!fpx->entries.contains(fullDir)) { - fpx->entries[fullDir] = {}; - } // Files while (true) { @@ -77,14 +74,16 @@ std::unique_ptr FPX::openInternal(const std::string& path, PackFileOpt Entry entry = createNewEntry(); + std::string entryPath; if (extension == " ") { - entry.path = fullDir.empty() ? "" : fullDir + '/'; - entry.path += entryName; + entryPath = fullDir.empty() ? "" : fullDir + '/'; + entryPath += entryName; } else { - entry.path = fullDir.empty() ? "" : fullDir + '/'; - entry.path += entryName + '.'; - entry.path += extension; + entryPath = fullDir.empty() ? "" : fullDir + '/'; + entryPath += entryName + '.'; + entryPath += extension; } + entryPath = fpx->cleanEntryPath(entryPath); reader.read(entry.crc32); auto preloadedDataSize = reader.read(); @@ -98,18 +97,18 @@ std::unique_ptr FPX::openInternal(const std::string& path, PackFileOpt } if (preloadedDataSize > 0) { - entry.vpk_preloadedData = reader.read_bytes(preloadedDataSize); + entry.extraData = reader.read_bytes(preloadedDataSize); entry.length += preloadedDataSize; } - fpx->entries[fullDir].push_back(entry); - if (entry.archiveIndex != VPK_DIR_INDEX && entry.archiveIndex > fpx->numArchives) { fpx->numArchives = entry.archiveIndex; } + fpx->entries.emplace(entryPath, entry); + if (callback) { - callback(fullDir, entry); + callback(entryPath, entry); } } } diff --git a/src/vpkpp/format/GCF.cpp b/src/vpkpp/format/GCF.cpp index 34555dd1d..3755c754f 100644 --- a/src/vpkpp/format/GCF.cpp +++ b/src/vpkpp/format/GCF.cpp @@ -8,7 +8,6 @@ #include #include #include -#include using namespace sourcepp; using namespace vpkpp; @@ -18,7 +17,7 @@ GCF::GCF(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::GCF; } -std::unique_ptr GCF::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr GCF::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { // TODO: Add v5 and perhaps v4 support if (!std::filesystem::exists(path)) { @@ -129,28 +128,22 @@ std::unique_ptr GCF::open(const std::string& path, PackFileOptions opt dirname.insert(0, "/"); dirname.insert(0, current_filename_entry.filename); } - Entry gcfEntry = createNewEntry(); - string::normalizeSlashes(dirname, true); - if (!gcf->isCaseSensitive()) { - string::toLower(dirname); - string::toLower(entry.filename); - } - if (!gcf->entries.contains(dirname)) { - //printf("dirname creation: %s\n", dirname.c_str()); - gcf->entries[dirname] = {}; + + auto gcfEntryPath = gcf->cleanEntryPath(dirname); + if (!gcfEntryPath.empty()) { + gcfEntryPath += '/'; } + gcfEntryPath += entry.filename; + + Entry gcfEntry = createNewEntry(); gcfEntry.length = entry.entry_real.itemsize; - gcfEntry.path = dirname; - gcfEntry.path += dirname.empty() ? "" : "/"; - gcfEntry.path += entry.filename; gcfEntry.crc32 = entry.entry_real.fileid; // INDEX INTO THE CHECKSUM MAP VECTOR NOT CRC32!!! gcfEntry.offset = i; // THIS IS THE STRUCT INDEX NOT SOME OFFSET!!! //printf("%s\n", gcfEntry.path.c_str()); - gcf->entries[dirname].push_back(gcfEntry); - //printf("dir %s file %s\n", dirname.c_str(), entry.filename.c_str()); + gcf->entries.emplace(gcfEntryPath, gcfEntry); if (callback) { - callback(dirname, gcfEntry); + callback(gcfEntryPath, gcfEntry); } } reader.seek_in_u(currentoffset); @@ -207,43 +200,46 @@ std::unique_ptr GCF::open(const std::string& path, PackFileOptions opt std::vector GCF::verifyEntryChecksums() const { std::vector bad; - for (const auto& entryList : this->entries) { - for (const auto& entry : entryList.second) { - auto bytes = this->readEntry(entry); - if (!bytes || bytes->empty()) { - continue; - } - std::size_t tocheck = bytes->size(); - uint32_t idx = entry.crc32; - uint32_t count = this->chksum_map[idx].count; - uint32_t checksumstart = this->chksum_map[idx].firstindex; - for (int i = 0; i < count; i++) { - uint32_t csum = this->checksums[checksumstart + i]; - std::size_t toread = std::min(static_cast(0x8000), tocheck); - const auto* data = bytes->data() + (i * 0x8000); - uint32_t checksum = crypto::computeCRC32(data, toread) ^ crypto::computeAdler32(data, toread); - if (checksum != csum) { - bad.push_back(entry.path); - } - tocheck -= toread; + this->runForAllEntries([this, &bad](const std::string& path, const Entry& entry) { + auto bytes = this->readEntry(path); + if (!bytes || bytes->empty()) { + return; + } + std::size_t tocheck = bytes->size(); + uint32_t idx = entry.crc32; + uint32_t count = this->chksum_map[idx].count; + uint32_t checksumstart = this->chksum_map[idx].firstindex; + for (int i = 0; i < count; i++) { + uint32_t csum = this->checksums[checksumstart + i]; + std::size_t toread = std::min(static_cast(0x8000), tocheck); + const auto* data = bytes->data() + (i * 0x8000); + uint32_t checksum = crypto::computeCRC32(data, toread) ^ crypto::computeAdler32(data, toread); + if (checksum != csum) { + bad.push_back(path); } + tocheck -= toread; } - } + }); return bad; } -std::optional> GCF::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> GCF::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } std::vector filedata; - if (entry.length == 0) { + if (entry->length == 0) { // don't bother return filedata; } - uint32_t dir_index = entry.offset; + uint32_t dir_index = entry->offset; //printf(" extracting file: %s\n", entry.path.c_str()); std::vector toread; @@ -253,15 +249,13 @@ std::optional> GCF::readEntry(const Entry& entry) const { } } - std::sort(toread.begin(), toread.end(), [](Block lhs, Block rhs) { - return (lhs.file_data_offset < rhs.file_data_offset); - } - ); - if (toread.empty()) { //printf("could not find any directory index for %lu", entry.vpk_offset); return std::nullopt; } + std::sort(toread.begin(), toread.end(), [](const Block& lhs, const Block& rhs) { + return lhs.file_data_offset < rhs.file_data_offset; + }); FileStream stream{this->fullFilePath}; if (!stream) { @@ -269,7 +263,7 @@ std::optional> GCF::readEntry(const Entry& entry) const { return std::nullopt; } - uint64_t remaining = entry.length; + uint64_t remaining = entry->length; for (const auto& block : toread) { uint32_t currindex = block.first_data_block_index; diff --git a/src/vpkpp/format/GMA.cpp b/src/vpkpp/format/GMA.cpp index 666193df7..bd67b968f 100644 --- a/src/vpkpp/format/GMA.cpp +++ b/src/vpkpp/format/GMA.cpp @@ -6,7 +6,6 @@ #include #include -#include using namespace sourcepp; using namespace vpkpp; @@ -16,7 +15,7 @@ GMA::GMA(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::GMA; } -std::unique_ptr GMA::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr GMA::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -42,42 +41,28 @@ std::unique_ptr GMA::open(const std::string& path, PackFileOptions opt reader.read(gma->header.addonAuthor); reader.read(gma->header.addonVersion); - std::vector entries; + std::vector> entries; while (reader.read() > 0) { Entry entry = createNewEntry(); - reader.read(entry.path); - string::normalizeSlashes(entry.path, true); - if (!gma->isCaseSensitive()) { - string::toLower(entry.path); - } + auto entryPath = reader.read_string(); entry.length = reader.read(); reader.read(entry.crc32); - entries.push_back(entry); + entries.emplace_back(entryPath, entry); } // At this point we've reached the file data section, calculate the offsets and then add the entries std::size_t offset = reader.tell_in(); - for (auto& entry : entries) { + for (auto& [entryPath, entry] : entries) { entry.offset = offset; offset += entry.length; - } - for (const auto& entry : entries) { - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - string::normalizeSlashes(parentDir, true); - if (!gma->isCaseSensitive()) { - string::toLower(parentDir); - } - if (!gma->entries.contains(parentDir)) { - gma->entries[parentDir] = {}; - } - gma->entries[parentDir].push_back(entry); + gma->entries.emplace(entryPath, entry); if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } @@ -106,9 +91,14 @@ bool GMA::verifyPackFileChecksum() const { return checksum == crypto::computeCRC32(data); } -std::optional> GMA::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> GMA::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // It's baked into the file on disk @@ -116,18 +106,11 @@ std::optional> GMA::readEntry(const Entry& entry) const { if (!stream) { return std::nullopt; } - stream.seek_in_u(entry.offset); - return stream.read_bytes(entry.length); + stream.seek_in_u(entry->offset); + return stream.read_bytes(entry->length); } -Entry& GMA::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - entry.path = filename; +void GMA::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.length = buffer.size(); if (this->options.gma_writeCRCs) { entry.crc32 = crypto::computeCRC32(buffer); @@ -135,36 +118,23 @@ Entry& GMA::addEntryInternal(Entry& entry, const std::string& filename_, std::ve // Offset will be reset when it's baked entry.offset = 0; - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool GMA::bake(const std::string& outputDir_, const Callback& callback) { +bool GMA::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); // Reconstruct data for ease of access - std::vector entriesToBake; - for (auto& [entryDir, entryList] : this->entries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } - for (auto& [entryDir, entryList] : this->unbakedEntries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } + std::vector> entriesToBake; + this->runForAllEntries([&entriesToBake](const std::string& path, Entry& entry) { + entriesToBake.emplace_back(path, &entry); + }); // Read data before overwriting, we don't know if we're writing to ourself std::vector fileData; - for (auto* entry : entriesToBake) { - if (auto binData = this->readEntry(*entry)) { + for (auto& [path, entry] : entriesToBake) { + if (auto binData = this->readEntry(path)) { fileData.insert(fileData.end(), binData->begin(), binData->end()); } else { entry->length = 0; @@ -189,20 +159,20 @@ bool GMA::bake(const std::string& outputDir_, const Callback& callback) { // File tree for (uint32_t i = 1; i <= entriesToBake.size(); i++) { stream.write(i); - auto* entry = entriesToBake[i - 1]; - stream.write(entry->path); + const auto& [path, entry] = entriesToBake[i - 1]; + stream.write(path); stream.write(entry->length); stream.write(this->options.gma_writeCRCs ? entry->crc32 : 0); if (callback) { - callback(entry->getParentPath(), *entry); + callback(path, *entry); } } - stream.write(static_cast(0)); + stream.write(0); // Fix offsets std::size_t offset = stream.tell_out(); - for (auto* entry : entriesToBake) { + for (auto& [path, entry] : entriesToBake) { entry->offset = offset; offset += entry->length; } diff --git a/src/vpkpp/format/GRP.cpp b/src/vpkpp/format/GRP.cpp deleted file mode 100644 index 48ccc5c6b..000000000 --- a/src/vpkpp/format/GRP.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include - -#include - -#include - -#include -#include - -using namespace sourcepp; -using namespace vpkpp; - -GRP::GRP(const std::string& fullFilePath_, PackFileOptions options_) - : PackFile(fullFilePath_, options_) { - this->type = PackFileType::GRP; -} - -std::unique_ptr GRP::open(const std::string& path, PackFileOptions options, const Callback& callback) { - if (!std::filesystem::exists(path)) { - // File does not exist - return nullptr; - } - - auto* grp = new GRP{path, options}; - auto packFile = std::unique_ptr(grp); - - FileStream reader{grp->fullFilePath}; - reader.seek_in(0); - - auto signature = reader.read_bytes(); - for (int i = 0; i < signature.size(); i++) { - if (static_cast(signature[i]) != GRP_SIGNATURE[i]) { - // File is not a GRP - return nullptr; - } - } - - auto fileCount = reader.read(); - - std::vector entries; - for (int i = 0; i < fileCount; i++) { - Entry entry = createNewEntry(); - - reader.read(entry.path, GRP_FILENAME_MAX_SIZE); - string::normalizeSlashes(entry.path, true); - if (!grp->isCaseSensitive()) { - string::toLower(entry.path); - } - - entry.length = reader.read(); - - entries.push_back(entry); - } - - // At this point we've reached the file data section, calculate the offsets and then add the entries - std::size_t offset = reader.tell_in(); - if (!grp->entries.contains("")) { - grp->entries[""] = {}; - } - for (auto& entry : entries) { - entry.offset = offset; - offset += entry.length; - - grp->entries[""].push_back(entry); - - if (callback) { - callback("", entry); - } - } - - return packFile; -} - -std::optional> GRP::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); - } - - // It's baked into the file on disk - FileStream stream{this->fullFilePath}; - if (!stream) { - return std::nullopt; - } - stream.seek_in_u(entry.offset); - return stream.read_bytes(entry.length); -} - -Entry& GRP::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - - entry.path = filename; - entry.length = buffer.size(); - - // Offset will be reset when it's baked - entry.offset = 0; - - if (!this->unbakedEntries.contains("")) { - this->unbakedEntries[""] = {}; - } - this->unbakedEntries.at("").push_back(entry); - return this->unbakedEntries.at("").back(); -} - -bool GRP::bake(const std::string& outputDir_, const Callback& callback) { - // Get the proper file output folder - std::string outputDir = this->getBakeOutputDir(outputDir_); - std::string outputPath = outputDir + '/' + this->getFilename(); - - // Reconstruct data for ease of access - std::vector entriesToBake; - for (auto& [entryDir, entryList] : this->entries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } - for (auto& [entryDir, entryList] : this->unbakedEntries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } - - // Read data before overwriting, we don't know if we're writing to ourself - std::vector fileData; - for (auto* entry : entriesToBake) { - if (auto binData = this->readEntry(*entry)) { - fileData.insert(fileData.end(), binData->begin(), binData->end()); - } else { - entry->length = 0; - } - } - - { - FileStream stream{outputPath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT}; - stream.seek_out(0); - - // Signature - stream.write(std::string{GRP_SIGNATURE}, false); - - // Number of files - stream.write(static_cast(entriesToBake.size())); - - // File tree - for (auto entry : entriesToBake) { - stream.write(entry->path, false, GRP_FILENAME_MAX_SIZE); - stream.write(static_cast(entry->length)); - - if (callback) { - callback(entry->getParentPath(), *entry); - } - } - - // Fix offsets - std::size_t offset = stream.tell_out(); - for (auto* entry : entriesToBake) { - entry->offset = offset; - offset += entry->length; - } - - // File data - stream.write(fileData); - } - - // Clean up - this->mergeUnbakedEntries(); - PackFile::setFullFilePath(outputDir); - return true; -} - -std::vector GRP::getSupportedEntryAttributes() const { - using enum Attribute; - return {LENGTH}; -} diff --git a/src/vpkpp/format/PAK.cpp b/src/vpkpp/format/PAK.cpp index 5e6dcc346..2482a20dc 100644 --- a/src/vpkpp/format/PAK.cpp +++ b/src/vpkpp/format/PAK.cpp @@ -15,7 +15,7 @@ PAK::PAK(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::PAK; } -std::unique_ptr PAK::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr PAK::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -40,37 +40,29 @@ std::unique_ptr PAK::open(const std::string& path, PackFileOptions opt for (uint32_t i = 0; i < fileCount; i++) { Entry entry = createNewEntry(); - reader.read(entry.path, PAK_FILENAME_MAX_SIZE); - string::normalizeSlashes(entry.path, true); - if (!pak->isCaseSensitive()) { - string::toLower(entry.path); - } + auto entryPath = pak->cleanEntryPath(reader.read_string(PAK_FILENAME_MAX_SIZE)); entry.offset = reader.read(); entry.length = reader.read(); - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - string::normalizeSlashes(parentDir, true); - if (!pak->isCaseSensitive()) { - string::toLower(parentDir); - } - - if (!pak->entries.contains(parentDir)) { - pak->entries[parentDir] = {}; - } - pak->entries[parentDir].push_back(entry); + pak->entries.emplace(entryPath, entry); if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } return packFile; } -std::optional> PAK::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> PAK::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // It's baked into the file on disk @@ -78,52 +70,32 @@ std::optional> PAK::readEntry(const Entry& entry) const { if (!stream) { return std::nullopt; } - stream.seek_in_u(entry.offset); - return stream.read_bytes(entry.length); + stream.seek_in_u(entry->offset); + return stream.read_bytes(entry->length); } -Entry& PAK::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - entry.path = filename; +void PAK::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.length = buffer.size(); // Offset will be reset when it's baked entry.offset = 0; - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool PAK::bake(const std::string& outputDir_, const Callback& callback) { +bool PAK::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); // Reconstruct data for ease of access - std::vector entriesToBake; - for (auto& [entryDir, entryList] : this->entries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } - for (auto& [entryDir, entryList] : this->unbakedEntries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } + std::vector> entriesToBake; + this->runForAllEntries([&entriesToBake](const std::string& path, Entry& entry) { + entriesToBake.emplace_back(path, &entry); + }); // Read data before overwriting, we don't know if we're writing to ourself std::vector fileData; - for (auto* entry : entriesToBake) { - if (auto binData = this->readEntry(*entry)) { + for (auto& [path, entry] : entriesToBake) { + if (auto binData = this->readEntry(path)) { entry->offset = fileData.size(); fileData.insert(fileData.end(), binData->begin(), binData->end()); @@ -147,13 +119,13 @@ bool PAK::bake(const std::string& outputDir_, const Callback& callback) { stream.write(directorySize); // Directory - for (auto entry : entriesToBake) { - stream.write(entry->path, false, PAK_FILENAME_MAX_SIZE); + for (const auto& [path, entry] : entriesToBake) { + stream.write(path, false, PAK_FILENAME_MAX_SIZE); stream.write(static_cast(entry->offset + directoryIndex + directorySize)); stream.write(static_cast(entry->length)); if (callback) { - callback(entry->getParentPath(), *entry); + callback(path, *entry); } } diff --git a/src/vpkpp/format/PCK.cpp b/src/vpkpp/format/PCK.cpp index 376a4e14b..60b4ab76c 100644 --- a/src/vpkpp/format/PCK.cpp +++ b/src/vpkpp/format/PCK.cpp @@ -6,7 +6,6 @@ #include #include -#include using namespace sourcepp; using namespace vpkpp; @@ -36,7 +35,7 @@ PCK::PCK(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::PCK; } -std::unique_ptr PCK::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr PCK::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -95,32 +94,21 @@ std::unique_ptr PCK::open(const std::string& path, PackFileOptions opt for (uint32_t i = 0; i < fileCount; i++) { Entry entry = createNewEntry(); - entry.path = reader.read_string(reader.read()); - if (entry.path.starts_with(PCK_PATH_PREFIX)) { - entry.path = entry.path.substr(PCK_PATH_PREFIX.length()); - } - string::normalizeSlashes(entry.path, true); - if (!pck->isCaseSensitive()) { - string::toLower(entry.path); + auto entryPath = pck->cleanEntryPath(reader.read_string(reader.read())); + if (entryPath.starts_with(PCK_PATH_PREFIX)) { + entryPath = entryPath.substr(PCK_PATH_PREFIX.length()); } entry.offset = reader.read() + extraEntryContentsOffset; entry.length = reader.read(); - entry.pck_md5 = reader.read_bytes<16>(); + entry.extraData = reader.read_bytes(16); if (pck->header.packVersion > 1) { entry.flags = reader.read(); } - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - string::normalizeSlashes(parentDir, true); - if (!pck->entries.contains(parentDir)) { - pck->entries[parentDir] = {}; - } - pck->entries[parentDir].push_back(entry); - if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } @@ -130,13 +118,18 @@ std::unique_ptr PCK::open(const std::string& path, PackFileOptions opt return packFile; } -std::optional> PCK::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> PCK::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // It's baked into the file on disk - if (entry.flags & FLAG_ENCRYPTED) { + if (entry->flags & FLAG_ENCRYPTED) { // File is encrypted return std::nullopt; } @@ -145,53 +138,35 @@ std::optional> PCK::readEntry(const Entry& entry) const { if (!stream) { return std::nullopt; } - stream.seek_in_u(entry.offset); - return stream.read_bytes(entry.length); + stream.seek_in_u(entry->offset); + return stream.read_bytes(entry->length); } -Entry& PCK::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - entry.path = filename; +void PCK::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.length = buffer.size(); - entry.pck_md5 = crypto::computeMD5(buffer); + + const auto md5 = crypto::computeMD5(buffer); + entry.extraData = {md5.begin(), md5.end()}; // Offset will be reset when it's baked entry.offset = 0; - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool PCK::bake(const std::string& outputDir_, const Callback& callback) { +bool PCK::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); // Reconstruct data for ease of access - std::vector entriesToBake; - for (auto& [entryDir, entryList] : this->entries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } - for (auto& [entryDir, entryList] : this->unbakedEntries) { - for (auto& entry : entryList) { - entriesToBake.push_back(&entry); - } - } + std::vector> entriesToBake; + this->runForAllEntries([&entriesToBake](const std::string& path, Entry& entry) { + entriesToBake.emplace_back(path, &entry); + }); // Read data before overwriting, we don't know if we're writing to ourself std::vector fileData; - for (auto* entry : entriesToBake) { - if (auto binData = this->readEntry(*entry)) { + for (auto& [path, entry] : entriesToBake) { + if (auto binData = this->readEntry(path)) { entry->offset = fileData.size(); fileData.insert(fileData.end(), binData->begin(), binData->end()); @@ -247,11 +222,11 @@ bool PCK::bake(const std::string& outputDir_, const Callback& callback) { // Dry-run to get the length of the directory section this->dataOffset = stream.tell_out(); - for (auto* entry : entriesToBake) { - const auto entryPath = std::string{PCK_PATH_PREFIX} + entry->path; + for (const auto& [path, entry] : entriesToBake) { + const auto entryPath = std::string{PCK_PATH_PREFIX} + path; const auto padding = ::getPadding(PCK_DIRECTORY_STRING_PADDING, static_cast(entryPath.length())); this->dataOffset += - sizeof(uint32_t) + // Path length + sizeof(uint32_t) + // Path length entryPath.length() + padding + // Path (sizeof(std::size_t) * 2) + // Offset, Length (sizeof(std::byte) * 16); // MD5 @@ -262,8 +237,8 @@ bool PCK::bake(const std::string& outputDir_, const Callback& callback) { } // Directory - for (auto* entry : entriesToBake) { - const auto entryPath = std::string{PCK_PATH_PREFIX} + entry->path; + for (const auto& [path, entry] : entriesToBake) { + const auto entryPath = std::string{PCK_PATH_PREFIX} + path; const auto padding = ::getPadding(PCK_DIRECTORY_STRING_PADDING, static_cast(entryPath.length())); stream.write(static_cast(entryPath.length() + padding)); stream.write(entryPath, false, entryPath.length() + padding); @@ -271,14 +246,14 @@ bool PCK::bake(const std::string& outputDir_, const Callback& callback) { entry->offset += this->dataOffset; stream.write(entry->offset); stream.write(entry->length); - stream.write(entry->pck_md5); + stream.write(entry->extraData); if (this->header.packVersion > 1) { stream.write(entry->flags); } if (callback) { - callback(entry->getParentPath(), *entry); + callback(path, *entry); } } diff --git a/src/vpkpp/format/VPK.cpp b/src/vpkpp/format/VPK.cpp index dca18fbb7..d691c15f0 100644 --- a/src/vpkpp/format/VPK.cpp +++ b/src/vpkpp/format/VPK.cpp @@ -76,13 +76,13 @@ std::unique_ptr VPK::createEmpty(const std::string& path, PackFileOpti return VPK::open(path, options); } -std::unique_ptr VPK::createFromDirectory(const std::string& vpkPath, const std::string& contentPath, bool saveToDir, PackFileOptions options, const Callback& bakeCallback) { +std::unique_ptr VPK::createFromDirectory(const std::string& vpkPath, const std::string& contentPath, bool saveToDir, PackFileOptions options, const EntryCallback& bakeCallback) { return VPK::createFromDirectoryProcedural(vpkPath, contentPath, [saveToDir](const std::string&) { return std::make_tuple(saveToDir, 0); }, options, bakeCallback); } -std::unique_ptr VPK::createFromDirectoryProcedural(const std::string& vpkPath, const std::string& contentPath, const EntryCreationCallback& creationCallback, PackFileOptions options, const Callback& bakeCallback) { +std::unique_ptr VPK::createFromDirectoryProcedural(const std::string& vpkPath, const std::string& contentPath, const EntryCreationCallback& creationCallback, PackFileOptions options, const EntryCallback& bakeCallback) { auto vpk = VPK::createEmpty(vpkPath, options); if (!std::filesystem::exists(contentPath) || std::filesystem::status(contentPath).type() != std::filesystem::file_type::directory) { return vpk; @@ -112,7 +112,7 @@ std::unique_ptr VPK::createFromDirectoryProcedural(const std::string& return vpk; } -std::unique_ptr VPK::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr VPK::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { auto vpk = VPK::openInternal(path, options, callback); if (!vpk && path.length() > 8) { // If it just tried to load a numbered archive, let's try to load the directory VPK @@ -123,7 +123,7 @@ std::unique_ptr VPK::open(const std::string& path, PackFileOptions opt return vpk; } -std::unique_ptr VPK::openInternal(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr VPK::openInternal(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -167,9 +167,6 @@ std::unique_ptr VPK::openInternal(const std::string& path, PackFileOpt } else { fullDir = directory; } - if (!vpk->entries.contains(fullDir)) { - vpk->entries[fullDir] = {}; - } // Files while (true) { @@ -180,18 +177,20 @@ std::unique_ptr VPK::openInternal(const std::string& path, PackFileOpt Entry entry = createNewEntry(); + std::string entryPath; if (extension == " ") { - entry.path = fullDir.empty() ? "" : fullDir + '/'; - entry.path += entryName; + entryPath = fullDir.empty() ? "" : fullDir + '/'; + entryPath += entryName; } else { - entry.path = fullDir.empty() ? "" : fullDir + '/'; - entry.path += entryName + '.'; - entry.path += extension; + entryPath = fullDir.empty() ? "" : fullDir + '/'; + entryPath += entryName + '.'; + entryPath += extension; } + entryPath = vpk->cleanEntryPath(entryPath); reader.read(entry.crc32); auto preloadedDataSize = reader.read(); - reader.read(entry.archiveIndex); + entry.archiveIndex = reader.read(); entry.offset = reader.read(); entry.length = reader.read(); @@ -201,18 +200,18 @@ std::unique_ptr VPK::openInternal(const std::string& path, PackFileOpt } if (preloadedDataSize > 0) { - entry.vpk_preloadedData = reader.read_bytes(preloadedDataSize); + entry.extraData = reader.read_bytes(preloadedDataSize); entry.length += preloadedDataSize; } - vpk->entries[fullDir].push_back(entry); - if (entry.archiveIndex != VPK_DIR_INDEX && entry.archiveIndex > vpk->numArchives) { vpk->numArchives = entry.archiveIndex; } + vpk->entries.emplace(entryPath, entry); + if (callback) { - callback(fullDir, entry); + callback(entryPath, entry); } } } @@ -324,51 +323,49 @@ bool VPK::verifyPackFileSignature() const { return crypto::verifySHA256PublicKey(dirFileBuffer, this->footer2.publicKey, this->footer2.signature); } -std::optional> VPK::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> VPK::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } - std::vector output(entry.length, static_cast(0)); + std::vector output(entry->length, static_cast(0)); - if (!entry.vpk_preloadedData.empty()) { - std::copy(entry.vpk_preloadedData.begin(), entry.vpk_preloadedData.end(), output.begin()); + if (!entry->extraData.empty()) { + std::copy(entry->extraData.begin(), entry->extraData.end(), output.begin()); } - if (entry.length == entry.vpk_preloadedData.size()) { + if (entry->length == entry->extraData.size()) { return output; } - if (entry.archiveIndex != VPK_DIR_INDEX) { + if (entry->archiveIndex != VPK_DIR_INDEX) { // Stored in a numbered archive - FileStream stream{this->getTruncatedFilepath() + '_' + string::padNumber(entry.archiveIndex, 3) + (::isFPX(this) ? FPX_EXTENSION : VPK_EXTENSION).data()}; + FileStream stream{this->getTruncatedFilepath() + '_' + string::padNumber(entry->archiveIndex, 3) + (::isFPX(this) ? FPX_EXTENSION : VPK_EXTENSION).data()}; if (!stream) { return std::nullopt; } - stream.seek_in_u(entry.offset); - auto bytes = stream.read_bytes(entry.length - entry.vpk_preloadedData.size()); - std::copy(bytes.begin(), bytes.end(), output.begin() + static_cast(entry.vpk_preloadedData.size())); + stream.seek_in_u(entry->offset); + auto bytes = stream.read_bytes(entry->length - entry->extraData.size()); + std::copy(bytes.begin(), bytes.end(), output.begin() + static_cast(entry->extraData.size())); } else { // Stored in this directory VPK FileStream stream{this->fullFilePath}; if (!stream) { return std::nullopt; } - stream.seek_in_u(this->getHeaderLength() + this->header1.treeSize + entry.offset); - auto bytes = stream.read_bytes(entry.length - entry.vpk_preloadedData.size()); - std::copy(bytes.begin(), bytes.end(), output.begin() + static_cast(entry.vpk_preloadedData.size())); + stream.seek_in_u(this->getHeaderLength() + this->header1.treeSize + entry->offset); + auto bytes = stream.read_bytes(entry->length - entry->extraData.size()); + std::copy(bytes.begin(), bytes.end(), output.begin() + static_cast(entry->extraData.size())); } return output; } -Entry& VPK::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - entry.path = filename; +void VPK::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.crc32 = crypto::computeCRC32(buffer); entry.length = buffer.size(); @@ -402,8 +399,8 @@ Entry& VPK::addEntryInternal(Entry& entry, const std::string& filename_, std::ve if (options_.vpk_preloadBytes > 0) { auto clampedPreloadBytes = std::clamp(options_.vpk_preloadBytes, 0u, buffer.size() > VPK_MAX_PRELOAD_BYTES ? VPK_MAX_PRELOAD_BYTES : static_cast(buffer.size())); - entry.vpk_preloadedData.resize(clampedPreloadBytes); - std::memcpy(entry.vpk_preloadedData.data(), buffer.data(), clampedPreloadBytes); + entry.extraData.resize(clampedPreloadBytes); + std::memcpy(entry.extraData.data(), buffer.data(), clampedPreloadBytes); buffer.erase(buffer.begin(), buffer.begin() + clampedPreloadBytes); } @@ -418,81 +415,74 @@ Entry& VPK::addEntryInternal(Entry& entry, const std::string& filename_, std::ve } } } - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } bool VPK::removeEntry(const std::string& filename_) { - if (auto entry = this->findEntry(filename_); entry && (!entry->unbaked || entry->flags & VPK_FLAG_REUSING_CHUNK)) { + auto filename = this->cleanEntryPath(filename_); + if (auto entry = this->findEntry(filename); entry && (!entry->unbaked || entry->flags & VPK_FLAG_REUSING_CHUNK)) { this->freedChunks.push_back({entry->offset, entry->length, entry->archiveIndex}); } - return PackFile::removeEntry(filename_); + return PackFile::removeEntry(filename); +} + +std::size_t VPK::removeDirectory(const std::string& dirName_) { + auto dirName = this->cleanEntryPath(dirName_); + if (!dirName.empty()) { + dirName += '/'; + } + this->runForAllEntries([this, &dirName](const std::string& path, const Entry& entry) { + if (path.starts_with(dirName) && (!entry.unbaked || entry.flags & VPK_FLAG_REUSING_CHUNK)) { + this->freedChunks.push_back({entry.offset, entry.length, entry.archiveIndex}); + } + }); + return PackFile::removeDirectory(dirName_); } -bool VPK::bake(const std::string& outputDir_, const Callback& callback) { +bool VPK::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); // Reconstruct data so we're not looping over it a ton of times - std::unordered_map>> temp; - - for (auto& [tDir, tEntries] : this->entries) { - for (auto& tEntry : tEntries) { - std::string extension = tEntry.getExtension(); - if (extension.empty()) { - extension = " "; - } - if (!temp.contains(extension)) { - temp[extension] = {}; - } - if (!temp.at(extension).contains(tDir)) { - temp.at(extension)[tDir] = {}; - } - temp.at(extension).at(tDir).push_back(&tEntry); + std::unordered_map>>> temp; + this->runForAllEntries([&temp](const std::string& path, Entry& entry) { + const auto fsPath = std::filesystem::path{path}; + auto extension = fsPath.extension().string(); + auto parentDir = fsPath.parent_path().string(); + + if (extension.empty()) { + extension = " "; } - } - for (auto& [tDir, tEntries]: this->unbakedEntries) { - for (auto& tEntry : tEntries) { - std::string extension = tEntry.getExtension(); - if (extension.empty()) { - extension = " "; - } - if (!temp.contains(extension)) { - temp[extension] = {}; - } - if (!temp.at(extension).contains(tDir)) { - temp.at(extension)[tDir] = {}; - } - temp.at(extension).at(tDir).push_back(&tEntry); + if (!temp.contains(extension)) { + temp[extension] = {}; } - } + if (!temp.at(extension).contains(parentDir)) { + temp.at(extension)[parentDir] = {}; + } + temp.at(extension).at(parentDir).emplace_back(path, &entry); + }); // Temporarily store baked file data that's stored in the directory VPK since it's getting overwritten std::vector dirVPKEntryData; std::size_t newDirEntryOffset = 0; - for (auto& [tDir, tEntries] : this->entries) { - for (Entry& tEntry : tEntries) { - if (!tEntry.unbaked && tEntry.archiveIndex == VPK_DIR_INDEX && tEntry.length != tEntry.vpk_preloadedData.size()) { - auto binData = this->readEntry(tEntry); - if (!binData) { - continue; - } - dirVPKEntryData.reserve(dirVPKEntryData.size() + tEntry.length - tEntry.vpk_preloadedData.size()); - dirVPKEntryData.insert(dirVPKEntryData.end(), binData->begin() + static_cast::difference_type>(tEntry.vpk_preloadedData.size()), binData->end()); + this->runForAllEntries([this, &dirVPKEntryData, &newDirEntryOffset](const std::string& path, Entry& entry) { + if (entry.archiveIndex != VPK_DIR_INDEX || entry.length == entry.extraData.size()) { + return; + } - tEntry.offset = newDirEntryOffset; - newDirEntryOffset += tEntry.length - tEntry.vpk_preloadedData.size(); - } + auto binData = this->readEntry(path); + if (!binData) { + return; } - } + dirVPKEntryData.reserve(dirVPKEntryData.size() + entry.length - entry.extraData.size()); + dirVPKEntryData.insert(dirVPKEntryData.end(), binData->begin() + static_cast::difference_type>(entry.extraData.size()), binData->end()); + + entry.offset = newDirEntryOffset; + newDirEntryOffset += entry.length - entry.extraData.size(); + }, false); // Helper - const auto getArchiveFilename = [this](const std::string& filename_, int archiveIndex) { + const auto getArchiveFilename = [this](const std::string& filename_, uint32_t archiveIndex) { std::string out{filename_ + '_' + string::padNumber(archiveIndex, 3) + (::isFPX(this) ? FPX_EXTENSION : VPK_EXTENSION).data()}; string::normalizeSlashes(out); return out; @@ -500,7 +490,7 @@ bool VPK::bake(const std::string& outputDir_, const Callback& callback) { // Copy external binary blobs to the new dir if (!outputDir_.empty()) { - for (int archiveIndex = 0; archiveIndex < this->numArchives; archiveIndex++) { + for (uint32_t archiveIndex = 0; archiveIndex < this->numArchives; archiveIndex++) { std::string from = getArchiveFilename(this->getTruncatedFilepath(), archiveIndex); if (!std::filesystem::exists(from)) { continue; @@ -524,23 +514,20 @@ bool VPK::bake(const std::string& outputDir_, const Callback& callback) { } // File tree data - for (auto& [ext, tDirs] : temp) { + for (auto& [ext, dirs] : temp) { outDir.write(ext); - for (auto& [dir, tEntries] : tDirs) { + for (auto& [dir, tempEntries] : dirs) { outDir.write(!dir.empty() ? dir : " "); - for (auto* entry : tEntries) { + for (auto& [path, entry] : tempEntries) { // Calculate entry offset if it's unbaked and upload the data if (entry->unbaked) { - std::vector entryData; - if (isEntryUnbakedUsingByteBuffer(*entry)) { - entryData = std::get>(getEntryUnbakedData(*entry)); - } else { - entryData = fs::readFileBuffer(std::get(getEntryUnbakedData(*entry)), entry->vpk_preloadedData.size()); + auto entryData = readUnbakedEntry(*entry); + if (!entryData) { + continue; } - - if (entry->length == entry->vpk_preloadedData.size()) { + if (entry->length == entry->extraData.size()) { // Override the archive index, no need for an archive VPK entry->archiveIndex = VPK_DIR_INDEX; entry->offset = dirVPKEntryData.size(); @@ -549,37 +536,37 @@ bool VPK::bake(const std::string& outputDir_, const Callback& callback) { auto archiveFilename = getArchiveFilename(::removeVPKAndOrDirSuffix(outputPath, ::isFPX(this)), entry->archiveIndex); FileStream stream{archiveFilename, FileStream::OPT_READ | FileStream::OPT_WRITE | FileStream::OPT_CREATE_IF_NONEXISTENT}; stream.seek_out_u(entry->offset); - stream.write(entryData); + stream.write(*entryData); } else if (entry->archiveIndex != VPK_DIR_INDEX) { // The entry is being appended to a newly created VPK archive auto archiveFilename = getArchiveFilename(::removeVPKAndOrDirSuffix(outputPath, ::isFPX(this)), entry->archiveIndex); entry->offset = std::filesystem::exists(archiveFilename) ? std::filesystem::file_size(archiveFilename) : 0; FileStream stream{archiveFilename, FileStream::OPT_APPEND | FileStream::OPT_CREATE_IF_NONEXISTENT}; - stream.write(entryData); + stream.write(*entryData); } else { // The entry will be added to the directory VPK entry->offset = dirVPKEntryData.size(); - dirVPKEntryData.insert(dirVPKEntryData.end(), entryData.data(), entryData.data() + entryData.size()); + dirVPKEntryData.insert(dirVPKEntryData.end(), entryData->data(), entryData->data() + entryData->size()); } // Clear flags entry->flags = 0; } - outDir.write(entry->getStem()); + outDir.write(std::filesystem::path{path}.stem().string()); outDir.write(entry->crc32); - outDir.write(static_cast(entry->vpk_preloadedData.size())); - outDir.write(entry->archiveIndex); - outDir.write(static_cast(entry->offset)); - outDir.write(static_cast(entry->length - entry->vpk_preloadedData.size())); + outDir.write(entry->extraData.size()); + outDir.write(entry->archiveIndex); + outDir.write(entry->offset); + outDir.write(entry->length - entry->extraData.size()); outDir.write(VPK_ENTRY_TERM); - if (!entry->vpk_preloadedData.empty()) { - outDir.write(entry->vpk_preloadedData); + if (!entry->extraData.empty()) { + outDir.write(entry->extraData); } if (callback) { - callback(dir, *entry); + callback(path, *entry); } } outDir.write('\0'); @@ -604,21 +591,20 @@ bool VPK::bake(const std::string& outputDir_, const Callback& callback) { // Calculate hashes for all entries this->md5Entries.clear(); if (this->options.vpk_generateMD5Entries) { - for (const auto& [tDir, tEntries] : this->entries) { - for (const auto& tEntry : tEntries) { - // Believe it or not this should be safe to call by now - auto binData = this->readEntry(tEntry); - if (!binData) { - continue; - } - MD5Entry md5Entry{}; - md5Entry.archiveIndex = tEntry.archiveIndex; - md5Entry.length = tEntry.length - tEntry.vpk_preloadedData.size(); - md5Entry.offset = tEntry.offset; - md5Entry.checksum = crypto::computeMD5(*binData); - this->md5Entries.push_back(md5Entry); + this->runForAllEntries([this](const std::string& path, const Entry& entry) { + // Believe it or not this should be safe to call by now + auto binData = this->readEntry(path); + if (!binData) { + return; } - } + MD5Entry md5Entry{ + .archiveIndex = entry.archiveIndex, + .offset = static_cast(entry.offset), + .length = static_cast(entry.length - entry.extraData.size()), + .checksum = crypto::computeMD5(*binData), + }; + this->md5Entries.push_back(md5Entry); + }, false); } // Calculate Header2 diff --git a/src/vpkpp/format/VPK_VTMB.cpp b/src/vpkpp/format/VPK_VTMB.cpp index 1e87c5509..49c6dcf73 100644 --- a/src/vpkpp/format/VPK_VTMB.cpp +++ b/src/vpkpp/format/VPK_VTMB.cpp @@ -16,7 +16,7 @@ VPK_VTMB::VPK_VTMB(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::VPK_VTMB; } -std::unique_ptr VPK_VTMB::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr VPK_VTMB::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -48,7 +48,7 @@ std::unique_ptr VPK_VTMB::open(const std::string& path, PackFileOption return packFile; } -void VPK_VTMB::openNumbered(uint16_t archiveIndex, const std::string& path, const Callback& callback) { +void VPK_VTMB::openNumbered(uint32_t archiveIndex, const std::string& path, const EntryCallback& callback) { FileStream reader{path}; reader.seek_in(-static_cast(sizeof(uint32_t) * 2 + sizeof(uint8_t)), std::ios::end); @@ -72,63 +72,47 @@ void VPK_VTMB::openNumbered(uint16_t archiveIndex, const std::string& path, cons Entry entry = createNewEntry(); entry.archiveIndex = archiveIndex; - entry.path = reader.read_string(reader.read()); - string::normalizeSlashes(entry.path, true); - if (!this->isCaseSensitive()) { - string::toLower(entry.path); - } + auto entryPath = this->cleanEntryPath(reader.read_string(reader.read())); entry.offset = reader.read(); entry.length = reader.read(); - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - if (!this->entries.contains(parentDir)) { - this->entries[parentDir] = {}; - } - this->entries[parentDir].push_back(entry); + this->entries.emplace(entryPath, entry); if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } } -std::optional> VPK_VTMB::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> VPK_VTMB::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // It's baked into the file on disk - FileStream stream{this->getTruncatedFilepath() + string::padNumber(entry.archiveIndex, 3) + VPK_VTMB_EXTENSION.data()}; + FileStream stream{this->getTruncatedFilepath() + string::padNumber(entry->archiveIndex, 3) + VPK_VTMB_EXTENSION.data()}; if (!stream) { return std::nullopt; } - stream.seek_in_u(entry.offset); - return stream.read_bytes(entry.length); + stream.seek_in_u(entry->offset); + return stream.read_bytes(entry->length); } -Entry& VPK_VTMB::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - +void VPK_VTMB::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.archiveIndex = this->currentArchive; - entry.path = filename; entry.length = buffer.size(); // Offset will be reset when it's baked entry.offset = 0; - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool VPK_VTMB::bake(const std::string& outputDir_, const Callback& callback) { +bool VPK_VTMB::bake(const std::string& outputDir_, const EntryCallback& callback) { if (this->knownArchives.empty()) { return false; } @@ -154,31 +138,21 @@ bool VPK_VTMB::bake(const std::string& outputDir_, const Callback& callback) { this->fullFilePath = (tempDir / (this->getTruncatedFilestem())).string() + string::padNumber(this->knownArchives[0], 3) + VPK_VTMB_EXTENSION.data(); // Reconstruct data for ease of access - std::unordered_map> entriesToBake; - for (auto& [entryDir, entryList] : this->entries) { - for (auto& entry : entryList) { - if (!entriesToBake.contains(entry.archiveIndex)) { - entriesToBake[entry.archiveIndex] = {}; - } - entriesToBake[entry.archiveIndex].push_back(&entry); - } - } - for (auto& [entryDir, entryList] : this->unbakedEntries) { - for (auto& entry : entryList) { - if (!entriesToBake.contains(entry.archiveIndex)) { - entriesToBake[entry.archiveIndex] = {}; - } - entriesToBake[entry.archiveIndex].push_back(&entry); + std::unordered_map>> entriesToBake; + this->runForAllEntries([&entriesToBake](const std::string& path, Entry& entry) { + if (!entriesToBake.contains(entry.archiveIndex)) { + entriesToBake[entry.archiveIndex] = {}; } - } + entriesToBake[entry.archiveIndex].emplace_back(path, &entry); + }); for (auto& [archiveIndex, entriesToBakeInArchive] : entriesToBake) { FileStream stream{outputPathStem + string::padNumber(archiveIndex, 3) + VPK_VTMB_EXTENSION.data(), FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT}; stream.seek_out(0); // File data - for (auto* entry : entriesToBakeInArchive) { - if (auto binData = this->readEntry(*entry)) { + for (auto& [path, entry] : entriesToBakeInArchive) { + if (auto binData = this->readEntry(path)) { entry->offset = stream.tell_out(); stream.write(*binData); } else { @@ -189,14 +163,14 @@ bool VPK_VTMB::bake(const std::string& outputDir_, const Callback& callback) { // Directory auto dirOffset = stream.tell_out(); - for (auto* entry : entriesToBakeInArchive) { - stream.write(entry->path.length()); - stream.write(entry->path, false); + for (const auto& [path, entry] : entriesToBakeInArchive) { + stream.write(path.length()); + stream.write(path, false); stream.write(entry->offset); stream.write(entry->length); if (callback) { - callback(entry->getParentPath(), *entry); + callback(path, *entry); } } diff --git a/src/vpkpp/format/ZIP.cpp b/src/vpkpp/format/ZIP.cpp index 6e2134047..946a238bd 100644 --- a/src/vpkpp/format/ZIP.cpp +++ b/src/vpkpp/format/ZIP.cpp @@ -29,7 +29,7 @@ ZIP::~ZIP() { this->closeZIP(); } -std::unique_ptr ZIP::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr ZIP::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { if (!std::filesystem::exists(path)) { // File does not exist return nullptr; @@ -51,30 +51,18 @@ std::unique_ptr ZIP::open(const std::string& path, PackFileOptions opt continue; } - Entry entry = createNewEntry(); - entry.path = fileInfo->filename; - string::normalizeSlashes(entry.path, true); - if (!zip->isCaseSensitive()) { - string::toLower(entry.path); - } + auto entryPath = zip->cleanEntryPath(fileInfo->filename); + Entry entry = createNewEntry(); entry.flags = fileInfo->compression_method; entry.length = fileInfo->uncompressed_size; entry.compressedLength = fileInfo->compressed_size; entry.crc32 = fileInfo->crc; - auto parentDir = std::filesystem::path{entry.path}.parent_path().string(); - string::normalizeSlashes(parentDir, true); - if (!zip->isCaseSensitive()) { - string::toLower(parentDir); - } - if (!zip->entries.contains(parentDir)) { - zip->entries[parentDir] = {}; - } - zip->entries[parentDir].push_back(entry); + zip->entries.insert(entryPath, entry); if (callback) { - callback(parentDir, entry); + callback(entryPath, entry); } } @@ -85,48 +73,40 @@ std::vector ZIP::verifyEntryChecksums() const { return this->verifyEntryChecksumsUsingCRC32(); } -std::optional> ZIP::readEntry(const Entry& entry) const { - if (entry.unbaked) { - return this->readUnbakedEntry(entry); +std::optional> ZIP::readEntry(const std::string& path_) const { + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // It's baked into the file on disk if (!this->streamOpen || !this->zipOpen) { return std::nullopt; } - if (mz_zip_locate_entry(this->zipHandle, entry.path.c_str(), !this->isCaseSensitive()) != MZ_OK) { + if (mz_zip_locate_entry(this->zipHandle, path.c_str(), !this->isCaseSensitive()) != MZ_OK) { return std::nullopt; } if (mz_zip_entry_read_open(this->zipHandle, 0, nullptr) != MZ_OK) { return std::nullopt; } std::vector out; - out.resize(entry.length); - mz_zip_entry_read(this->zipHandle, out.data(), static_cast(entry.length)); + out.resize(entry->length); + mz_zip_entry_read(this->zipHandle, out.data(), static_cast(entry->length)); mz_zip_entry_close(this->zipHandle); return out; } -Entry& ZIP::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - - entry.path = filename; +void ZIP::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { entry.length = buffer.size(); entry.compressedLength = 0; entry.crc32 = crypto::computeCRC32(buffer); - - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool ZIP::bake(const std::string& outputDir_, const Callback& callback) { +bool ZIP::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); @@ -162,7 +142,7 @@ void ZIP::setCompressionMethod(uint16_t compressionMethod) { } #endif -bool ZIP::bakeTempZip(const std::string& writeZipPath, const Callback& callback) { +bool ZIP::bakeTempZip(const std::string& writeZipPath, const EntryCallback& callback) { void* writeStreamHandle; #ifdef VPKPP_ZIP_COMPRESSION if (this->options.zip_compressionMethod != MZ_COMPRESS_METHOD_STORE) { @@ -189,55 +169,31 @@ bool ZIP::bakeTempZip(const std::string& writeZipPath, const Callback& callback) } #endif - for (const auto& [entryDir, entries] : this->getBakedEntries()) { - for (const Entry& entry : entries) { - auto binData = this->readEntry(entry); - if (!binData) { - continue; - } - - mz_zip_file fileInfo; - std::memset(&fileInfo, 0, sizeof(mz_zip_entry)); - fileInfo.flag = MZ_ZIP_FLAG_DATA_DESCRIPTOR; - fileInfo.filename = entry.path.c_str(); - fileInfo.filename_size = entry.path.length(); - fileInfo.uncompressed_size = static_cast(entry.length); - fileInfo.compressed_size = static_cast(entry.compressedLength); - fileInfo.crc = entry.crc32; - fileInfo.compression_method = this->options.zip_compressionMethod; - if (mz_zip_writer_add_buffer(writeZipHandle, binData->data(), static_cast(binData->size()), &fileInfo)) { - return false; - } - - if (callback) { - callback(entry.getParentPath(), entry); - } + bool noneFailed = true; + this->runForAllEntries([this, &callback, writeZipHandle, &noneFailed](const std::string& path, const Entry& entry) { + auto binData = this->readEntry(path); + if (!binData) { + return; } - } - for (const auto& [entryDir, entries] : this->getUnbakedEntries()) { - for (const Entry& entry : entries) { - auto binData = this->readEntry(entry); - if (!binData) { - continue; - } - - mz_zip_entry fileInfo; - std::memset(&fileInfo, 0, sizeof(mz_zip_entry)); - fileInfo.filename = entry.path.c_str(); - fileInfo.filename_size = entry.path.length(); - fileInfo.uncompressed_size = static_cast(entry.length); - fileInfo.compressed_size = static_cast(entry.compressedLength); - fileInfo.crc = entry.crc32; - fileInfo.compression_method = this->options.zip_compressionMethod; - if (mz_zip_writer_add_buffer(writeZipHandle, binData->data(), static_cast(binData->size()), &fileInfo)) { - return false; - } - - if (callback) { - callback(entry.getParentPath(), entry); - } + + mz_zip_file fileInfo; + std::memset(&fileInfo, 0, sizeof(mz_zip_entry)); + fileInfo.flag = MZ_ZIP_FLAG_DATA_DESCRIPTOR; + fileInfo.filename = path.c_str(); + fileInfo.filename_size = path.length(); + fileInfo.uncompressed_size = static_cast(entry.length); + fileInfo.compressed_size = static_cast(entry.compressedLength); + fileInfo.crc = entry.crc32; + fileInfo.compression_method = this->options.zip_compressionMethod; + if (mz_zip_writer_add_buffer(writeZipHandle, binData->data(), static_cast(binData->size()), &fileInfo)) { + noneFailed = false; + return; } - } + + if (callback) { + callback(path, entry); + } + }); if (mz_zip_writer_close(writeZipHandle)) { return false; @@ -249,7 +205,7 @@ bool ZIP::bakeTempZip(const std::string& writeZipPath, const Callback& callback) } mz_stream_delete(&writeStreamHandle); - return true; + return noneFailed; } bool ZIP::openZIP(std::string_view path) { diff --git a/src/vpkpp/format/example/ExamplePackFileImpl.cpp b/src/vpkpp/format/example/ExamplePackFileImpl.cpp index 009d6025e..6588a6366 100644 --- a/src/vpkpp/format/example/ExamplePackFileImpl.cpp +++ b/src/vpkpp/format/example/ExamplePackFileImpl.cpp @@ -4,7 +4,6 @@ #include #include -#include using namespace sourcepp; using namespace vpkpp; @@ -15,7 +14,7 @@ EXAMPLE::EXAMPLE(const std::string& fullFilePath_, PackFileOptions options_) this->type = PackFileType::UNKNOWN; } -std::unique_ptr EXAMPLE::open(const std::string& path, PackFileOptions options, const Callback& callback) { +std::unique_ptr EXAMPLE::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { // Check if the file exists if (!std::filesystem::exists(path)) { // File does not exist @@ -29,65 +28,46 @@ std::unique_ptr EXAMPLE::open(const std::string& path, PackFileOptions // Here is where you add entries to the entries member variable // It's a map between a directory and a vector of entries // Every time an entry is added, the callback should be called if the callback exists - std::vector> samplePaths{ - {"a/b/c", "skibidi_toilet.png"}, - {"d/c", "boykisser.mdl"}, - {"", "megamind.txt"}, + std::vector samplePaths{ + {"a/b/c/skibidi_toilet.png"}, + {"d/c/boykisser.mdl"}, + {"megamind.txt"}, }; - for (auto& [dir, name] : samplePaths) { + for (const auto& entryPath_ : samplePaths) { // The path needs to be normalized, and respect case sensitivity - string::normalizeSlashes(dir); - if (!example->isCaseSensitive()) { - string::toLower(dir); - string::toLower(name); - } - - // Create the list if it doesn't exist - if (!example->entries.contains(dir)) { - example->entries[dir] = {}; - } + auto entryPath = example->cleanEntryPath(entryPath_); // Use the createNewEntry function to avoid Entry having to friend every single damn class Entry entry = createNewEntry(); - // The path should be the full path to the file - entry.path = dir; - entry.path += dir.empty() ? "" : "/"; - entry.path += name; - - // We already did it at the start, but this is how it's usually done - //::normalizeSlashes(entry.path); - //if (!options.allowUppercaseLettersInFilenames) { - // ::toLowerCase(entry.path); - //} - // The length should be the full uncompressed length of the file data in bytes entry.length = 42; - // The compressed length will be non-zero if the file is compressed, the length is in bytes - // This can be omitted if unused, 0 is the default - entry.compressedLength = 0; - // This is the CRC32 of the file - a helper function to compute it is in // This can also be omitted if unused, 0 is the default entry.crc32 = 0; - // Add the entry to the entries map - example->entries[dir].push_back(std::move(entry)); + // Add the entry to the entries trie + example->entries.emplace(entryPath, entry); // Call the callback if (callback) { - callback(dir, entry); + callback(entryPath, entry); } } return packFile; } -std::optional> EXAMPLE::readEntry(const Entry& entry) const { +std::optional> EXAMPLE::readEntry(const std::string& path_) const { // Include this code verbatim - if (entry.unbaked) { - return this->readUnbakedEntry(entry); + auto path = this->cleanEntryPath(path_); + auto entry = this->findEntry(path); + if (!entry) { + return std::nullopt; + } + if (entry->unbaked) { + return readUnbakedEntry(*entry); } // Use the contents of the entry to access the file data and return it @@ -95,65 +75,32 @@ std::optional> EXAMPLE::readEntry(const Entry& entry) con return std::nullopt; } -Entry& EXAMPLE::addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) { - // Include this verbatim - auto filename = filename_; - if (!this->isCaseSensitive()) { - string::toLower(filename); - } - auto [dir, name] = splitFilenameAndParentDir(filename); - +void EXAMPLE::addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) { // Initialize the entry - set the entry properties just like in EXAMPLE::open - entry.path = filename; + entry.length = 0; // ... - - // Include this verbatim - if (!this->unbakedEntries.contains(dir)) { - this->unbakedEntries[dir] = {}; - } - this->unbakedEntries.at(dir).push_back(entry); - return this->unbakedEntries.at(dir).back(); } -bool EXAMPLE::bake(const std::string& outputDir_, const PackFile::Callback& callback) { +bool EXAMPLE::bake(const std::string& outputDir_, const EntryCallback& callback) { // Get the proper file output folder (include this verbatim) std::string outputDir = this->getBakeOutputDir(outputDir_); std::string outputPath = outputDir + '/' + this->getFilename(); // Loop over all entries and save them - for (const auto& [entryDir, entries] : this->getBakedEntries()) { - for (const Entry& entry : entries) { - auto binData = this->readEntry(entry); - if (!binData) { - continue; - } - - // Write data here - // ... - - // Call the callback - if (callback) { - callback(entry.getParentPath(), entry); - } + this->runForAllEntries([&callback](const std::string& path, const Entry& entry) { + auto binData = this->readEntry(path); + if (!binData) { + return; } - } - // Yes this is copy-paste, you could probably turn this into a lambda and call it on both maps - for (const auto& [entryDir, entries] : this->getUnbakedEntries()) { - for (const Entry& entry : entries) { - auto binData = this->readEntry(entry); - if (!binData) { - continue; - } - - // Write data here - // ... - - // Call the callback - if (callback) { - callback(entry.getParentPath(), entry); - } + + // Write data here + // ... + + // Call the callback + if (callback) { + callback(path, entry); } - } + }); // Call this when all the entries have been written to disk this->mergeUnbakedEntries(); diff --git a/src/vpkpp/format/example/ExamplePackFileImpl.h b/src/vpkpp/format/example/ExamplePackFileImpl.h index ff9798f77..61fa18ac8 100644 --- a/src/vpkpp/format/example/ExamplePackFileImpl.h +++ b/src/vpkpp/format/example/ExamplePackFileImpl.h @@ -24,18 +24,18 @@ class EXAMPLE : public PackFile { public: // Always return a unique_ptr to PackFile so it has a uniform return type // If your type needs any new options, add them to PackFileOptions - it was the cleanest way to do it without messing with variants or std::any - [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const Callback& callback = nullptr); + [[nodiscard]] static std::unique_ptr open(const std::string& path, PackFileOptions options = {}, const EntryCallback& callback = nullptr); // [OPTIONAL] Implement this and return true if your file format is case-sensitive [[nodiscard]] constexpr bool isCaseSensitive() const noexcept override { return PackFile::isCaseSensitive(); } - // Returns the raw data the Entry points to - [[nodiscard]] std::optional> readEntry(const Entry& entry) const override; + // Returns the raw entry data + [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; // [WRITE] Save any changes made to the opened file(s) - bool bake(const std::string& outputDir_ /*= ""*/, const Callback& callback /*= nullptr*/) override; + bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; // [OPTIONAL] Returns any attributes your file format's entries have (refer to the other file formats for more info) [[nodiscard]] std::vector getSupportedEntryAttributes() const override { @@ -52,7 +52,7 @@ class EXAMPLE : public PackFile { // [WRITE] Adds a new entry from either a filename or a buffer // Again, if your type needs any new options specific to entries, add them to EntryOptions - Entry& addEntryInternal(Entry& entry, const std::string& filename_, std::vector& buffer, EntryOptions options_) override; + void addEntryInternal(Entry& entry, const std::string& path, std::vector& buffer, EntryOptions options_) override; private: // Finally, register the open method with the extension From 6bb63d7f65ebfe1d26820d71ee3c8f458b53c80a Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 00:11:30 -0400 Subject: [PATCH 02/10] feat(vpkpp): update C wrapper, supported attributes return bitflags instead of a vector --- README.md | 1 - include/sourcepp/BitwiseEnumClass.h | 39 ++++ include/vpkpp/Attribute.h | 18 +- include/vpkpp/PackFile.h | 2 +- include/vpkpp/format/GCF.h | 2 +- include/vpkpp/format/GMA.h | 2 +- include/vpkpp/format/PAK.h | 2 +- include/vpkpp/format/PCK.h | 2 +- include/vpkpp/format/VPK.h | 2 +- include/vpkpp/format/VPK_VTMB.h | 2 +- include/vpkpp/format/ZIP.h | 2 +- lang/c/include/vpkppc/Attribute.h | 13 +- lang/c/include/vpkppc/Convert.hpp | 4 + lang/c/include/vpkppc/Entry.h | 26 ++- lang/c/include/vpkppc/Options.h | 9 +- lang/c/include/vpkppc/PackFile.h | 87 +++++--- lang/c/include/vpkppc/PackFileType.h | 1 - lang/c/include/vpkppc/format/BSP.h | 6 + lang/c/include/vpkppc/format/FPX.h | 6 + lang/c/include/vpkppc/format/GCF.h | 6 + lang/c/include/vpkppc/format/GMA.h | 6 + lang/c/include/vpkppc/format/GRP.h | 9 - lang/c/include/vpkppc/format/PAK.h | 6 + lang/c/include/vpkppc/format/PCK.h | 6 + lang/c/include/vpkppc/format/VPK.h | 12 ++ lang/c/include/vpkppc/format/VPK_VTMB.h | 6 + lang/c/include/vpkppc/format/ZIP.h | 6 + lang/c/src/vpkppc/Convert.cpp | 18 +- lang/c/src/vpkppc/Entry.cpp | 63 +++--- lang/c/src/vpkppc/PackFile.cpp | 261 ++++++++++++------------ lang/c/src/vpkppc/_vpkppc.cmake | 3 +- lang/c/src/vpkppc/format/BSP.cpp | 26 +++ lang/c/src/vpkppc/format/FPX.cpp | 26 +++ lang/c/src/vpkppc/format/GCF.cpp | 26 +++ lang/c/src/vpkppc/format/GMA.cpp | 26 +++ lang/c/src/vpkppc/format/GRP.cpp | 28 --- lang/c/src/vpkppc/format/PAK.cpp | 26 +++ lang/c/src/vpkppc/format/PCK.cpp | 26 +++ lang/c/src/vpkppc/format/VPK.cpp | 56 ++++- lang/c/src/vpkppc/format/VPK_VTMB.cpp | 26 +++ lang/c/src/vpkppc/format/ZIP.cpp | 26 +++ src/vpkpp/PackFile.cpp | 4 +- src/vpkpp/format/GCF.cpp | 4 +- src/vpkpp/format/GMA.cpp | 4 +- src/vpkpp/format/PAK.cpp | 4 +- src/vpkpp/format/PCK.cpp | 4 +- src/vpkpp/format/VPK.cpp | 4 +- src/vpkpp/format/VPK_VTMB.cpp | 4 +- src/vpkpp/format/ZIP.cpp | 4 +- 49 files changed, 658 insertions(+), 294 deletions(-) create mode 100644 include/sourcepp/BitwiseEnumClass.h delete mode 100644 lang/c/include/vpkppc/format/GRP.h delete mode 100644 lang/c/src/vpkppc/format/GRP.cpp diff --git a/README.md b/README.md index 7c436942c..df643bc54 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one
  • FPX v10 (Tactical Intervention)
  • GCF v6
  • GMA v1-3 (Garry's Mod)
  • -
  • GRP (Build Engine)
  • PAK (Quake, WON Half-Life)
  • PCK v1-2 (Godot Engine)
  • VPK v1-2
  • diff --git a/include/sourcepp/BitwiseEnumClass.h b/include/sourcepp/BitwiseEnumClass.h new file mode 100644 index 000000000..e11eadc56 --- /dev/null +++ b/include/sourcepp/BitwiseEnumClass.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#define SOURCEPP_SETUP_BITWISE_ENUM_CLASS(Enum) \ + inline constexpr Enum operator|(Enum lhs, Enum rhs) { \ + return static_cast( \ + static_cast>(lhs) | \ + static_cast>(rhs)); \ + } \ + inline constexpr Enum operator&(Enum lhs, Enum rhs) { \ + return static_cast( \ + static_cast>(lhs) & \ + static_cast>(rhs)); \ + } \ + inline constexpr Enum operator^(Enum lhs, Enum rhs) { \ + return static_cast( \ + static_cast>(lhs) ^ \ + static_cast>(rhs)); \ + } \ + inline constexpr Enum operator~(Enum e) { \ + return static_cast( \ + ~static_cast>(e)); \ + } \ + inline Enum& operator|=(Enum& lhs, Enum rhs) { \ + return lhs = static_cast( \ + static_cast>(lhs) | \ + static_cast>(rhs)); \ + } \ + inline Enum& operator&=(Enum& lhs, Enum rhs) { \ + return lhs = static_cast( \ + static_cast>(lhs) & \ + static_cast>(rhs)); \ + } \ + inline Enum& operator^=(Enum& lhs, Enum rhs) { \ + return lhs = static_cast( \ + static_cast>(lhs) ^ \ + static_cast>(rhs)); \ + } diff --git a/include/vpkpp/Attribute.h b/include/vpkpp/Attribute.h index 255a1ba15..29c16f73a 100644 --- a/include/vpkpp/Attribute.h +++ b/include/vpkpp/Attribute.h @@ -1,15 +1,17 @@ #pragma once +#include + namespace vpkpp { -enum class Attribute : int { - //PATH, // Not included because its implied - LENGTH = 0, - VPK_PRELOADED_DATA_LENGTH, - ARCHIVE_INDEX, - CRC32, - PCK_MD5, - ATTRIBUTE_COUNT, +enum class Attribute { + NONE = 0, + LENGTH = 1 << 0, + VPK_PRELOADED_DATA_LENGTH = 1 << 1, + ARCHIVE_INDEX = 1 << 2, + CRC32 = 1 << 3, + PCK_MD5 = 1 << 4, }; +SOURCEPP_SETUP_BITWISE_ENUM_CLASS(Attribute) } // namespace vpkpp diff --git a/include/vpkpp/PackFile.h b/include/vpkpp/PackFile.h index c13fbe0f2..4681b02df 100644 --- a/include/vpkpp/PackFile.h +++ b/include/vpkpp/PackFile.h @@ -157,7 +157,7 @@ class PackFile { /// Returns a list of supported entry attributes /// Mostly for GUI programs that show entries and their metadata in a table ;) - [[nodiscard]] virtual std::vector getSupportedEntryAttributes() const; + [[nodiscard]] virtual Attribute getSupportedEntryAttributes() const; [[nodiscard]] virtual explicit operator std::string() const; diff --git a/include/vpkpp/format/GCF.h b/include/vpkpp/format/GCF.h index 3c86c6dc7..666bb41d5 100644 --- a/include/vpkpp/format/GCF.h +++ b/include/vpkpp/format/GCF.h @@ -120,7 +120,7 @@ class GCF : public PackFileReadOnly { [[nodiscard]] std::optional> readEntry(const std::string& path_) const override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; [[nodiscard]] explicit operator std::string() const override; diff --git a/include/vpkpp/format/GMA.h b/include/vpkpp/format/GMA.h index d0b85d187..41d0f39d3 100644 --- a/include/vpkpp/format/GMA.h +++ b/include/vpkpp/format/GMA.h @@ -41,7 +41,7 @@ class GMA : public PackFile { bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; [[nodiscard]] explicit operator std::string() const override; diff --git a/include/vpkpp/format/PAK.h b/include/vpkpp/format/PAK.h index 68e3486f6..d7df0b5ce 100644 --- a/include/vpkpp/format/PAK.h +++ b/include/vpkpp/format/PAK.h @@ -19,7 +19,7 @@ class PAK : public PackFile { bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; protected: PAK(const std::string& fullFilePath_, PackFileOptions options_); diff --git a/include/vpkpp/format/PCK.h b/include/vpkpp/format/PCK.h index 5137f17b2..c6956f2a9 100644 --- a/include/vpkpp/format/PCK.h +++ b/include/vpkpp/format/PCK.h @@ -38,7 +38,7 @@ class PCK : public PackFile { bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; [[nodiscard]] explicit operator std::string() const override; diff --git a/include/vpkpp/format/VPK.h b/include/vpkpp/format/VPK.h index 2db26279b..9a8ecf8f2 100644 --- a/include/vpkpp/format/VPK.h +++ b/include/vpkpp/format/VPK.h @@ -98,7 +98,7 @@ class VPK : public PackFile { [[nodiscard]] std::string getTruncatedFilestem() const override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; [[nodiscard]] explicit operator std::string() const override; diff --git a/include/vpkpp/format/VPK_VTMB.h b/include/vpkpp/format/VPK_VTMB.h index 8268351ed..2af76ea80 100644 --- a/include/vpkpp/format/VPK_VTMB.h +++ b/include/vpkpp/format/VPK_VTMB.h @@ -20,7 +20,7 @@ class VPK_VTMB : public PackFile { [[nodiscard]] std::string getTruncatedFilestem() const override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; protected: VPK_VTMB(const std::string& fullFilePath_, PackFileOptions options_); diff --git a/include/vpkpp/format/ZIP.h b/include/vpkpp/format/ZIP.h index be833dfa4..fb52c45c8 100644 --- a/include/vpkpp/format/ZIP.h +++ b/include/vpkpp/format/ZIP.h @@ -28,7 +28,7 @@ class ZIP : public PackFile { bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; - [[nodiscard]] std::vector getSupportedEntryAttributes() const override; + [[nodiscard]] Attribute getSupportedEntryAttributes() const override; #ifdef VPKPP_ZIP_COMPRESSION [[nodiscard]] uint16_t getCompressionMethod() const; diff --git a/lang/c/include/vpkppc/Attribute.h b/lang/c/include/vpkppc/Attribute.h index 01dada6b7..2b7aaac1d 100644 --- a/lang/c/include/vpkppc/Attribute.h +++ b/lang/c/include/vpkppc/Attribute.h @@ -5,13 +5,12 @@ extern "C" { #endif typedef enum { - VPKPP_ATTRIBUTE_NONE = -1, - VPKPP_ATTRIBUTE_LENGTH = 0, - VPKPP_ATTRIBUTE_VPK_PRELOADED_DATA_LENGTH, - VPKPP_ATTRIBUTE_ARCHIVE_INDEX, - VPKPP_ATTRIBUTE_CRC32, - VPKPP_ATTRIBUTE_PCK_MD5, - VPKPP_ATTRIBUTE_COUNT, + VPKPP_ATTRIBUTE_NONE = 0, + VPKPP_ATTRIBUTE_LENGTH = 1 << 0, + VPKPP_ATTRIBUTE_VPK_PRELOADED_DATA_LENGTH = 1 << 1, + VPKPP_ATTRIBUTE_ARCHIVE_INDEX = 1 << 2, + VPKPP_ATTRIBUTE_CRC32 = 1 << 3, + VPKPP_ATTRIBUTE_PCK_MD5 = 1 << 4, } vpkpp_attribute_e; #ifdef __cplusplus diff --git a/lang/c/include/vpkppc/Convert.hpp b/lang/c/include/vpkppc/Convert.hpp index 4d06f345c..a4bb57cf8 100644 --- a/lang/c/include/vpkppc/Convert.hpp +++ b/lang/c/include/vpkppc/Convert.hpp @@ -28,6 +28,10 @@ vpkpp::Entry* entry(vpkpp_entry_handle_t handle); vpkpp::PackFileOptions optionsFromC(vpkpp_pack_file_options_t options); +vpkpp::EntryOptions optionsFromC(vpkpp_entry_options_t options); + vpkpp_pack_file_options_t optionsToC(vpkpp::PackFileOptions options); +vpkpp_entry_options_t optionsToC(vpkpp::EntryOptions options); + } // namespace Convert diff --git a/lang/c/include/vpkppc/Entry.h b/lang/c/include/vpkppc/Entry.h index 6a4385784..d86faf422 100644 --- a/lang/c/include/vpkppc/Entry.h +++ b/lang/c/include/vpkppc/Entry.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -8,27 +9,24 @@ extern "C" { typedef void* vpkpp_entry_handle_t; -typedef struct { - int64_t size; - vpkpp_entry_handle_t* data; -} vpkpp_entry_handle_array_t; - -#define VPKPP_ENTRY_HANDLE_ARRAY_INVALID (vpkpp_entry_handle_array_t{.size = -1, .data = NULL}) - #ifdef __cplusplus } // extern "C" #endif -SOURCEPP_API size_t vpkpp_entry_get_path(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API uint32_t vpkpp_entry_get_flags(vpkpp_entry_handle_t handle); -SOURCEPP_API size_t vpkpp_entry_get_parent_path(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API uint32_t vpkpp_entry_get_archive_index(vpkpp_entry_handle_t handle); -SOURCEPP_API size_t vpkpp_entry_get_filename(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API uint64_t vpkpp_entry_get_length(vpkpp_entry_handle_t handle); -SOURCEPP_API size_t vpkpp_entry_get_stem(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API uint64_t vpkpp_entry_get_compressed_length(vpkpp_entry_handle_t handle); -SOURCEPP_API size_t vpkpp_entry_get_extension(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API uint64_t vpkpp_entry_get_offset(vpkpp_entry_handle_t handle); -SOURCEPP_API void vpkpp_entry_free(vpkpp_entry_handle_t* handle); +SOURCEPP_API sourcepp_buffer_t vpkpp_entry_get_extra_data(vpkpp_entry_handle_t handle); -SOURCEPP_API void vpkpp_entry_array_free(vpkpp_entry_handle_array_t* array); +SOURCEPP_API uint32_t vpkpp_entry_get_crc32(vpkpp_entry_handle_t handle); + +SOURCEPP_API int vpkpp_entry_is_unbaked(vpkpp_entry_handle_t handle); + +SOURCEPP_API void vpkpp_entry_free(vpkpp_entry_handle_t* handle); diff --git a/lang/c/include/vpkppc/Options.h b/lang/c/include/vpkppc/Options.h index 95e52e651..4ff7a7321 100644 --- a/lang/c/include/vpkppc/Options.h +++ b/lang/c/include/vpkppc/Options.h @@ -7,13 +7,18 @@ extern "C" { #endif typedef struct { - bool gma_writeCRCs; + int gma_writeCRCs; uint32_t vpk_version; uint32_t vpk_preferredChunkSize; - bool vpk_generateMD5Entries; + int vpk_generateMD5Entries; uint16_t zip_compressionMethod; } vpkpp_pack_file_options_t; +typedef struct { + int vpk_saveToDirectory; + uint32_t vpk_preloadBytes; +} vpkpp_entry_options_t; + #ifdef __cplusplus } // extern "C" #endif diff --git a/lang/c/include/vpkppc/PackFile.h b/lang/c/include/vpkppc/PackFile.h index 72972309a..cbf0f2dca 100644 --- a/lang/c/include/vpkppc/PackFile.h +++ b/lang/c/include/vpkppc/PackFile.h @@ -14,6 +14,10 @@ extern "C" { typedef void* vpkpp_pack_file_handle_t; +typedef void(*EntryCallback)(const char* path, vpkpp_entry_handle_t entry); + +typedef int(*EntryPredicate)(const char* path, vpkpp_entry_handle_t entry); + #ifdef __cplusplus } // extern "C" #endif @@ -21,85 +25,102 @@ typedef void* vpkpp_pack_file_handle_t; // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options(const char* path, vpkpp_pack_file_options_t options); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); + SOURCEPP_API vpkpp_pack_file_type_e vpkpp_get_type(vpkpp_pack_file_handle_t handle); SOURCEPP_API vpkpp_pack_file_options_t vpkpp_get_options(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_has_entry_checksums(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_has_entry_checksums(vpkpp_pack_file_handle_t handle); // REQUIRES MANUAL FREE: sourcepp_string_array_free SOURCEPP_API sourcepp_string_array_t vpkpp_verify_entry_checksums(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_has_pack_file_checksum(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_has_pack_file_checksum(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_verify_pack_file_checksum(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_verify_pack_file_checksum(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_has_pack_file_signature(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_has_pack_file_signature(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_verify_pack_file_signature(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_verify_pack_file_signature(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_is_case_sensitive(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_is_case_sensitive(vpkpp_pack_file_handle_t handle); -SOURCEPP_API bool vpkpp_has_entry(vpkpp_pack_file_handle_t handle, const char* filename, bool includeUnbaked); +SOURCEPP_API int vpkpp_has_entry(vpkpp_pack_file_handle_t handle, const char* path, int includeUnbaked); // REQUIRES MANUAL FREE: vpkpp_entry_free -SOURCEPP_API vpkpp_entry_handle_t vpkpp_find_entry(vpkpp_pack_file_handle_t handle, const char* filename, bool includeUnbaked); +SOURCEPP_API vpkpp_entry_handle_t vpkpp_find_entry(vpkpp_pack_file_handle_t handle, const char* path, int includeUnbaked); // REQUIRES MANUAL FREE: sourcepp_buffer_free -SOURCEPP_API sourcepp_buffer_t vpkpp_read_entry(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry); +SOURCEPP_API sourcepp_buffer_t vpkpp_read_entry(vpkpp_pack_file_handle_t handle, const char* path); // REQUIRES MANUAL FREE: sourcepp_string_free -SOURCEPP_API sourcepp_string_t vpkpp_read_entry_text(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry); +SOURCEPP_API sourcepp_string_t vpkpp_read_entry_text(vpkpp_pack_file_handle_t handle, const char* path); + +SOURCEPP_API int vpkpp_is_read_only(vpkpp_pack_file_handle_t handle); + +SOURCEPP_API void vpkpp_add_entry_from_file(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filepath); -SOURCEPP_API bool vpkpp_is_read_only(vpkpp_pack_file_handle_t handle); +SOURCEPP_API void vpkpp_add_entry_from_file_with_options(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filepath, vpkpp_entry_options_t options); -SOURCEPP_API void vpkpp_add_entry_from_file(vpkpp_pack_file_handle_t handle, const char* filename, const char* pathToFile); +SOURCEPP_API void vpkpp_add_entry_from_mem(vpkpp_pack_file_handle_t handle, const char* path, const unsigned char* buffer, size_t bufferLen); -SOURCEPP_API void vpkpp_add_entry_from_mem(vpkpp_pack_file_handle_t handle, const char* filename, const unsigned char* buffer, size_t bufferLen); +SOURCEPP_API void vpkpp_add_entry_from_mem_with_options(vpkpp_pack_file_handle_t handle, const char* path, const unsigned char* buffer, size_t bufferLen, vpkpp_entry_options_t options); -SOURCEPP_API bool vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* filename); +SOURCEPP_API int vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* path); -SOURCEPP_API bool vpkpp_bake(vpkpp_pack_file_handle_t handle, const char* outputDir); +SOURCEPP_API int vpkpp_remove_directory(vpkpp_pack_file_handle_t handle, const char* dirName); -SOURCEPP_API bool vpkpp_extract_entry(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry, const char* filePath); +SOURCEPP_API int vpkpp_bake(vpkpp_pack_file_handle_t handle, const char* outputDir); -SOURCEPP_API bool vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const char* dir, const char* outputDir); +SOURCEPP_API int vpkpp_bake_with_callback(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryCallback callback); -SOURCEPP_API bool vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* outputDir, bool createUnderPackFileDir); +SOURCEPP_API int vpkpp_extract_entry(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filepath); -SOURCEPP_API bool vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, bool(*predicate)(vpkpp_entry_handle_t)); +SOURCEPP_API int vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const char* dir, const char* outputDir); -// REQUIRES MANUAL FREE: vpkpp_entry_array_free -SOURCEPP_API vpkpp_entry_handle_array_t vpkpp_get_baked_entries(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* outputDir, int createUnderPackFileDir); -// REQUIRES MANUAL FREE: vpkpp_entry_array_free -SOURCEPP_API vpkpp_entry_handle_array_t vpkpp_get_unbaked_entries(vpkpp_pack_file_handle_t handle); +SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate); -SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, bool includeUnbaked); +SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, int includeUnbaked); -SOURCEPP_API size_t vpkpp_get_filepath(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +SOURCEPP_API void vpkpp_run_for_all_entries(vpkpp_pack_file_handle_t handle, EntryCallback operation, int includeUnbaked); -SOURCEPP_API size_t vpkpp_get_truncated_filepath(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_filepath(vpkpp_pack_file_handle_t handle); + +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filepath(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_get_filename(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_filename(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_get_truncated_filename(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filename(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_get_filestem(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_filestem(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_get_truncated_filestem(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filestem(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_get_supported_entry_attributes(vpkpp_pack_file_handle_t handle, vpkpp_attribute_e* buffer, size_t bufferLen); +SOURCEPP_API vpkpp_attribute_e vpkpp_get_supported_entry_attributes(vpkpp_pack_file_handle_t handle); -SOURCEPP_API size_t vpkpp_to_string(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen); +// REQUIRES MANUAL FREE: sourcepp_string_free +SOURCEPP_API sourcepp_string_t vpkpp_to_string(vpkpp_pack_file_handle_t handle); SOURCEPP_API void vpkpp_close(vpkpp_pack_file_handle_t* handle); // REQUIRES MANUAL FREE: sourcepp_string_free -SOURCEPP_API sourcepp_string_t vpkpp_escape_entry_path(const char* path); +SOURCEPP_API sourcepp_string_t vpkpp_escape_entry_path_for_write(const char* path); // REQUIRES MANUAL FREE: sourcepp_string_array_free SOURCEPP_API sourcepp_string_array_t vpkpp_get_supported_file_types(); diff --git a/lang/c/include/vpkppc/PackFileType.h b/lang/c/include/vpkppc/PackFileType.h index fe7baa1ff..914f30ae9 100644 --- a/lang/c/include/vpkppc/PackFileType.h +++ b/lang/c/include/vpkppc/PackFileType.h @@ -10,7 +10,6 @@ typedef enum { VPKPP_PACK_FILE_TYPE_FPX, VPKPP_PACK_FILE_TYPE_GCF, VPKPP_PACK_FILE_TYPE_GMA, - VPKPP_PACK_FILE_TYPE_GRP, VPKPP_PACK_FILE_TYPE_PAK, VPKPP_PACK_FILE_TYPE_PCK, VPKPP_PACK_FILE_TYPE_VPK, diff --git a/lang/c/include/vpkppc/format/BSP.h b/lang/c/include/vpkppc/format/BSP.h index 820003ff2..c01444686 100644 --- a/lang/c/include/vpkppc/format/BSP.h +++ b/lang/c/include/vpkppc/format/BSP.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/FPX.h b/lang/c/include/vpkppc/format/FPX.h index 93b7f13c1..cf6426c90 100644 --- a/lang/c/include/vpkppc/format/FPX.h +++ b/lang/c/include/vpkppc/format/FPX.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/GCF.h b/lang/c/include/vpkppc/format/GCF.h index 45725972c..18e5edb5a 100644 --- a/lang/c/include/vpkppc/format/GCF.h +++ b/lang/c/include/vpkppc/format/GCF.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/GMA.h b/lang/c/include/vpkppc/format/GMA.h index a8b43cc5d..488017003 100644 --- a/lang/c/include/vpkppc/format/GMA.h +++ b/lang/c/include/vpkppc/format/GMA.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/GRP.h b/lang/c/include/vpkppc/format/GRP.h deleted file mode 100644 index 44be3fc04..000000000 --- a/lang/c/include/vpkppc/format/GRP.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../PackFile.h" - -// REQUIRES MANUAL FREE: vpkpp_close -SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open(const char* path); - -// REQUIRES MANUAL FREE: vpkpp_close -SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open_with_options(const char* path, vpkpp_pack_file_options_t options); diff --git a/lang/c/include/vpkppc/format/PAK.h b/lang/c/include/vpkppc/format/PAK.h index 1bdb75333..ee7151c02 100644 --- a/lang/c/include/vpkppc/format/PAK.h +++ b/lang/c/include/vpkppc/format/PAK.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/PCK.h b/lang/c/include/vpkppc/format/PCK.h index 0a89f60bd..144d9a1a2 100644 --- a/lang/c/include/vpkppc/format/PCK.h +++ b/lang/c/include/vpkppc/format/PCK.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/VPK.h b/lang/c/include/vpkppc/format/VPK.h index 91a5934c5..f2cc3a139 100644 --- a/lang/c/include/vpkppc/format/VPK.h +++ b/lang/c/include/vpkppc/format/VPK.h @@ -11,15 +11,27 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_empty_with_options(const // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory(const char* vpkPath, const char* contentPath, bool saveToDir); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_callback(const char* vpkPath, const char* contentPath, bool saveToDir, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_options(const char* vpkPath, const char* contentPath, bool saveToDir, vpkpp_pack_file_options_t options); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_options_and_callback(const char* vpkPath, const char* contentPath, bool saveToDir, vpkpp_pack_file_options_t options, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options(const char* path, vpkpp_pack_file_options_t options); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); + SOURCEPP_API bool vpkpp_vpk_generate_keypair_files(const char* path); SOURCEPP_API bool vpkpp_vpk_sign_from_file(vpkpp_pack_file_handle_t handle, const char* filename); diff --git a/lang/c/include/vpkppc/format/VPK_VTMB.h b/lang/c/include/vpkppc/format/VPK_VTMB.h index 504c69ab9..bc07c08a4 100644 --- a/lang/c/include/vpkppc/format/VPK_VTMB.h +++ b/lang/c/include/vpkppc/format/VPK_VTMB.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/include/vpkppc/format/ZIP.h b/lang/c/include/vpkppc/format/ZIP.h index f472c05ea..6245d01cc 100644 --- a/lang/c/include/vpkppc/format/ZIP.h +++ b/lang/c/include/vpkppc/format/ZIP.h @@ -5,5 +5,11 @@ // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open(const char* path); +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_callback(const char* path, EntryCallback callback); + // REQUIRES MANUAL FREE: vpkpp_close SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options(const char* path, vpkpp_pack_file_options_t options); + +// REQUIRES MANUAL FREE: vpkpp_close +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback); diff --git a/lang/c/src/vpkppc/Convert.cpp b/lang/c/src/vpkppc/Convert.cpp index 2f467bf5f..111fc22ec 100644 --- a/lang/c/src/vpkppc/Convert.cpp +++ b/lang/c/src/vpkppc/Convert.cpp @@ -14,14 +14,21 @@ Entry* Convert::entry(vpkpp_entry_handle_t handle) { PackFileOptions Convert::optionsFromC(vpkpp_pack_file_options_t options) { return { - .gma_writeCRCs = options.gma_writeCRCs, + .gma_writeCRCs = static_cast(options.gma_writeCRCs), .vpk_version = options.vpk_version, .vpk_preferredChunkSize = options.vpk_preferredChunkSize, - .vpk_generateMD5Entries = options.vpk_generateMD5Entries, + .vpk_generateMD5Entries = static_cast(options.vpk_generateMD5Entries), .zip_compressionMethod = options.zip_compressionMethod, }; } +EntryOptions Convert::optionsFromC(vpkpp_entry_options_t options) { + return { + .vpk_saveToDirectory = static_cast(options.vpk_saveToDirectory), + .vpk_preloadBytes = options.vpk_preloadBytes, + }; +} + vpkpp_pack_file_options_t Convert::optionsToC(PackFileOptions options) { return { .gma_writeCRCs = options.gma_writeCRCs, @@ -31,3 +38,10 @@ vpkpp_pack_file_options_t Convert::optionsToC(PackFileOptions options) { .zip_compressionMethod = options.zip_compressionMethod, }; } + +vpkpp_entry_options_t Convert::optionsToC(EntryOptions options) { + return { + .vpk_saveToDirectory = options.vpk_saveToDirectory, + .vpk_preloadBytes = options.vpk_preloadBytes, + }; +} diff --git a/lang/c/src/vpkppc/Entry.cpp b/lang/c/src/vpkppc/Entry.cpp index 449e79c69..efad90c5f 100644 --- a/lang/c/src/vpkppc/Entry.cpp +++ b/lang/c/src/vpkppc/Entry.cpp @@ -8,44 +8,52 @@ using namespace vpkpp; -SOURCEPP_API size_t vpkpp_entry_get_path(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen) { +SOURCEPP_API uint32_t vpkpp_entry_get_flags(vpkpp_entry_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); - return Convert::writeStringToMem(Convert::entry(handle)->path, buffer, bufferLen); + return Convert::entry(handle)->flags; } -SOURCEPP_API size_t vpkpp_entry_get_parent_path(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen) { +SOURCEPP_API uint32_t vpkpp_entry_get_archive_index(vpkpp_entry_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); - return Convert::writeStringToMem(Convert::entry(handle)->getParentPath(), buffer, bufferLen); + return Convert::entry(handle)->archiveIndex; } -SOURCEPP_API size_t vpkpp_entry_get_filename(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen) { +SOURCEPP_API uint64_t vpkpp_entry_get_length(vpkpp_entry_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); - return Convert::writeStringToMem(Convert::entry(handle)->getFilename(), buffer, bufferLen); + return Convert::entry(handle)->length; } -SOURCEPP_API size_t vpkpp_entry_get_stem(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen) { +SOURCEPP_API uint64_t vpkpp_entry_get_compressed_length(vpkpp_entry_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); - return Convert::writeStringToMem(Convert::entry(handle)->getStem(), buffer, bufferLen); + return Convert::entry(handle)->compressedLength; } -SOURCEPP_API size_t vpkpp_entry_get_extension(vpkpp_entry_handle_t handle, char* buffer, size_t bufferLen) { +SOURCEPP_API uint64_t vpkpp_entry_get_offset(vpkpp_entry_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); - return Convert::writeStringToMem(Convert::entry(handle)->getExtension(), buffer, bufferLen); + return Convert::entry(handle)->offset; +} + +SOURCEPP_API sourcepp_buffer_t vpkpp_entry_get_extra_data(vpkpp_entry_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_BUFFER_INVALID); + + return Convert::toBuffer(Convert::entry(handle)->extraData); +} + +SOURCEPP_API uint32_t vpkpp_entry_get_crc32(vpkpp_entry_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, 0); + + return Convert::entry(handle)->crc32; +} + +SOURCEPP_API int vpkpp_entry_is_unbaked(vpkpp_entry_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, false); + + return Convert::entry(handle)->unbaked; } SOURCEPP_API void vpkpp_entry_free(vpkpp_entry_handle_t* handle) { @@ -54,18 +62,3 @@ SOURCEPP_API void vpkpp_entry_free(vpkpp_entry_handle_t* handle) { delete Convert::entry(*handle); *handle = nullptr; } - -SOURCEPP_API void vpkpp_entry_array_free(vpkpp_entry_handle_array_t* array) { - SOURCEPP_EARLY_RETURN(array); - - if (array->data) { - for (size_t i = 0; i < array->size; i++) { - if (auto*& entry = array->data[i]) { - vpkpp_entry_free(&entry); - } - } - std::free(array->data); - array->data = nullptr; - } - array->size = 0; -} diff --git a/lang/c/src/vpkppc/PackFile.cpp b/lang/c/src/vpkppc/PackFile.cpp index d347f09ac..0b6999cdd 100644 --- a/lang/c/src/vpkppc/PackFile.cpp +++ b/lang/c/src/vpkppc/PackFile.cpp @@ -23,6 +23,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PackFile::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -33,6 +46,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options(const char* path, return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PackFile::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_type_e vpkpp_get_type(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, VPKPP_PACK_FILE_TYPE_UNKNOWN); @@ -43,7 +69,7 @@ SOURCEPP_API vpkpp_pack_file_options_t vpkpp_get_options(vpkpp_pack_file_handle_ return Convert::optionsToC(Convert::packFile(handle)->getOptions()); } -SOURCEPP_API bool vpkpp_has_entry_checksums(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_has_entry_checksums(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->hasEntryChecksums(); @@ -55,118 +81,154 @@ SOURCEPP_API sourcepp_string_array_t vpkpp_verify_entry_checksums(vpkpp_pack_fil return Convert::toStringArray(Convert::packFile(handle)->verifyEntryChecksums()); } -SOURCEPP_API bool vpkpp_has_pack_file_checksum(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_has_pack_file_checksum(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->hasPackFileChecksum(); } -SOURCEPP_API bool vpkpp_verify_pack_file_checksum(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_verify_pack_file_checksum(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->verifyPackFileChecksum(); } -SOURCEPP_API bool vpkpp_has_pack_file_signature(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_has_pack_file_signature(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->hasPackFileSignature(); } -SOURCEPP_API bool vpkpp_verify_pack_file_signature(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_verify_pack_file_signature(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->verifyPackFileSignature(); } -SOURCEPP_API bool vpkpp_is_case_sensitive(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_is_case_sensitive(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->isCaseSensitive(); } -SOURCEPP_API bool vpkpp_has_entry(vpkpp_pack_file_handle_t handle, const char* filename, bool includeUnbaked) { +SOURCEPP_API int vpkpp_has_entry(vpkpp_pack_file_handle_t handle, const char* path, int includeUnbaked) { SOURCEPP_EARLY_RETURN_VAL(handle, false); - SOURCEPP_EARLY_RETURN_VAL(filename, false); + SOURCEPP_EARLY_RETURN_VAL(path, false); - return Convert::packFile(handle)->hasEntry(filename, includeUnbaked); + return Convert::packFile(handle)->hasEntry(path, includeUnbaked); } -SOURCEPP_API vpkpp_entry_handle_t vpkpp_find_entry(vpkpp_pack_file_handle_t handle, const char* filename, bool includeUnbaked) { +SOURCEPP_API vpkpp_entry_handle_t vpkpp_find_entry(vpkpp_pack_file_handle_t handle, const char* path, int includeUnbaked) { SOURCEPP_EARLY_RETURN_VAL(handle, nullptr); - SOURCEPP_EARLY_RETURN_VAL(filename, nullptr); + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); - auto entry = Convert::packFile(handle)->findEntry(filename, includeUnbaked); + auto entry = Convert::packFile(handle)->findEntry(path, includeUnbaked); if (!entry) { return nullptr; } return new Entry(std::move(*entry)); } -SOURCEPP_API sourcepp_buffer_t vpkpp_read_entry(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry) { +SOURCEPP_API sourcepp_buffer_t vpkpp_read_entry(vpkpp_pack_file_handle_t handle, const char* path) { SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_BUFFER_INVALID); - SOURCEPP_EARLY_RETURN_VAL(entry, SOURCEPP_BUFFER_INVALID); + SOURCEPP_EARLY_RETURN_VAL(path, SOURCEPP_BUFFER_INVALID); - if (auto binary = Convert::packFile(handle)->readEntry(*Convert::entry(entry))) { + if (auto binary = Convert::packFile(handle)->readEntry(path)) { return Convert::toBuffer(*binary); } return SOURCEPP_BUFFER_INVALID; } -SOURCEPP_API sourcepp_string_t vpkpp_read_entry_text(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry) { +SOURCEPP_API sourcepp_string_t vpkpp_read_entry_text(vpkpp_pack_file_handle_t handle, const char* path) { SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - SOURCEPP_EARLY_RETURN_VAL(entry, SOURCEPP_STRING_INVALID); + SOURCEPP_EARLY_RETURN_VAL(path, SOURCEPP_STRING_INVALID); - if (auto text = Convert::packFile(handle)->readEntryText(*Convert::entry(entry))) { + if (auto text = Convert::packFile(handle)->readEntryText(path)) { return Convert::toString(*text); } return SOURCEPP_STRING_INVALID; } -SOURCEPP_API bool vpkpp_is_read_only(vpkpp_pack_file_handle_t handle) { +SOURCEPP_API int vpkpp_is_read_only(vpkpp_pack_file_handle_t handle) { SOURCEPP_EARLY_RETURN_VAL(handle, false); return Convert::packFile(handle)->isReadOnly(); } -SOURCEPP_API void vpkpp_add_entry_from_file(vpkpp_pack_file_handle_t handle, const char* filename, const char* pathToFile) { +SOURCEPP_API void vpkpp_add_entry_from_file(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filepath) { SOURCEPP_EARLY_RETURN(handle); - SOURCEPP_EARLY_RETURN(filename); - SOURCEPP_EARLY_RETURN(pathToFile); + SOURCEPP_EARLY_RETURN(entryPath); + SOURCEPP_EARLY_RETURN(filepath); - Convert::packFile(handle)->addEntry(filename, pathToFile, {}); + Convert::packFile(handle)->addEntry(entryPath, filepath, {}); } -SOURCEPP_API void vpkpp_add_entry_from_mem(vpkpp_pack_file_handle_t handle, const char* filename, const unsigned char* buffer, size_t bufferLen) { +SOURCEPP_API void vpkpp_add_entry_from_file_with_options(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filepath, vpkpp_entry_options_t options) { SOURCEPP_EARLY_RETURN(handle); - SOURCEPP_EARLY_RETURN(filename); + SOURCEPP_EARLY_RETURN(entryPath); + SOURCEPP_EARLY_RETURN(filepath); + + Convert::packFile(handle)->addEntry(entryPath, filepath, Convert::optionsFromC(options)); +} + +SOURCEPP_API void vpkpp_add_entry_from_mem(vpkpp_pack_file_handle_t handle, const char* path, const unsigned char* buffer, size_t bufferLen) { + SOURCEPP_EARLY_RETURN(handle); + SOURCEPP_EARLY_RETURN(path); + SOURCEPP_EARLY_RETURN(buffer); + SOURCEPP_EARLY_RETURN(bufferLen); + + Convert::packFile(handle)->addEntry(path, reinterpret_cast(buffer), bufferLen, {}); +} + +SOURCEPP_API void vpkpp_add_entry_from_mem_with_options(vpkpp_pack_file_handle_t handle, const char* path, const unsigned char* buffer, size_t bufferLen, vpkpp_entry_options_t options) { + SOURCEPP_EARLY_RETURN(handle); + SOURCEPP_EARLY_RETURN(path); + SOURCEPP_EARLY_RETURN(buffer); + SOURCEPP_EARLY_RETURN(bufferLen); + + Convert::packFile(handle)->addEntry(path, reinterpret_cast(buffer), bufferLen, Convert::optionsFromC(options)); +} - Convert::packFile(handle)->addEntry(filename, reinterpret_cast(buffer), bufferLen, {}); +SOURCEPP_API int vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* path) { + SOURCEPP_EARLY_RETURN_VAL(handle, false); + SOURCEPP_EARLY_RETURN_VAL(path, false); + + return Convert::packFile(handle)->removeEntry(path); } -SOURCEPP_API bool vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* filename) { +SOURCEPP_API int vpkpp_remove_directory(vpkpp_pack_file_handle_t handle, const char* dirName) { SOURCEPP_EARLY_RETURN_VAL(handle, false); - SOURCEPP_EARLY_RETURN_VAL(filename, false); + SOURCEPP_EARLY_RETURN_VAL(dirName, false); - return Convert::packFile(handle)->removeEntry(filename); + return Convert::packFile(handle)->removeEntry(dirName); } -SOURCEPP_API bool vpkpp_bake(vpkpp_pack_file_handle_t handle, const char* outputDir) { +SOURCEPP_API int vpkpp_bake(vpkpp_pack_file_handle_t handle, const char* outputDir) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(outputDir, false); return Convert::packFile(handle)->bake(outputDir, nullptr); } -SOURCEPP_API bool vpkpp_extract_entry(vpkpp_pack_file_handle_t handle, vpkpp_entry_handle_t entry, const char* filePath) { +SOURCEPP_API int vpkpp_bake_with_callback(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(handle, false); + SOURCEPP_EARLY_RETURN_VAL(outputDir, false); + SOURCEPP_EARLY_RETURN_VAL(callback, false); + + return Convert::packFile(handle)->bake(outputDir, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); +} + +SOURCEPP_API int vpkpp_extract_entry(vpkpp_pack_file_handle_t handle, const char* entryPath, const char* filePath) { SOURCEPP_EARLY_RETURN_VAL(handle, false); - SOURCEPP_EARLY_RETURN_VAL(entry, false); + SOURCEPP_EARLY_RETURN_VAL(entryPath, false); SOURCEPP_EARLY_RETURN_VAL(filePath, false); - return Convert::packFile(handle)->extractEntry(*Convert::entry(entry), filePath); + return Convert::packFile(handle)->extractEntry(entryPath, filePath); } -SOURCEPP_API bool vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const char* dir, const char* outputDir) { +SOURCEPP_API int vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const char* dir, const char* outputDir) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(dir, false); SOURCEPP_EARLY_RETURN_VAL(outputDir, false); @@ -174,137 +236,82 @@ SOURCEPP_API bool vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const return Convert::packFile(handle)->extractDirectory(dir, outputDir); } -SOURCEPP_API bool vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* outputDir, bool createUnderPackFileDir) { +SOURCEPP_API int vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* outputDir, int createUnderPackFileDir) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(outputDir, false); return Convert::packFile(handle)->extractAll(outputDir, createUnderPackFileDir); } -SOURCEPP_API bool vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, bool(*predicate)(vpkpp_entry_handle_t)) { +SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(outputDir, false); SOURCEPP_EARLY_RETURN_VAL(predicate, false); - return Convert::packFile(handle)->extractAll(outputDir, [predicate](const Entry& entry) { - return predicate(reinterpret_cast(const_cast(&entry))); + return Convert::packFile(handle)->extractAll(outputDir, [predicate](const std::string& path, const Entry& entry) { + return predicate(path.c_str(), const_cast(&entry)); }); } -SOURCEPP_API vpkpp_entry_handle_array_t vpkpp_get_baked_entries(vpkpp_pack_file_handle_t handle) { - SOURCEPP_EARLY_RETURN_VAL(handle, VPKPP_ENTRY_HANDLE_ARRAY_INVALID); - - std::vector heapEntries; - for (const auto& [dir, entries] : Convert::packFile(handle)->getBakedEntries()) { - for (const auto& entry : entries) { - heapEntries.push_back(new Entry{entry}); - } - } - - vpkpp_entry_handle_array_t array; - array.size = static_cast(heapEntries.size()); - array.data = static_cast(std::malloc(sizeof(vpkpp_entry_handle_t) * array.size)); - - for (size_t i = 0; i < array.size; i++) { - array.data[i] = heapEntries[i]; - } - return array; +SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, int includeUnbaked) { + return Convert::packFile(handle)->getEntryCount(includeUnbaked); } -SOURCEPP_API vpkpp_entry_handle_array_t vpkpp_get_unbaked_entries(vpkpp_pack_file_handle_t handle) { - SOURCEPP_EARLY_RETURN_VAL(handle, VPKPP_ENTRY_HANDLE_ARRAY_INVALID); - - std::vector heapEntries; - for (const auto& [dir, entries] : Convert::packFile(handle)->getUnbakedEntries()) { - for (const auto& entry : entries) { - heapEntries.push_back(new Entry{entry}); - } - } - - vpkpp_entry_handle_array_t array; - array.size = static_cast(heapEntries.size()); - array.data = static_cast(std::malloc(sizeof(vpkpp_entry_handle_t) * array.size)); - - for (size_t i = 0; i < array.size; i++) { - array.data[i] = heapEntries[i]; - } - return array; -} +SOURCEPP_API void vpkpp_run_for_all_entries(vpkpp_pack_file_handle_t handle, EntryCallback operation, int includeUnbaked) { + SOURCEPP_EARLY_RETURN(handle); + SOURCEPP_EARLY_RETURN(operation); -SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, bool includeUnbaked) { - return Convert::packFile(handle)->getEntryCount(includeUnbaked); + return const_cast(Convert::packFile(handle))->runForAllEntries([operation](const std::string& path, const Entry& entry) { + return operation(path.c_str(), const_cast(&entry)); + }, includeUnbaked); } -SOURCEPP_API size_t vpkpp_get_filepath(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_filepath(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getFilepath(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getFilepath()); } -SOURCEPP_API size_t vpkpp_get_truncated_filepath(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filepath(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getTruncatedFilepath(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getTruncatedFilepath()); } -SOURCEPP_API size_t vpkpp_get_filename(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_filename(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getFilename(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getFilename()); } -SOURCEPP_API size_t vpkpp_get_truncated_filename(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filename(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getTruncatedFilename(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getTruncatedFilename()); } -SOURCEPP_API size_t vpkpp_get_filestem(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_filestem(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getFilestem(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getFilestem()); } -SOURCEPP_API size_t vpkpp_get_truncated_filestem(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_get_truncated_filestem(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(Convert::packFile(handle)->getTruncatedFilestem(), buffer, bufferLen); + return Convert::toString(Convert::packFile(handle)->getTruncatedFilestem()); } -SOURCEPP_API size_t vpkpp_get_supported_entry_attributes(vpkpp_pack_file_handle_t handle, vpkpp_attribute_e* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API vpkpp_attribute_e vpkpp_get_supported_entry_attributes(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, VPKPP_ATTRIBUTE_NONE); - auto attrs = Convert::packFile(handle)->getSupportedEntryAttributes(); - for (size_t i = 0; i < bufferLen; i++) { - if (i < attrs.size()) { - buffer[i] = static_cast(attrs[i]); - } else { - buffer[i] = VPKPP_ATTRIBUTE_NONE; - } - } - return std::min(attrs.size(), bufferLen); + return static_cast(Convert::packFile(handle)->getSupportedEntryAttributes()); } -SOURCEPP_API size_t vpkpp_to_string(vpkpp_pack_file_handle_t handle, char* buffer, size_t bufferLen) { - SOURCEPP_EARLY_RETURN_VAL(handle, 0); - SOURCEPP_EARLY_RETURN_VAL(buffer, 0); - SOURCEPP_EARLY_RETURN_VAL(bufferLen, 0); +SOURCEPP_API sourcepp_string_t vpkpp_to_string(vpkpp_pack_file_handle_t handle) { + SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID); - return Convert::writeStringToMem(std::string{*Convert::packFile(handle)}, buffer, bufferLen); + return Convert::toString(std::string{*Convert::packFile(handle)}); } SOURCEPP_API void vpkpp_close(vpkpp_pack_file_handle_t* handle) { @@ -314,10 +321,10 @@ SOURCEPP_API void vpkpp_close(vpkpp_pack_file_handle_t* handle) { *handle = nullptr; } -SOURCEPP_API sourcepp_string_t vpkpp_escape_entry_path(const char* path) { +SOURCEPP_API sourcepp_string_t vpkpp_escape_entry_path_for_write(const char* path) { SOURCEPP_EARLY_RETURN_VAL(path, SOURCEPP_STRING_INVALID); - return Convert::toString(PackFile::escapeEntryPath(path)); + return Convert::toString(PackFile::escapeEntryPathForWrite(path)); } SOURCEPP_API sourcepp_string_array_t vpkpp_get_supported_file_types() { diff --git a/lang/c/src/vpkppc/_vpkppc.cmake b/lang/c/src/vpkppc/_vpkppc.cmake index 04fb5855c..d06246d84 100644 --- a/lang/c/src/vpkppc/_vpkppc.cmake +++ b/lang/c/src/vpkppc/_vpkppc.cmake @@ -1,10 +1,10 @@ add_pretty_parser(vpkpp C + DEPS tsl::hat_trie PRECOMPILED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/BSP.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/FPX.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/GCF.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/GMA.h" - "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/GRP.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/PAK.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/PCK.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/VPK.h" @@ -22,7 +22,6 @@ add_pretty_parser(vpkpp C "${CMAKE_CURRENT_LIST_DIR}/format/FPX.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GCF.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/GMA.cpp" - "${CMAKE_CURRENT_LIST_DIR}/format/GRP.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/PAK.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/PCK.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/VPK.cpp" diff --git a/lang/c/src/vpkppc/format/BSP.cpp b/lang/c/src/vpkppc/format/BSP.cpp index 86786450c..e0f422e0f 100644 --- a/lang/c/src/vpkppc/format/BSP.cpp +++ b/lang/c/src/vpkppc/format/BSP.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = BSP::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = BSP::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/FPX.cpp b/lang/c/src/vpkppc/format/FPX.cpp index 16e5181cd..34edb8fcf 100644 --- a/lang/c/src/vpkppc/format/FPX.cpp +++ b/lang/c/src/vpkppc/format/FPX.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = FPX::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = FPX::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/GCF.cpp b/lang/c/src/vpkppc/format/GCF.cpp index 499e9bace..abdd58879 100644 --- a/lang/c/src/vpkppc/format/GCF.cpp +++ b/lang/c/src/vpkppc/format/GCF.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = GCF::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = GCF::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/GMA.cpp b/lang/c/src/vpkppc/format/GMA.cpp index 4315a6336..cb72c801f 100644 --- a/lang/c/src/vpkppc/format/GMA.cpp +++ b/lang/c/src/vpkppc/format/GMA.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = GMA::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = GMA::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/GRP.cpp b/lang/c/src/vpkppc/format/GRP.cpp deleted file mode 100644 index 580e9140b..000000000 --- a/lang/c/src/vpkppc/format/GRP.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include - -#include -#include - -using namespace vpkpp; - -SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open(const char* path) { - SOURCEPP_EARLY_RETURN_VAL(path, nullptr); - - auto packFile = GRP::open(path); - if (!packFile) { - return nullptr; - } - return packFile.release(); -} - -SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open_with_options(const char* path, vpkpp_pack_file_options_t options) { - SOURCEPP_EARLY_RETURN_VAL(path, nullptr); - - auto packFile = GRP::open(path, Convert::optionsFromC(options)); - if (!packFile) { - return nullptr; - } - return packFile.release(); -} diff --git a/lang/c/src/vpkppc/format/PAK.cpp b/lang/c/src/vpkppc/format/PAK.cpp index 9547de352..e5b3f48fa 100644 --- a/lang/c/src/vpkppc/format/PAK.cpp +++ b/lang/c/src/vpkppc/format/PAK.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PAK::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PAK::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/PCK.cpp b/lang/c/src/vpkppc/format/PCK.cpp index 6bfebeb58..d221d40b4 100644 --- a/lang/c/src/vpkppc/format/PCK.cpp +++ b/lang/c/src/vpkppc/format/PCK.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PCK::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = PCK::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/VPK.cpp b/lang/c/src/vpkppc/format/VPK.cpp index 24b41da88..822ad4395 100644 --- a/lang/c/src/vpkppc/format/VPK.cpp +++ b/lang/c/src/vpkppc/format/VPK.cpp @@ -20,7 +20,7 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_empty(const char* path) { SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_empty_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); - auto packFile = PackFile::open(path, Convert::optionsFromC(options)); + auto packFile = VPK::createEmpty(path, Convert::optionsFromC(options)); if (!packFile) { return nullptr; } @@ -38,6 +38,20 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory(const char return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_callback(const char* vpkPath, const char* contentPath, bool saveToDir, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(vpkPath, nullptr); + SOURCEPP_EARLY_RETURN_VAL(contentPath, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK::createFromDirectory(vpkPath, contentPath, saveToDir, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_options(const char* vpkPath, const char* contentPath, bool saveToDir, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(vpkPath, nullptr); SOURCEPP_EARLY_RETURN_VAL(contentPath, nullptr); @@ -49,6 +63,20 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_optio return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_options_and_callback(const char* vpkPath, const char* contentPath, bool saveToDir, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(vpkPath, nullptr); + SOURCEPP_EARLY_RETURN_VAL(contentPath, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK::createFromDirectory(vpkPath, contentPath, saveToDir, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open(const char* path) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -59,6 +87,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -69,6 +110,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options(const char* pa return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API bool vpkpp_vpk_generate_keypair_files(const char* path) { SOURCEPP_EARLY_RETURN_VAL(path, false); diff --git a/lang/c/src/vpkppc/format/VPK_VTMB.cpp b/lang/c/src/vpkppc/format/VPK_VTMB.cpp index e5bba5b79..22d83edbb 100644 --- a/lang/c/src/vpkppc/format/VPK_VTMB.cpp +++ b/lang/c/src/vpkppc/format/VPK_VTMB.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK_VTMB::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_options(const cha } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_vtmb_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = VPK_VTMB::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/lang/c/src/vpkppc/format/ZIP.cpp b/lang/c/src/vpkppc/format/ZIP.cpp index 28235a3f1..9c066f378 100644 --- a/lang/c/src/vpkppc/format/ZIP.cpp +++ b/lang/c/src/vpkppc/format/ZIP.cpp @@ -17,6 +17,19 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open(const char* path) { return packFile.release(); } +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_callback(const char* path, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = ZIP::open(path, {}, [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} + SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options(const char* path, vpkpp_pack_file_options_t options) { SOURCEPP_EARLY_RETURN_VAL(path, nullptr); @@ -26,3 +39,16 @@ SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options(const char* pa } return packFile.release(); } + +SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options_and_callback(const char* path, vpkpp_pack_file_options_t options, EntryCallback callback) { + SOURCEPP_EARLY_RETURN_VAL(path, nullptr); + SOURCEPP_EARLY_RETURN_VAL(callback, nullptr); + + auto packFile = ZIP::open(path, Convert::optionsFromC(options), [callback](const std::string& path, const Entry& entry) { + callback(path.c_str(), const_cast(&entry)); + }); + if (!packFile) { + return nullptr; + } + return packFile.release(); +} diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index fa180f458..5d3e1f1ed 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -476,8 +476,8 @@ std::string PackFile::getTruncatedFilestem() const { return this->getFilestem(); } -std::vector PackFile::getSupportedEntryAttributes() const { - return {}; +Attribute PackFile::getSupportedEntryAttributes() const { + return Attribute::NONE; } PackFile::operator std::string() const { diff --git a/src/vpkpp/format/GCF.cpp b/src/vpkpp/format/GCF.cpp index 3755c754f..87921875e 100644 --- a/src/vpkpp/format/GCF.cpp +++ b/src/vpkpp/format/GCF.cpp @@ -283,9 +283,9 @@ std::optional> GCF::readEntry(const std::string& path_) c return filedata; } -std::vector GCF::getSupportedEntryAttributes() const { +Attribute GCF::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH}; + return LENGTH; } GCF::operator std::string() const { diff --git a/src/vpkpp/format/GMA.cpp b/src/vpkpp/format/GMA.cpp index bd67b968f..91181d79c 100644 --- a/src/vpkpp/format/GMA.cpp +++ b/src/vpkpp/format/GMA.cpp @@ -200,9 +200,9 @@ bool GMA::bake(const std::string& outputDir_, const EntryCallback& callback) { return true; } -std::vector GMA::getSupportedEntryAttributes() const { +Attribute GMA::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH, CRC32}; + return LENGTH | CRC32; } GMA::operator std::string() const { diff --git a/src/vpkpp/format/PAK.cpp b/src/vpkpp/format/PAK.cpp index 2482a20dc..c67cd4ae6 100644 --- a/src/vpkpp/format/PAK.cpp +++ b/src/vpkpp/format/PAK.cpp @@ -139,7 +139,7 @@ bool PAK::bake(const std::string& outputDir_, const EntryCallback& callback) { return true; } -std::vector PAK::getSupportedEntryAttributes() const { +Attribute PAK::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH}; + return LENGTH; } diff --git a/src/vpkpp/format/PCK.cpp b/src/vpkpp/format/PCK.cpp index 60b4ab76c..c2cd6e1fb 100644 --- a/src/vpkpp/format/PCK.cpp +++ b/src/vpkpp/format/PCK.cpp @@ -273,9 +273,9 @@ bool PCK::bake(const std::string& outputDir_, const EntryCallback& callback) { return true; } -std::vector PCK::getSupportedEntryAttributes() const { +Attribute PCK::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH, PCK_MD5}; + return LENGTH | PCK_MD5; } PCK::operator std::string() const { diff --git a/src/vpkpp/format/VPK.cpp b/src/vpkpp/format/VPK.cpp index d691c15f0..eb3f59974 100644 --- a/src/vpkpp/format/VPK.cpp +++ b/src/vpkpp/format/VPK.cpp @@ -677,9 +677,9 @@ std::string VPK::getTruncatedFilestem() const { return filestem; } -std::vector VPK::getSupportedEntryAttributes() const { +Attribute VPK::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH, VPK_PRELOADED_DATA_LENGTH, ARCHIVE_INDEX, CRC32}; + return LENGTH | VPK_PRELOADED_DATA_LENGTH | ARCHIVE_INDEX | CRC32; } VPK::operator std::string() const { diff --git a/src/vpkpp/format/VPK_VTMB.cpp b/src/vpkpp/format/VPK_VTMB.cpp index 49c6dcf73..7dfe45132 100644 --- a/src/vpkpp/format/VPK_VTMB.cpp +++ b/src/vpkpp/format/VPK_VTMB.cpp @@ -197,7 +197,7 @@ std::string VPK_VTMB::getTruncatedFilestem() const { return "pack"; } -std::vector VPK_VTMB::getSupportedEntryAttributes() const { +Attribute VPK_VTMB::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH, ARCHIVE_INDEX}; + return LENGTH | ARCHIVE_INDEX; } diff --git a/src/vpkpp/format/ZIP.cpp b/src/vpkpp/format/ZIP.cpp index 946a238bd..19dc39d2f 100644 --- a/src/vpkpp/format/ZIP.cpp +++ b/src/vpkpp/format/ZIP.cpp @@ -127,9 +127,9 @@ bool ZIP::bake(const std::string& outputDir_, const EntryCallback& callback) { return true; } -std::vector ZIP::getSupportedEntryAttributes() const { +Attribute ZIP::getSupportedEntryAttributes() const { using enum Attribute; - return {LENGTH, CRC32}; + return LENGTH | CRC32; } #ifdef VPKPP_ZIP_COMPRESSION From 2f49c972d5d317c76905255ee8b07278a4ed14cc Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 01:26:44 -0400 Subject: [PATCH 03/10] feat(vpkpp): update C# wrapper --- lang/csharp/src/vpkpp/Attribute.cs | 13 +- lang/csharp/src/vpkpp/Constants.cs | 11 - lang/csharp/src/vpkpp/Entry.cs | 129 ++++------ lang/csharp/src/vpkpp/Format/BSP.cs | 35 +++ lang/csharp/src/vpkpp/Format/FPX.cs | 37 ++- lang/csharp/src/vpkpp/Format/GCF.cs | 35 +++ lang/csharp/src/vpkpp/Format/GMA.cs | 35 +++ lang/csharp/src/vpkpp/Format/GRP.cs | 36 --- lang/csharp/src/vpkpp/Format/PAK.cs | 35 +++ lang/csharp/src/vpkpp/Format/PCK.cs | 35 +++ lang/csharp/src/vpkpp/Format/VPK.cs | 34 +++ lang/csharp/src/vpkpp/Format/VPK_VTMB.cs | 35 +++ lang/csharp/src/vpkpp/Format/ZIP.cs | 35 +++ lang/csharp/src/vpkpp/PackFile.cs | 262 +++++++++++--------- lang/csharp/src/vpkpp/PackFileType.cs | 1 - lang/csharp/test/vpkpp.test/PackFileTest.cs | 24 +- 16 files changed, 516 insertions(+), 276 deletions(-) delete mode 100644 lang/csharp/src/vpkpp/Constants.cs delete mode 100644 lang/csharp/src/vpkpp/Format/GRP.cs diff --git a/lang/csharp/src/vpkpp/Attribute.cs b/lang/csharp/src/vpkpp/Attribute.cs index e3fc26437..47c919ca0 100644 --- a/lang/csharp/src/vpkpp/Attribute.cs +++ b/lang/csharp/src/vpkpp/Attribute.cs @@ -2,12 +2,11 @@ namespace vpkpp { public enum Attribute { - NONE = -1, - LENGTH = 0, - VPK_PRELOADED_DATA_LENGTH, - ARCHIVE_INDEX, - CRC32, - PCK_MD5, - COUNT, + NONE = 0, + LENGTH = 1 << 0, + VPK_PRELOADED_DATA_LENGTH = 1 << 1, + ARCHIVE_INDEX = 1 << 2, + CRC32 = 1 << 3, + PCK_MD5 = 1 << 4, } } diff --git a/lang/csharp/src/vpkpp/Constants.cs b/lang/csharp/src/vpkpp/Constants.cs deleted file mode 100644 index f2f9b8690..000000000 --- a/lang/csharp/src/vpkpp/Constants.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace vpkpp -{ - internal static class Constants - { - public const int MaxPath = 4096; // This seems reasonable - - public const int MaxFilename = 1024; // Again, seems reasonable - - public const int MaxPackFileString = 256; // Definitely works - } -} diff --git a/lang/csharp/src/vpkpp/Entry.cs b/lang/csharp/src/vpkpp/Entry.cs index 3fd2d6672..624c55ba3 100644 --- a/lang/csharp/src/vpkpp/Entry.cs +++ b/lang/csharp/src/vpkpp/Entry.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Runtime.InteropServices; namespace vpkpp @@ -8,45 +6,44 @@ namespace vpkpp internal static unsafe partial class Extern { [DllImport("vpkppc")] - public static extern ulong vpkpp_entry_get_path(void* handle, sbyte* buffer, ulong bufferLen); + public static extern uint vpkpp_entry_get_flags(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_entry_get_parent_path(void* handle, sbyte* buffer, ulong bufferLen); + public static extern uint vpkpp_entry_get_archive_index(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_entry_get_filename(void* handle, sbyte* buffer, ulong bufferLen); + public static extern ulong vpkpp_entry_get_length(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_entry_get_stem(void* handle, sbyte* buffer, ulong bufferLen); + public static extern ulong vpkpp_entry_get_compressed_length(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_entry_get_extension(void* handle, sbyte* buffer, ulong bufferLen); + public static extern ulong vpkpp_entry_get_offset(void* handle); [DllImport("vpkppc")] - public static extern void vpkpp_entry_free(void** handle); + public static extern Buffer vpkpp_entry_get_extra_data(void* handle); [DllImport("vpkppc")] - public static extern void vpkpp_entry_array_free(EntryHandleArray* array); - } + public static extern uint vpkpp_entry_get_crc32(void* handle); - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct EntryHandleArray - { - internal long size; - internal void** data; + [DllImport("vpkppc")] + public static extern int vpkpp_entry_is_unbaked(void* handle); + + [DllImport("vpkppc")] + public static extern void vpkpp_entry_free(void** handle); } public class Entry { - internal unsafe Entry(void* handle, bool inArray) + internal unsafe Entry(void* handle, bool managed) { Handle = handle; - _inArray = inArray; + _managed = managed; } ~Entry() { - if (!_inArray) + if (!_managed) { unsafe { @@ -58,135 +55,97 @@ internal unsafe Entry(void* handle, bool inArray) } } - public string Path + public uint Flags { get { - Span stringArray = new sbyte[Constants.MaxPath]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_entry_get_path(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + return Extern.vpkpp_entry_get_flags(Handle); } } } - public string ParentPath + public uint ArchiveIndex { get { - Span stringArray = new sbyte[Constants.MaxPath]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_entry_get_parent_path(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + return Extern.vpkpp_entry_get_archive_index(Handle); } } } - public string FileName + public ulong Length { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_entry_get_filename(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + return Extern.vpkpp_entry_get_length(Handle); } } } - public string Stem + public ulong CompressedLength { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_entry_get_stem(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + return Extern.vpkpp_entry_get_compressed_length(Handle); } } } - public string Extension + public ulong Offset { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_entry_get_extension(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + return Extern.vpkpp_entry_get_offset(Handle); } } } - internal unsafe void* Handle; - - private readonly bool _inArray; - } - - public class EntryEnumerable : IEnumerable - { - internal EntryEnumerable(EntryHandleArray array) + public byte[] ExtraData { - _array = array; - } - - ~EntryEnumerable() - { - unsafe + get { - fixed (EntryHandleArray* arrayPtr = &_array) + unsafe { - Extern.vpkpp_entry_array_free(arrayPtr); + var buffer = Extern.vpkpp_entry_get_extra_data(Handle); + return BufferUtils.ConvertToArrayAndDelete(ref buffer); } } } - private Entry GetEntryAtPosition(ulong pos) + public uint CRC32 { - unsafe + get { - return new Entry(_array.data[pos], true); + unsafe + { + return Extern.vpkpp_entry_get_crc32(Handle); + } } } - private IEnumerator GetEnumerator() + public bool Unbaked { - for (long i = 0; i < _array.size; i++) + get { - yield return GetEntryAtPosition((ulong) i); + unsafe + { + return Convert.ToBoolean(Extern.vpkpp_entry_is_unbaked(Handle)); + } } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + internal unsafe void* Handle; - private EntryHandleArray _array; + private readonly bool _managed; } } diff --git a/lang/csharp/src/vpkpp/Format/BSP.cs b/lang/csharp/src/vpkpp/Format/BSP.cs index 1557affa1..cd7574a4f 100644 --- a/lang/csharp/src/vpkpp/Format/BSP.cs +++ b/lang/csharp/src/vpkpp/Format/BSP.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_bsp_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_bsp_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_bsp_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_bsp_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class BSP : PackFile @@ -24,6 +33,19 @@ private protected unsafe BSP(void* handle) : base(handle) {} } } + public new static BSP? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_bsp_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new BSP(handle); + } + } + public new static BSP? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe BSP(void* handle) : base(handle) {} return handle == null ? null : new BSP(handle); } } + + public new static BSP? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_bsp_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new BSP(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/FPX.cs b/lang/csharp/src/vpkpp/Format/FPX.cs index a9e351b92..c7d3771d2 100644 --- a/lang/csharp/src/vpkpp/Format/FPX.cs +++ b/lang/csharp/src/vpkpp/Format/FPX.cs @@ -1,14 +1,23 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_fpx_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_fpx_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_fpx_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_fpx_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class FPX : PackFile @@ -24,6 +33,19 @@ private protected unsafe FPX(void* handle) : base(handle) {} } } + public new static FPX? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_fpx_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new FPX(handle); + } + } + public new static FPX? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe FPX(void* handle) : base(handle) {} return handle == null ? null : new FPX(handle); } } + + public new static FPX? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_fpx_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new FPX(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/GCF.cs b/lang/csharp/src/vpkpp/Format/GCF.cs index 68a253380..30ebeb6c1 100644 --- a/lang/csharp/src/vpkpp/Format/GCF.cs +++ b/lang/csharp/src/vpkpp/Format/GCF.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_gcf_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_gcf_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_gcf_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_gcf_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class GCF : PackFile @@ -24,6 +33,19 @@ private protected unsafe GCF(void* handle) : base(handle) {} } } + public new static GCF? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_gcf_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new GCF(handle); + } + } + public new static GCF? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe GCF(void* handle) : base(handle) {} return handle == null ? null : new GCF(handle); } } + + public new static GCF? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_gcf_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new GCF(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/GMA.cs b/lang/csharp/src/vpkpp/Format/GMA.cs index a450141a8..d4a6e401f 100644 --- a/lang/csharp/src/vpkpp/Format/GMA.cs +++ b/lang/csharp/src/vpkpp/Format/GMA.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_gma_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_gma_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_gma_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_gma_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class GMA : PackFile @@ -24,6 +33,19 @@ private protected unsafe GMA(void* handle) : base(handle) {} } } + public new static GMA? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_gma_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new GMA(handle); + } + } + public new static GMA? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe GMA(void* handle) : base(handle) {} return handle == null ? null : new GMA(handle); } } + + public new static GMA? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_gma_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new GMA(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/GRP.cs b/lang/csharp/src/vpkpp/Format/GRP.cs deleted file mode 100644 index 3cda91a7c..000000000 --- a/lang/csharp/src/vpkpp/Format/GRP.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Runtime.InteropServices; - -namespace vpkpp.Format -{ - internal static unsafe partial class Extern - { - [DllImport("vpkppc")] - public static extern void* vpkpp_grp_open([MarshalAs(UnmanagedType.LPStr)] string path); - - [DllImport("vpkppc")] - public static extern void* vpkpp_grp_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); - } - - public class GRP : PackFile - { - private protected unsafe GRP(void* handle) : base(handle) {} - - public new static GRP? Open(string path) - { - unsafe - { - var handle = Extern.vpkpp_grp_open(path); - return handle == null ? null : new GRP(handle); - } - } - - public new static GRP? Open(string path, PackFileOptions options) - { - unsafe - { - var handle = Extern.vpkpp_grp_open_with_options(path, options); - return handle == null ? null : new GRP(handle); - } - } - } -} diff --git a/lang/csharp/src/vpkpp/Format/PAK.cs b/lang/csharp/src/vpkpp/Format/PAK.cs index 676650aa8..21d86a7c2 100644 --- a/lang/csharp/src/vpkpp/Format/PAK.cs +++ b/lang/csharp/src/vpkpp/Format/PAK.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_pak_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_pak_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_pak_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_pak_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class PAK : PackFile @@ -24,6 +33,19 @@ private protected unsafe PAK(void* handle) : base(handle) {} } } + public new static PAK? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_pak_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PAK(handle); + } + } + public new static PAK? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe PAK(void* handle) : base(handle) {} return handle == null ? null : new PAK(handle); } } + + public new static PAK? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_pak_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PAK(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/PCK.cs b/lang/csharp/src/vpkpp/Format/PCK.cs index 8caa7d5fa..61b1c328f 100644 --- a/lang/csharp/src/vpkpp/Format/PCK.cs +++ b/lang/csharp/src/vpkpp/Format/PCK.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_pck_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_pck_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_pck_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_pck_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class PCK : PackFile @@ -24,6 +33,19 @@ private protected unsafe PCK(void* handle) : base(handle) {} } } + public new static PCK? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_pck_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PCK(handle); + } + } + public new static PCK? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe PCK(void* handle) : base(handle) {} return handle == null ? null : new PCK(handle); } } + + public new static PCK? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_pck_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PCK(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/VPK.cs b/lang/csharp/src/vpkpp/Format/VPK.cs index 788a07fcc..e9f522e0f 100644 --- a/lang/csharp/src/vpkpp/Format/VPK.cs +++ b/lang/csharp/src/vpkpp/Format/VPK.cs @@ -5,6 +5,8 @@ namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] @@ -22,9 +24,15 @@ internal static unsafe partial class Extern [DllImport("vpkppc")] public static extern void* vpkpp_vpk_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_vpk_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_vpk_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + [DllImport("vpkppc")] + public static extern void* vpkpp_vpk_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); + [DllImport("vpkppc")] public static extern byte vpkpp_vpk_generate_keypair_files([MarshalAs(UnmanagedType.LPStr)] string path); @@ -90,6 +98,19 @@ private unsafe VPK(void* handle) : base(handle) {} } } + public new static VPK? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_vpk_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new VPK(handle); + } + } + public new static VPK? Open(string path, PackFileOptions options) { unsafe @@ -99,6 +120,19 @@ private unsafe VPK(void* handle) : base(handle) {} } } + public new static VPK? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_vpk_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new VPK(handle); + } + } + public static bool GenerateKeyPairFiles(string path) { unsafe diff --git a/lang/csharp/src/vpkpp/Format/VPK_VTMB.cs b/lang/csharp/src/vpkpp/Format/VPK_VTMB.cs index 45be95d14..c0df55b70 100644 --- a/lang/csharp/src/vpkpp/Format/VPK_VTMB.cs +++ b/lang/csharp/src/vpkpp/Format/VPK_VTMB.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_vpk_vtmb_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_vpk_vtmb_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_vpk_vtmb_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_vpk_vtmb_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class VPK_VTMB : PackFile @@ -24,6 +33,19 @@ private protected unsafe VPK_VTMB(void* handle) : base(handle) {} } } + public new static VPK_VTMB? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_vpk_vtmb_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new VPK_VTMB(handle); + } + } + public new static VPK_VTMB? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe VPK_VTMB(void* handle) : base(handle) {} return handle == null ? null : new VPK_VTMB(handle); } } + + public new static VPK_VTMB? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_vpk_vtmb_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new VPK_VTMB(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/Format/ZIP.cs b/lang/csharp/src/vpkpp/Format/ZIP.cs index 0a9d4ead1..d1d8192a4 100644 --- a/lang/csharp/src/vpkpp/Format/ZIP.cs +++ b/lang/csharp/src/vpkpp/Format/ZIP.cs @@ -1,14 +1,23 @@ +using System; using System.Runtime.InteropServices; namespace vpkpp.Format { + using EntryCallback = Action; + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_zip_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_zip_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_zip_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + + [DllImport("vpkppc")] + public static extern void* vpkpp_zip_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); } public class ZIP : PackFile @@ -24,6 +33,19 @@ private protected unsafe ZIP(void* handle) : base(handle) {} } } + public new static ZIP? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_zip_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new ZIP(handle); + } + } + public new static ZIP? Open(string path, PackFileOptions options) { unsafe @@ -32,5 +54,18 @@ private protected unsafe ZIP(void* handle) : base(handle) {} return handle == null ? null : new ZIP(handle); } } + + public new static ZIP? Open(string path, PackFileOptions options, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_zip_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new ZIP(handle); + } + } } } diff --git a/lang/csharp/src/vpkpp/PackFile.cs b/lang/csharp/src/vpkpp/PackFile.cs index e2831967f..ad43e76c0 100644 --- a/lang/csharp/src/vpkpp/PackFile.cs +++ b/lang/csharp/src/vpkpp/PackFile.cs @@ -1,18 +1,33 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace vpkpp { + using EntryCallback = Action; + + using EntryPredicate = Func; + + internal unsafe delegate void EntryCallbackNative(string path, void* entry); + + internal unsafe delegate int EntryPredicateNative(string path, void* entry); + internal static unsafe partial class Extern { [DllImport("vpkppc")] public static extern void* vpkpp_open([MarshalAs(UnmanagedType.LPStr)] string path); + [DllImport("vpkppc")] + public static extern void* vpkpp_open_with_callback([MarshalAs(UnmanagedType.LPStr)] string path, IntPtr callback); + [DllImport("vpkppc")] public static extern void* vpkpp_open_with_options([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options); + [DllImport("vpkppc")] + public static extern void* vpkpp_open_with_options_and_callback([MarshalAs(UnmanagedType.LPStr)] string path, PackFileOptions options, IntPtr callback); + [DllImport("vpkppc")] public static extern PackFileType vpkpp_get_type(void* handle); @@ -20,103 +35,106 @@ internal static unsafe partial class Extern public static extern PackFileOptions vpkpp_get_options(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_has_entry_checksums(void* handle); + public static extern int vpkpp_has_entry_checksums(void* handle); [DllImport("vpkppc")] public static extern StringArray vpkpp_verify_entry_checksums(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_has_pack_file_checksum(void* handle); + public static extern int vpkpp_has_pack_file_checksum(void* handle); + + [DllImport("vpkppc")] + public static extern int vpkpp_verify_pack_file_checksum(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_verify_pack_file_checksum(void* handle); + public static extern int vpkpp_has_pack_file_signature(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_has_pack_file_signature(void* handle); + public static extern int vpkpp_verify_pack_file_signature(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_verify_pack_file_signature(void* handle); + public static extern int vpkpp_is_case_sensitive(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_is_case_sensitive(void* handle); + public static extern int vpkpp_has_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path, int includeUnbaked); [DllImport("vpkppc")] - public static extern byte vpkpp_has_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string filename, byte includeUnbaked); + public static extern void* vpkpp_find_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path, int includeUnbaked); [DllImport("vpkppc")] - public static extern void* vpkpp_find_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string filename, byte includeUnbaked); + public static extern Buffer vpkpp_read_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path); [DllImport("vpkppc")] - public static extern Buffer vpkpp_read_entry(void* handle, void* entry); + public static extern String vpkpp_read_entry_text(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path); [DllImport("vpkppc")] - public static extern String vpkpp_read_entry_text(void* handle, void* entry); + public static extern int vpkpp_is_read_only(void* handle); [DllImport("vpkppc")] - public static extern byte vpkpp_is_read_only(void* handle); + public static extern void vpkpp_add_entry_from_file(void* handle, [MarshalAs(UnmanagedType.LPStr)] string entryPath, [MarshalAs(UnmanagedType.LPStr)] string filepath); [DllImport("vpkppc")] - public static extern void vpkpp_add_entry_from_file(void* handle, [MarshalAs(UnmanagedType.LPStr)] string filename, [MarshalAs(UnmanagedType.LPStr)] string pathToFile); + public static extern void vpkpp_add_entry_from_mem(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path, byte* buffer, ulong bufferLen); [DllImport("vpkppc")] - public static extern void vpkpp_add_entry_from_mem(void* handle, [MarshalAs(UnmanagedType.LPStr)] string filename, byte* buffer, ulong bufferLen); + public static extern int vpkpp_remove_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path); [DllImport("vpkppc")] - public static extern byte vpkpp_remove_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string filename); + public static extern ulong vpkpp_remove_directory(void* handle, [MarshalAs(UnmanagedType.LPStr)] string dirName); [DllImport("vpkppc")] - public static extern byte vpkpp_bake(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir); + public static extern int vpkpp_bake(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir); [DllImport("vpkppc")] - public static extern byte vpkpp_extract_entry(void* handle, void* entry, [MarshalAs(UnmanagedType.LPStr)] string filePath); + public static extern int vpkpp_bake_with_callback(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, IntPtr callback); [DllImport("vpkppc")] - public static extern byte vpkpp_extract_directory(void* handle, [MarshalAs(UnmanagedType.LPStr)] string dir, [MarshalAs(UnmanagedType.LPStr)] string outputDir); + public static extern int vpkpp_extract_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string entryPath, [MarshalAs(UnmanagedType.LPStr)] string filepath); [DllImport("vpkppc")] - public static extern byte vpkpp_extract_all(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, byte createUnderPackFileDir); + public static extern int vpkpp_extract_directory(void* handle, [MarshalAs(UnmanagedType.LPStr)] string dir, [MarshalAs(UnmanagedType.LPStr)] string outputDir); [DllImport("vpkppc")] - public static extern byte vpkpp_extract_all_if(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, IntPtr predicate); + public static extern int vpkpp_extract_all(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, int createUnderPackFileDir); [DllImport("vpkppc")] - public static extern EntryHandleArray vpkpp_get_baked_entries(void* handle); + public static extern int vpkpp_extract_all_if(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, IntPtr predicate); [DllImport("vpkppc")] - public static extern EntryHandleArray vpkpp_get_unbaked_entries(void* handle); + public static extern ulong vpkpp_get_entry_count(void* handle, int includeUnbaked); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_entry_count(void* handle, byte includeUnbaked); + public static extern void vpkpp_run_for_all_entries(void* handle, IntPtr operation, int includeUnbaked); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_filepath(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_filepath(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_truncated_filepath(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_truncated_filepath(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_filename(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_filename(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_truncated_filename(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_truncated_filename(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_filestem(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_filestem(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_truncated_filestem(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_get_truncated_filestem(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_get_supported_entry_attributes(void* handle, Attribute* buffer, ulong bufferLen); + public static extern Attribute vpkpp_get_supported_entry_attributes(void* handle); [DllImport("vpkppc")] - public static extern ulong vpkpp_to_string(void* handle, sbyte* buffer, ulong bufferLen); + public static extern String vpkpp_to_string(void* handle); [DllImport("vpkppc")] public static extern void vpkpp_close(void** handle); [DllImport("vpkppc")] - public static extern String vpkpp_escape_entry_path([MarshalAs(UnmanagedType.LPStr)] string path); + public static extern String vpkpp_escape_entry_path_for_write([MarshalAs(UnmanagedType.LPStr)] string path); [DllImport("vpkppc")] public static extern StringArray vpkpp_get_supported_file_types(); @@ -149,6 +167,19 @@ private protected unsafe PackFile(void* handle) } } + public static PackFile? Open(string path, EntryCallback callback) + { + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_open_with_callback(path, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PackFile(handle); + } + } + public static PackFile? Open(string path, PackFileOptions options) { unsafe @@ -158,9 +189,22 @@ private protected unsafe PackFile(void* handle) } } - public static string EscapeEntryPath(string path) + public static PackFile? Open(string path, PackFileOptions options, EntryCallback callback) { - var str = Extern.vpkpp_escape_entry_path(path); + unsafe + { + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + var handle = Extern.vpkpp_open_with_options_and_callback(path, options, Marshal.GetFunctionPointerForDelegate(callbackNative)); + return handle == null ? null : new PackFile(handle); + } + } + + public static string EscapeEntryPathForWrite(string path) + { + var str = Extern.vpkpp_escape_entry_path_for_write(path); return StringUtils.ConvertToStringAndDelete(ref str); } @@ -219,77 +263,85 @@ public bool VerifyPackFileSignature() } } - public bool HasEntry(string filename, bool includeUnbaked = true) + public bool HasEntry(string path, bool includeUnbaked = true) { unsafe { - return Convert.ToBoolean(Extern.vpkpp_has_entry(Handle, filename, Convert.ToByte(includeUnbaked))); + return Convert.ToBoolean(Extern.vpkpp_has_entry(Handle, path, Convert.ToInt32(includeUnbaked))); } } - public Entry? FindEntry(string filename, bool includeUnbaked = true) + public Entry? FindEntry(string path, bool includeUnbaked = true) { unsafe { - var entry = Extern.vpkpp_find_entry(Handle, filename, Convert.ToByte(includeUnbaked)); + var entry = Extern.vpkpp_find_entry(Handle, path, Convert.ToInt32(includeUnbaked)); return entry == null ? null : new Entry(entry, false); } } - public byte[]? ReadEntry(Entry entry) + public byte[]? ReadEntry(string path) { unsafe { - var buffer = Extern.vpkpp_read_entry(Handle, entry.Handle); + var buffer = Extern.vpkpp_read_entry(Handle, path); return buffer.size < 0 ? null : BufferUtils.ConvertToArrayAndDelete(ref buffer); } } - public string? ReadEntryText(Entry entry) + public string? ReadEntryText(string path) { unsafe { - var str = Extern.vpkpp_read_entry_text(Handle, entry.Handle); + var str = Extern.vpkpp_read_entry_text(Handle, path); return str.size < 0 ? null : StringUtils.ConvertToStringAndDelete(ref str); } } - public void AddEntry(string filename, string pathToFile) + public void AddEntry(string entryPath, string filepath) { unsafe { - Extern.vpkpp_add_entry_from_file(Handle, filename, pathToFile); + Extern.vpkpp_add_entry_from_file(Handle, entryPath, filepath); } } - public void AddEntry(string filename, byte[] buffer) + public void AddEntry(string path, byte[] buffer) { unsafe { fixed (byte* bufferPtr = buffer) { - Extern.vpkpp_add_entry_from_mem(Handle, filename, bufferPtr, (ulong) buffer.LongLength); + Extern.vpkpp_add_entry_from_mem(Handle, path, bufferPtr, (ulong) buffer.LongLength); } } } - public void AddEntry(string filename, IEnumerable buffer) + public void AddEntry(string path, IEnumerable buffer) { unsafe { var data = buffer.ToArray(); fixed (byte* bufferPtr = data) { - Extern.vpkpp_add_entry_from_mem(Handle, filename, bufferPtr, (ulong) data.LongLength); + Extern.vpkpp_add_entry_from_mem(Handle, path, bufferPtr, (ulong) data.LongLength); } } } - public bool RemoveEntry(string filename) + public bool RemoveEntry(string path) { unsafe { - return Convert.ToBoolean(Extern.vpkpp_remove_entry(Handle, filename)); + return Convert.ToBoolean(Extern.vpkpp_remove_entry(Handle, path)); + } + } + + public ulong RemoveDirectory(string dirName) + { + unsafe + { + return Extern.vpkpp_remove_directory(Handle, dirName); } } @@ -301,66 +353,71 @@ public bool Bake(string outputDir) } } - public bool ExtractEntry(Entry entry, string filePath) + public bool Bake(string outputDir, EntryCallback callback) { unsafe { - return Convert.ToBoolean(Extern.vpkpp_extract_entry(Handle, entry.Handle, filePath)); + EntryCallbackNative callbackNative = (path, entry) => + { + callback(path, new Entry(entry, true)); + }; + return Convert.ToBoolean(Extern.vpkpp_bake_with_callback(Handle, outputDir, Marshal.GetFunctionPointerForDelegate(callbackNative))); } } - public bool ExtractDirectory(string dir, string outputDir) + public bool ExtractEntry(string entryPath, string filepath) { unsafe { - return Convert.ToBoolean(Extern.vpkpp_extract_directory(Handle, dir, outputDir)); + return Convert.ToBoolean(Extern.vpkpp_extract_entry(Handle, entryPath, filepath)); } } - public bool ExtractAll(string outputDir, bool createUnderPackFileDir = true) + public bool ExtractDirectory(string dir, string outputDir) { unsafe { - return Convert.ToBoolean(Extern.vpkpp_extract_all(Handle, outputDir, Convert.ToByte(createUnderPackFileDir))); + return Convert.ToBoolean(Extern.vpkpp_extract_directory(Handle, dir, outputDir)); } } - private unsafe delegate byte ExtractAllPredicateNative(void* entry); - - public bool ExtractAll(string outputDir, Func predicate) + public bool ExtractAll(string outputDir, bool createUnderPackFileDir = true) { unsafe { - ExtractAllPredicateNative predicateWrapper = (entry) => - { - // HACK: tell the entry its in an array so it doesn't kill itself - return Convert.ToByte(predicate(new Entry(entry, true))); - }; - return Convert.ToBoolean(Extern.vpkpp_extract_all_if(Handle, outputDir, Marshal.GetFunctionPointerForDelegate(predicateWrapper))); + return Convert.ToBoolean(Extern.vpkpp_extract_all(Handle, outputDir, Convert.ToInt32(createUnderPackFileDir))); } } - public EntryEnumerable GetBakedEntries() + public bool ExtractAll(string outputDir, EntryPredicate predicate) { unsafe { - return new EntryEnumerable(Extern.vpkpp_get_baked_entries(Handle)); + EntryPredicateNative predicateNative = (path, entry) => + { + return Convert.ToInt32(predicate(path, new Entry(entry, true))); + }; + return Convert.ToBoolean(Extern.vpkpp_extract_all_if(Handle, outputDir, Marshal.GetFunctionPointerForDelegate(predicateNative))); } } - public EntryEnumerable GetUnbakedEntries() + public ulong GetEntryCount(bool includeUnbaked = true) { unsafe { - return new EntryEnumerable(Extern.vpkpp_get_unbaked_entries(Handle)); + return Extern.vpkpp_get_entry_count(Handle, Convert.ToInt32(includeUnbaked)); } } - public ulong GetEntryCount(bool includeUnbaked = true) + public void RunForAllEntries(EntryCallback operation, bool includeUnbaked = true) { unsafe { - return Extern.vpkpp_get_entry_count(Handle, Convert.ToByte(includeUnbaked)); + EntryCallbackNative callbackNative = (path, entry) => + { + operation(path, new Entry(entry, true)); + }; + Extern.vpkpp_run_for_all_entries(Handle, Marshal.GetFunctionPointerForDelegate(callbackNative), Convert.ToInt32(includeUnbaked)); } } @@ -368,9 +425,8 @@ public override string ToString() { unsafe { - var stringPtr = stackalloc sbyte[Constants.MaxPackFileString]; - Extern.vpkpp_to_string(Handle, stringPtr, Convert.ToUInt64(Constants.MaxPackFileString)); - return new string(stringPtr); + var str = Extern.vpkpp_to_string(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } @@ -422,14 +478,10 @@ public string FilePath { get { - Span stringArray = new sbyte[Constants.MaxPath]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_filepath(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_filepath(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } @@ -438,14 +490,10 @@ public string TruncatedFilePath { get { - Span stringArray = new sbyte[Constants.MaxPath]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_truncated_filepath(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_truncated_filepath(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } @@ -454,14 +502,10 @@ public string FileName { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_filename(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_filename(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } @@ -470,14 +514,10 @@ public string TruncatedFileName { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_truncated_filename(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_truncated_filename(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } @@ -486,14 +526,10 @@ public string FileStem { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_filestem(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_filestem(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } @@ -502,33 +538,21 @@ public string TruncatedFileStem { get { - Span stringArray = new sbyte[Constants.MaxFilename]; unsafe { - fixed (sbyte* stringPtr = stringArray) - { - Extern.vpkpp_get_truncated_filestem(Handle, stringPtr, Convert.ToUInt64(stringArray.Length)); - return new string(stringPtr); - } + var str = Extern.vpkpp_get_truncated_filestem(Handle); + return StringUtils.ConvertToStringAndDelete(ref str); } } } - public List SupportedEntryAttributes + public Attribute SupportedEntryAttributes { get { unsafe { - var attrArray = stackalloc Attribute[(int) Attribute.COUNT]; - var numAttributes = Extern.vpkpp_get_supported_entry_attributes(Handle, attrArray, (ulong) Attribute.COUNT); - - var result = new List(); - for (ulong i = 0; i < numAttributes; i++) - { - result.Add(attrArray[i]); - } - return result; + return Extern.vpkpp_get_supported_entry_attributes(Handle); } } } diff --git a/lang/csharp/src/vpkpp/PackFileType.cs b/lang/csharp/src/vpkpp/PackFileType.cs index 8225b07f8..55cb3b212 100644 --- a/lang/csharp/src/vpkpp/PackFileType.cs +++ b/lang/csharp/src/vpkpp/PackFileType.cs @@ -7,7 +7,6 @@ public enum PackFileType FPX, GCF, GMA, - GRP, PAK, PCK, VPK, diff --git a/lang/csharp/test/vpkpp.test/PackFileTest.cs b/lang/csharp/test/vpkpp.test/PackFileTest.cs index 271f21226..624a08200 100644 --- a/lang/csharp/test/vpkpp.test/PackFileTest.cs +++ b/lang/csharp/test/vpkpp.test/PackFileTest.cs @@ -8,7 +8,10 @@ public class PackFileTest [TestMethod] public void Open() { - var vpk = PackFile.Open(BasePortalPath + "portal_pak_dir.vpk"); + var vpk = PackFile.Open(BasePortalPath + "portal_pak_dir.vpk", (path, entry) => + { + Console.WriteLine(path); + }); Assert.IsNotNull(vpk); Assert.AreEqual(vpk.Type, PackFileType.VPK); @@ -16,15 +19,10 @@ public void Open() var entry = vpk.FindEntry("materials/console/background1.vmt"); Assert.IsNotNull(entry); - Assert.AreEqual(vpk.ReadEntryText(entry), "\"UnlitGeneric\"\r\n{\r\n\t\"$basetexture\" \"console/background1\"\r\n\t\"$vertexcolor\" 1\r\n\t\"$vertexalpha\" 1\r\n\t\"$ignorez\" 1\r\n\t\"$no_fullbright\" \"1\"\r\n\t\"$nolod\" \"1\"\r\n}"); + Assert.AreEqual(vpk.ReadEntryText("materials/console/background1.vmt"), "\"UnlitGeneric\"\r\n{\r\n\t\"$basetexture\" \"console/background1\"\r\n\t\"$vertexcolor\" 1\r\n\t\"$vertexalpha\" 1\r\n\t\"$ignorez\" 1\r\n\t\"$no_fullbright\" \"1\"\r\n\t\"$nolod\" \"1\"\r\n}"); Assert.IsFalse(vpk.ReadOnly); - foreach(var baked in vpk.GetBakedEntries()) - { - Console.WriteLine(baked.Path); - } - Assert.AreEqual(vpk.GetEntryCount(), 3509u); Assert.AreEqual(vpk.FilePath, BasePortalPath + "portal_pak_dir.vpk"); @@ -33,13 +31,8 @@ public void Open() Assert.AreEqual(vpk.TruncatedFileName, "portal_pak.vpk"); Assert.AreEqual(vpk.FileStem, "portal_pak_dir"); Assert.AreEqual(vpk.TruncatedFileStem, "portal_pak"); - - CollectionAssert.AreEqual(vpk.SupportedEntryAttributes, new List - { - Attribute.LENGTH, Attribute.VPK_PRELOADED_DATA_LENGTH, Attribute.VPK_ARCHIVE_INDEX, Attribute.CRC32 - }); - - Assert.AreEqual(vpk.ToString(), "portal_pak_dir.vpk | Version v2"); + Assert.AreEqual(vpk.SupportedEntryAttributes, Attribute.LENGTH | Attribute.VPK_PRELOADED_DATA_LENGTH | Attribute.ARCHIVE_INDEX | Attribute.CRC32); + Assert.AreEqual(vpk.ToString(), "portal_pak.vpk | Version v2"); } [TestMethod] @@ -54,8 +47,7 @@ public void VerifyChecksums() [TestMethod] public void GetSupportedFileTypes() { - // 15 as of v4.1.2 - Assert.AreEqual(PackFile.GetSupportedFileTypes().Count, 15); + Assert.AreEqual(PackFile.GetSupportedFileTypes().Count, 12); } [TestMethod] From aece195f3cc3e04df9fca661ef43435d7a53df45 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 01:31:01 -0400 Subject: [PATCH 04/10] fix(vpkpp): reorder attribute definitions --- include/vpkpp/Attribute.h | 12 ++++++------ lang/c/include/vpkppc/Attribute.h | 12 ++++++------ lang/csharp/src/vpkpp/Attribute.cs | 12 ++++++------ src/vpkpp/format/VPK.cpp | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/vpkpp/Attribute.h b/include/vpkpp/Attribute.h index 29c16f73a..09579bb6e 100644 --- a/include/vpkpp/Attribute.h +++ b/include/vpkpp/Attribute.h @@ -5,12 +5,12 @@ namespace vpkpp { enum class Attribute { - NONE = 0, - LENGTH = 1 << 0, - VPK_PRELOADED_DATA_LENGTH = 1 << 1, - ARCHIVE_INDEX = 1 << 2, - CRC32 = 1 << 3, - PCK_MD5 = 1 << 4, + NONE = 0, + ARCHIVE_INDEX = 1 << 0, + LENGTH = 1 << 1, + CRC32 = 1 << 2, + PCK_MD5 = 1 << 3, + VPK_PRELOADED_DATA = 1 << 4, }; SOURCEPP_SETUP_BITWISE_ENUM_CLASS(Attribute) diff --git a/lang/c/include/vpkppc/Attribute.h b/lang/c/include/vpkppc/Attribute.h index 2b7aaac1d..2d3a7aac2 100644 --- a/lang/c/include/vpkppc/Attribute.h +++ b/lang/c/include/vpkppc/Attribute.h @@ -5,12 +5,12 @@ extern "C" { #endif typedef enum { - VPKPP_ATTRIBUTE_NONE = 0, - VPKPP_ATTRIBUTE_LENGTH = 1 << 0, - VPKPP_ATTRIBUTE_VPK_PRELOADED_DATA_LENGTH = 1 << 1, - VPKPP_ATTRIBUTE_ARCHIVE_INDEX = 1 << 2, - VPKPP_ATTRIBUTE_CRC32 = 1 << 3, - VPKPP_ATTRIBUTE_PCK_MD5 = 1 << 4, + VPKPP_ATTRIBUTE_NONE = 0, + VPKPP_ATTRIBUTE_ARCHIVE_INDEX = 1 << 0, + VPKPP_ATTRIBUTE_LENGTH = 1 << 1, + VPKPP_ATTRIBUTE_CRC32 = 1 << 2, + VPKPP_ATTRIBUTE_PCK_MD5 = 1 << 3, + VPKPP_ATTRIBUTE_VPK_PRELOADED_DATA = 1 << 4, } vpkpp_attribute_e; #ifdef __cplusplus diff --git a/lang/csharp/src/vpkpp/Attribute.cs b/lang/csharp/src/vpkpp/Attribute.cs index 47c919ca0..a4a0e12a9 100644 --- a/lang/csharp/src/vpkpp/Attribute.cs +++ b/lang/csharp/src/vpkpp/Attribute.cs @@ -2,11 +2,11 @@ namespace vpkpp { public enum Attribute { - NONE = 0, - LENGTH = 1 << 0, - VPK_PRELOADED_DATA_LENGTH = 1 << 1, - ARCHIVE_INDEX = 1 << 2, - CRC32 = 1 << 3, - PCK_MD5 = 1 << 4, + NONE = 0, + ARCHIVE_INDEX = 1 << 0, + LENGTH = 1 << 1, + CRC32 = 1 << 2, + PCK_MD5 = 1 << 3, + VPK_PRELOADED_DATA = 1 << 4, } } diff --git a/src/vpkpp/format/VPK.cpp b/src/vpkpp/format/VPK.cpp index eb3f59974..5281c127a 100644 --- a/src/vpkpp/format/VPK.cpp +++ b/src/vpkpp/format/VPK.cpp @@ -679,7 +679,7 @@ std::string VPK::getTruncatedFilestem() const { Attribute VPK::getSupportedEntryAttributes() const { using enum Attribute; - return LENGTH | VPK_PRELOADED_DATA_LENGTH | ARCHIVE_INDEX | CRC32; + return LENGTH | VPK_PRELOADED_DATA | ARCHIVE_INDEX | CRC32; } VPK::operator std::string() const { From b9d449f30d81b67912c147a7842555fac6fb22d8 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 01:37:24 -0400 Subject: [PATCH 05/10] chore: bump miniz --- ext/miniz | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/miniz b/ext/miniz index bf7a1f0a5..1ff82be7d 160000 --- a/ext/miniz +++ b/ext/miniz @@ -1 +1 @@ -Subproject commit bf7a1f0a5aa1deae9cab2d73b5ef9edec41b877c +Subproject commit 1ff82be7d67f5c2f8b5497f538eea247861e0717 From 560da0c93bf7efc93416315e03ac48c213d0e107 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 02:08:15 -0400 Subject: [PATCH 06/10] feat: add sourcepp:: target alias to all libraries in cmake --- CMakeLists.txt | 20 ++++++++++---------- cmake/AddPrettyParser.cmake | 5 ++++- lang/c/src/vpkppc/_vpkppc.cmake | 1 - src/steampp/_steampp.cmake | 2 +- src/vpkpp/_vpkpp.cmake | 5 ++++- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f81fd546..1506e0dcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,16 +87,16 @@ endif() # Add libraries -add_sourcepp_library(bsppp NO_TEST) -add_sourcepp_library(dmxpp) -add_sourcepp_library(fgdpp) -add_sourcepp_library(gamepp) -add_sourcepp_library(kvpp) -add_sourcepp_library(mdlpp) -add_sourcepp_library(steampp C) -add_sourcepp_library(vicepp C CSHARP) -add_sourcepp_library(vpkpp C CSHARP NO_TEST) -add_sourcepp_library(vtfpp) +add_sourcepp_library(bsppp NO_TEST) # sourcepp::bsppp +add_sourcepp_library(dmxpp) # sourcepp::dmxpp +add_sourcepp_library(fgdpp) # sourcepp::fgdpp +add_sourcepp_library(gamepp) # sourcepp::gamepp +add_sourcepp_library(kvpp) # sourcepp::kvpp +add_sourcepp_library(mdlpp) # sourcepp::mdlpp +add_sourcepp_library(steampp C) # sourcepp::steampp +add_sourcepp_library(vicepp C CSHARP) # sourcepp::vicepp +add_sourcepp_library(vpkpp C CSHARP NO_TEST) # sourcepp::vpkpp +add_sourcepp_library(vtfpp) # sourcepp::vtfpp # Tests, part 2 diff --git a/cmake/AddPrettyParser.cmake b/cmake/AddPrettyParser.cmake index 2ff7ade9a..d8a081f6e 100644 --- a/cmake/AddPrettyParser.cmake +++ b/cmake/AddPrettyParser.cmake @@ -1,21 +1,24 @@ # Add a new parser library function(add_pretty_parser TARGET) - cmake_parse_arguments(PARSE_ARGV 1 OPTIONS "C" "" "DEPS;PRECOMPILED_HEADERS;SOURCES") + cmake_parse_arguments(PARSE_ARGV 1 OPTIONS "C" "" "DEPS;DEPS_INTERFACE;PRECOMPILED_HEADERS;SOURCES") if(OPTIONS_C) add_library(${TARGET}c SHARED ${${PROJECT_NAME}c_SOURCES} ${OPTIONS_PRECOMPILED_HEADERS} ${OPTIONS_SOURCES}) + add_library(sourcepp::${TARGET}c ALIAS ${TARGET}c) set_target_properties(${TARGET}c PROPERTIES PREFIX "") target_link_libraries(${TARGET}c PRIVATE ${TARGET}) target_include_directories(${TARGET}c PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include") set(TARGET "${TARGET}c") else() add_library(${TARGET} STATIC ${OPTIONS_PRECOMPILED_HEADERS} ${OPTIONS_SOURCES}) + add_library(sourcepp::${TARGET} ALIAS ${TARGET}) endif() if(NOT ("PRECOMPILED_HEADERS" IN_LIST OPTIONS_UNPARSED_ARGUMENTS)) target_precompile_headers(${TARGET} PUBLIC ${OPTIONS_HEADERS}) endif() target_link_libraries(${TARGET} PUBLIC ${PROJECT_NAME}) target_link_libraries(${TARGET} PRIVATE ${OPTIONS_DEPS}) + target_link_libraries(${TARGET} INTERFACE ${OPTIONS_DEPS_INTERFACE}) # Define DEBUG macro target_compile_definitions(${TARGET} PRIVATE "$<$:DEBUG>") diff --git a/lang/c/src/vpkppc/_vpkppc.cmake b/lang/c/src/vpkppc/_vpkppc.cmake index d06246d84..0f115eb58 100644 --- a/lang/c/src/vpkppc/_vpkppc.cmake +++ b/lang/c/src/vpkppc/_vpkppc.cmake @@ -1,5 +1,4 @@ add_pretty_parser(vpkpp C - DEPS tsl::hat_trie PRECOMPILED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/BSP.h" "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/FPX.h" diff --git a/src/steampp/_steampp.cmake b/src/steampp/_steampp.cmake index 7001d000c..6c4b89f82 100644 --- a/src/steampp/_steampp.cmake +++ b/src/steampp/_steampp.cmake @@ -1,5 +1,5 @@ add_pretty_parser(steampp - DEPS kvpp + DEPS sourcepp::kvpp SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/include/steampp/steampp.h" "${CMAKE_CURRENT_LIST_DIR}/steampp.cpp") diff --git a/src/vpkpp/_vpkpp.cmake b/src/vpkpp/_vpkpp.cmake index 32a025ab1..da18b4a42 100644 --- a/src/vpkpp/_vpkpp.cmake +++ b/src/vpkpp/_vpkpp.cmake @@ -1,5 +1,6 @@ add_pretty_parser(vpkpp - DEPS bsppp cryptopp::cryptopp kvpp MINIZIP::minizip tsl::hat_trie + DEPS cryptopp::cryptopp MINIZIP::minizip sourcepp::bsppp sourcepp::kvpp + DEPS_INTERFACE tsl::hat_trie PRECOMPILED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/BSP.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/vpkpp/format/FPX.h" @@ -27,3 +28,5 @@ add_pretty_parser(vpkpp "${CMAKE_CURRENT_LIST_DIR}/format/VPK_VTMB.cpp" "${CMAKE_CURRENT_LIST_DIR}/format/ZIP.cpp" "${CMAKE_CURRENT_LIST_DIR}/PackFile.cpp") + +target_include_directories(vpkpp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/ext/hat-trie/include") From a34056c4b60f29d44a8feb47d65b747c3c3bac29 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 02:53:57 -0400 Subject: [PATCH 07/10] fix(vpkpp): extractAll with predicate was searching for entries with a cut off path --- include/vpkpp/PackFile.h | 2 +- lang/c/include/vpkppc/PackFile.h | 2 +- lang/c/src/vpkppc/PackFile.cpp | 4 ++-- lang/csharp/src/vpkpp/PackFile.cs | 6 +++--- src/vpkpp/PackFile.cpp | 19 ++++++++++--------- .../format/example/ExamplePackFileImpl.h | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/vpkpp/PackFile.h b/include/vpkpp/PackFile.h index 4681b02df..9bd1afde6 100644 --- a/include/vpkpp/PackFile.h +++ b/include/vpkpp/PackFile.h @@ -123,7 +123,7 @@ class PackFile { bool extractAll(const std::string& outputDir, bool createUnderPackFileDir = true) const; // NOLINT(*-use-nodiscard) /// Extract the contents of the pack file to disk at the given directory - only entries which match the predicate are extracted - bool extractAll(const std::string& outputDir, const EntryPredicate& predicate) const; // NOLINT(*-use-nodiscard) + bool extractAll(const std::string& outputDir, const EntryPredicate& predicate, bool stripSharedDirs = true) const; // NOLINT(*-use-nodiscard) /// Get entries saved to disk [[nodiscard]] const EntryTrie& getBakedEntries() const; diff --git a/lang/c/include/vpkppc/PackFile.h b/lang/c/include/vpkppc/PackFile.h index cbf0f2dca..67b9c2dc5 100644 --- a/lang/c/include/vpkppc/PackFile.h +++ b/lang/c/include/vpkppc/PackFile.h @@ -88,7 +88,7 @@ SOURCEPP_API int vpkpp_extract_directory(vpkpp_pack_file_handle_t handle, const SOURCEPP_API int vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* outputDir, int createUnderPackFileDir); -SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate); +SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate, int stripSharedDirs); SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, int includeUnbaked); diff --git a/lang/c/src/vpkppc/PackFile.cpp b/lang/c/src/vpkppc/PackFile.cpp index 0b6999cdd..8b25c9c42 100644 --- a/lang/c/src/vpkppc/PackFile.cpp +++ b/lang/c/src/vpkppc/PackFile.cpp @@ -243,14 +243,14 @@ SOURCEPP_API int vpkpp_extract_all(vpkpp_pack_file_handle_t handle, const char* return Convert::packFile(handle)->extractAll(outputDir, createUnderPackFileDir); } -SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate) { +SOURCEPP_API int vpkpp_extract_all_if(vpkpp_pack_file_handle_t handle, const char* outputDir, EntryPredicate predicate, int stripSharedDirs) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(outputDir, false); SOURCEPP_EARLY_RETURN_VAL(predicate, false); return Convert::packFile(handle)->extractAll(outputDir, [predicate](const std::string& path, const Entry& entry) { return predicate(path.c_str(), const_cast(&entry)); - }); + }, stripSharedDirs); } SOURCEPP_API size_t vpkpp_get_entry_count(vpkpp_pack_file_handle_t handle, int includeUnbaked) { diff --git a/lang/csharp/src/vpkpp/PackFile.cs b/lang/csharp/src/vpkpp/PackFile.cs index ad43e76c0..a3300feec 100644 --- a/lang/csharp/src/vpkpp/PackFile.cs +++ b/lang/csharp/src/vpkpp/PackFile.cs @@ -98,7 +98,7 @@ internal static unsafe partial class Extern public static extern int vpkpp_extract_all(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, int createUnderPackFileDir); [DllImport("vpkppc")] - public static extern int vpkpp_extract_all_if(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, IntPtr predicate); + public static extern int vpkpp_extract_all_if(void* handle, [MarshalAs(UnmanagedType.LPStr)] string outputDir, IntPtr predicate, int stripSharedDirs); [DllImport("vpkppc")] public static extern ulong vpkpp_get_entry_count(void* handle, int includeUnbaked); @@ -389,7 +389,7 @@ public bool ExtractAll(string outputDir, bool createUnderPackFileDir = true) } } - public bool ExtractAll(string outputDir, EntryPredicate predicate) + public bool ExtractAll(string outputDir, EntryPredicate predicate, bool stripSharedDirs = true) { unsafe { @@ -397,7 +397,7 @@ public bool ExtractAll(string outputDir, EntryPredicate predicate) { return Convert.ToInt32(predicate(path, new Entry(entry, true))); }; - return Convert.ToBoolean(Extern.vpkpp_extract_all_if(Handle, outputDir, Marshal.GetFunctionPointerForDelegate(predicateNative))); + return Convert.ToBoolean(Extern.vpkpp_extract_all_if(Handle, outputDir, Marshal.GetFunctionPointerForDelegate(predicateNative), Convert.ToInt32(stripSharedDirs)); } } diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index 5d3e1f1ed..b1a71a0b6 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -343,7 +343,7 @@ bool PackFile::extractAll(const std::string& outputDir, bool createUnderPackFile return noneFailed; } -bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& predicate) const { +bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& predicate, bool stripSharedDirs) const { if (outputDir.empty() || !predicate) { return false; } @@ -359,9 +359,11 @@ bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& pr return false; } - // Strip shared directories until we have a root folder - std::vector rootDirList; - { + std::size_t rootDirLen = 0; + if (stripSharedDirs) { + // Strip shared directories until we have a root folder + std::vector rootDirList; + std::vector> pathSplits; pathSplits.reserve(saveEntryPaths.size()); for (const auto& path : saveEntryPaths) { @@ -388,18 +390,17 @@ bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& pr path.erase(path.begin()); } } + rootDirLen = ::joinPath(rootDirList).length() + 1; } - auto rootDirLen = ::joinPath(rootDirList).length() + 1; // Extract std::filesystem::path outputDirPath{outputDir}; bool noneFailed = true; - for (const auto& path : saveEntryPaths) { - std::string entryPath = path.substr(rootDirLen); + for (auto& path : saveEntryPaths) { #ifdef _WIN32 - ::fixFilePathForWindows(entryPath); + ::fixFilePathForWindows(path); #endif - if (!this->extractEntry(entryPath, (outputDirPath / entryPath).string())) { + if (!this->extractEntry(path, (outputDirPath / path.substr(rootDirLen)).string())) { noneFailed = false; } } diff --git a/src/vpkpp/format/example/ExamplePackFileImpl.h b/src/vpkpp/format/example/ExamplePackFileImpl.h index 61fa18ac8..2063e0776 100644 --- a/src/vpkpp/format/example/ExamplePackFileImpl.h +++ b/src/vpkpp/format/example/ExamplePackFileImpl.h @@ -38,7 +38,7 @@ class EXAMPLE : public PackFile { bool bake(const std::string& outputDir_ /*= ""*/, const EntryCallback& callback /*= nullptr*/) override; // [OPTIONAL] Returns any attributes your file format's entries have (refer to the other file formats for more info) - [[nodiscard]] std::vector getSupportedEntryAttributes() const override { + [[nodiscard]] Attribute getSupportedEntryAttributes() const override { return PackFile::getSupportedEntryAttributes(); } From 4ab09315bf7a4e7e41e83bd7e66fb60c590ee868 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 03:08:32 -0400 Subject: [PATCH 08/10] fix(vpkpp): improve windows filepath cleaning logic --- src/vpkpp/PackFile.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index b1a71a0b6..ea9a44cd7 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -91,23 +91,11 @@ void fixFilePathForWindows(std::string& path) { string::toUpper(stem); // Replace bad filenames - if (stem == "CON") { - filename = "_CON_" + extension; - } else if (stem == "PRN") { - filename = "_PRN_" + extension; - } else if (stem == "AUX") { - filename = "_AUX_" + extension; - } else if (stem == "NUL") { - filename = "_NUL_" + extension; - } else if (stem.starts_with("COM") && stem.length() == 4 && std::isdigit(stem[3]) && stem[3] != '0') { - filename = "_COM"; + if (stem == "CON" || stem == "PRN" || stem == "AUX" || stem == "NUL") { + filename = "___" + extension; + } else if (stem.length() == 4 && stem[3] != '0' && ((stem.starts_with("COM") || stem.starts_with("LPT")))) { + filename = "___"; filename += stem[3]; - filename += '_'; - filename += extension; - } else if (stem.starts_with("LPT") && stem.length() == 4 && std::isdigit(stem[3]) && stem[3] != '0') { - filename = "_LPT"; - filename += stem[3]; - filename += '_'; filename += extension; } @@ -126,7 +114,7 @@ void fixFilePathForWindows(std::string& path) { PackFile::PackFile(std::string fullFilePath_, PackFileOptions options_) : fullFilePath(std::move(fullFilePath_)) - , options(options_) {} + , options(options_) {} std::unique_ptr PackFile::open(const std::string& path, PackFileOptions options, const EntryCallback& callback) { auto extension = std::filesystem::path{path}.extension().string(); @@ -396,11 +384,12 @@ bool PackFile::extractAll(const std::string& outputDir, const EntryPredicate& pr // Extract std::filesystem::path outputDirPath{outputDir}; bool noneFailed = true; - for (auto& path : saveEntryPaths) { + for (const auto& path : saveEntryPaths) { + auto savePath = path; #ifdef _WIN32 - ::fixFilePathForWindows(path); + ::fixFilePathForWindows(savePath); #endif - if (!this->extractEntry(path, (outputDirPath / path.substr(rootDirLen)).string())) { + if (!this->extractEntry(path, (outputDirPath / savePath.substr(rootDirLen)).string())) { noneFailed = false; } } From c599f427d7c73a83517b09cb70f5a5f6bf85c8d5 Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 03:45:23 -0400 Subject: [PATCH 09/10] fix(vpkpp): add rename entry/directory methods --- include/vpkpp/PackFile.h | 6 ++++ lang/c/include/vpkppc/PackFile.h | 4 +++ lang/c/src/vpkppc/PackFile.cpp | 16 +++++++++++ lang/csharp/src/vpkpp/PackFile.cs | 22 ++++++++++++++ src/vpkpp/PackFile.cpp | 48 +++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/include/vpkpp/PackFile.h b/include/vpkpp/PackFile.h index 9bd1afde6..f0857cfc7 100644 --- a/include/vpkpp/PackFile.h +++ b/include/vpkpp/PackFile.h @@ -104,6 +104,12 @@ class PackFile { /// Add a new entry from a buffer void addEntry(const std::string& path, const std::byte* buffer, uint64_t bufferLen, EntryOptions options_); + /// Rename an existing entry + virtual bool renameEntry(const std::string& oldPath_, const std::string& newPath_); // NOLINT(*-use-nodiscard) + + /// Rename an existing directory + virtual bool renameDirectory(const std::string& oldDir_, const std::string& newDir_); // NOLINT(*-use-nodiscard) + /// Remove an entry virtual bool removeEntry(const std::string& path_); diff --git a/lang/c/include/vpkppc/PackFile.h b/lang/c/include/vpkppc/PackFile.h index 67b9c2dc5..777ae5ccf 100644 --- a/lang/c/include/vpkppc/PackFile.h +++ b/lang/c/include/vpkppc/PackFile.h @@ -74,6 +74,10 @@ SOURCEPP_API void vpkpp_add_entry_from_mem(vpkpp_pack_file_handle_t handle, cons SOURCEPP_API void vpkpp_add_entry_from_mem_with_options(vpkpp_pack_file_handle_t handle, const char* path, const unsigned char* buffer, size_t bufferLen, vpkpp_entry_options_t options); +SOURCEPP_API int vpkpp_rename_entry(vpkpp_pack_file_handle_t handle, const char* oldPath, const char* newPath); + +SOURCEPP_API int vpkpp_rename_directory(vpkpp_pack_file_handle_t handle, const char* oldDir, const char* newDir); + SOURCEPP_API int vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* path); SOURCEPP_API int vpkpp_remove_directory(vpkpp_pack_file_handle_t handle, const char* dirName); diff --git a/lang/c/src/vpkppc/PackFile.cpp b/lang/c/src/vpkppc/PackFile.cpp index 8b25c9c42..db6ff8460 100644 --- a/lang/c/src/vpkppc/PackFile.cpp +++ b/lang/c/src/vpkppc/PackFile.cpp @@ -189,6 +189,22 @@ SOURCEPP_API void vpkpp_add_entry_from_mem_with_options(vpkpp_pack_file_handle_t Convert::packFile(handle)->addEntry(path, reinterpret_cast(buffer), bufferLen, Convert::optionsFromC(options)); } +SOURCEPP_API int vpkpp_rename_entry(vpkpp_pack_file_handle_t handle, const char* oldPath, const char* newPath) { + SOURCEPP_EARLY_RETURN_VAL(handle, false); + SOURCEPP_EARLY_RETURN_VAL(oldPath, false); + SOURCEPP_EARLY_RETURN_VAL(newPath, false); + + return Convert::packFile(handle)->renameEntry(oldPath, newPath); +} + +SOURCEPP_API int vpkpp_rename_directory(vpkpp_pack_file_handle_t handle, const char* oldDir, const char* newDir) { + SOURCEPP_EARLY_RETURN_VAL(handle, false); + SOURCEPP_EARLY_RETURN_VAL(oldDir, false); + SOURCEPP_EARLY_RETURN_VAL(newDir, false); + + return Convert::packFile(handle)->renameEntry(oldDir, newDir); +} + SOURCEPP_API int vpkpp_remove_entry(vpkpp_pack_file_handle_t handle, const char* path) { SOURCEPP_EARLY_RETURN_VAL(handle, false); SOURCEPP_EARLY_RETURN_VAL(path, false); diff --git a/lang/csharp/src/vpkpp/PackFile.cs b/lang/csharp/src/vpkpp/PackFile.cs index a3300feec..ee28dbaae 100644 --- a/lang/csharp/src/vpkpp/PackFile.cs +++ b/lang/csharp/src/vpkpp/PackFile.cs @@ -76,6 +76,12 @@ internal static unsafe partial class Extern [DllImport("vpkppc")] public static extern void vpkpp_add_entry_from_mem(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path, byte* buffer, ulong bufferLen); + [DllImport("vpkppc")] + public static extern int vpkpp_rename_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string oldPath, [MarshalAs(UnmanagedType.LPStr)] string newPath); + + [DllImport("vpkppc")] + public static extern int vpkpp_rename_directory(void* handle, [MarshalAs(UnmanagedType.LPStr)] string oldDir, [MarshalAs(UnmanagedType.LPStr)] string newDir); + [DllImport("vpkppc")] public static extern int vpkpp_remove_entry(void* handle, [MarshalAs(UnmanagedType.LPStr)] string path); @@ -329,6 +335,22 @@ public void AddEntry(string path, IEnumerable buffer) } } + public bool RenameEntry(string oldPath, string newPath) + { + unsafe + { + return Convert.ToBoolean(Extern.vpkpp_rename_entry(Handle, oldPath, newPath)); + } + } + + public bool RenameDirectory(string oldDir, string newDir) + { + unsafe + { + return Convert.ToBoolean(Extern.vpkpp_rename_directory(Handle, oldDir, newDir)); + } + } + public bool RemoveEntry(string path) { unsafe diff --git a/src/vpkpp/PackFile.cpp b/src/vpkpp/PackFile.cpp index ea9a44cd7..9897527fa 100644 --- a/src/vpkpp/PackFile.cpp +++ b/src/vpkpp/PackFile.cpp @@ -230,6 +230,54 @@ void PackFile::addEntry(const std::string& path, const std::byte* buffer, uint64 this->addEntry(path, std::move(data), options_); } +bool PackFile::renameEntry(const std::string& oldPath_, const std::string& newPath_) { + auto oldPath = this->cleanEntryPath(oldPath_); + auto newPath = this->cleanEntryPath(newPath_); + if (this->entries.count(oldPath)) { + // Currently there is no pack file format that relies on file path to access data. + // If there ever is one, we're in trouble! (Well, no, just override the method.) + auto entry = this->entries.at(oldPath); + this->entries.erase(oldPath); + this->entries.emplace(newPath, entry); + return true; + } else if (this->unbakedEntries.count(oldPath)) { + auto entry = this->unbakedEntries.at(oldPath); + this->unbakedEntries.erase(oldPath); + this->unbakedEntries.emplace(newPath, entry); + return true; + } + return false; +} + +bool PackFile::renameDirectory(const std::string& oldDir_, const std::string& newDir_) { + auto oldDir = this->cleanEntryPath(oldDir_) + '/'; + auto newDir = this->cleanEntryPath(newDir_) + '/'; + + std::vector entryPaths; + std::vector unbakedEntryPaths; + this->runForAllEntries([&oldDir, &entryPaths, &unbakedEntryPaths](const std::string& path, const Entry& entry) { + if (path.starts_with(oldDir)) { + if (entry.unbaked) { + unbakedEntryPaths.push_back(path); + } else { + entryPaths.push_back(path); + } + } + }); + + for (const auto& entryPath : entryPaths) { + auto entry = this->entries.at(entryPath); + this->entries.erase(entryPath); + this->entries.emplace(newDir + entryPath.substr(oldDir.length()), entry); + } + for (const auto& entryPath : unbakedEntryPaths) { + auto entry = this->unbakedEntries.at(entryPath); + this->unbakedEntries.erase(entryPath); + this->unbakedEntries.emplace(newDir + entryPath.substr(oldDir.length()), entry); + } + return !entryPaths.empty() || !unbakedEntryPaths.empty(); +} + bool PackFile::removeEntry(const std::string& path_) { if (this->isReadOnly()) { return false; From 7dee2f1bea04993e88265410634d896461bedb8d Mon Sep 17 00:00:00 2001 From: craftablescience Date: Tue, 20 Aug 2024 04:20:03 -0400 Subject: [PATCH 10/10] fix(vpkpp): was adding an extra period to FPX/VPK extensions --- src/vpkpp/format/VPK.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vpkpp/format/VPK.cpp b/src/vpkpp/format/VPK.cpp index 5281c127a..3e5dc9c9a 100644 --- a/src/vpkpp/format/VPK.cpp +++ b/src/vpkpp/format/VPK.cpp @@ -378,9 +378,9 @@ void VPK::addEntryInternal(Entry& entry, const std::string& path, std::vectorfreedChunks.size(); i++) { if ( - (bestChunkIndex < 0 && this->freedChunks[i].length >= entry.length) || - (bestChunkIndex >= 0 && this->freedChunks[i].length >= entry.length && (this->freedChunks[i].length - entry.length) < currentChunkGap) - ) { + (bestChunkIndex < 0 && this->freedChunks[i].length >= entry.length) || + (bestChunkIndex >= 0 && this->freedChunks[i].length >= entry.length && (this->freedChunks[i].length - entry.length) < currentChunkGap) + ) { bestChunkIndex = i; currentChunkGap = this->freedChunks[i].length - entry.length; } @@ -448,6 +448,9 @@ bool VPK::bake(const std::string& outputDir_, const EntryCallback& callback) { this->runForAllEntries([&temp](const std::string& path, Entry& entry) { const auto fsPath = std::filesystem::path{path}; auto extension = fsPath.extension().string(); + if (extension.starts_with('.')) { + extension = extension.substr(1); + } auto parentDir = fsPath.parent_path().string(); if (extension.empty()) { @@ -598,10 +601,10 @@ bool VPK::bake(const std::string& outputDir_, const EntryCallback& callback) { return; } MD5Entry md5Entry{ - .archiveIndex = entry.archiveIndex, - .offset = static_cast(entry.offset), - .length = static_cast(entry.length - entry.extraData.size()), - .checksum = crypto::computeMD5(*binData), + .archiveIndex = entry.archiveIndex, + .offset = static_cast(entry.offset), + .length = static_cast(entry.length - entry.extraData.size()), + .checksum = crypto::computeMD5(*binData), }; this->md5Entries.push_back(md5Entry); }, false); @@ -684,7 +687,7 @@ Attribute VPK::getSupportedEntryAttributes() const { VPK::operator std::string() const { return PackFile::operator std::string() + - " | Version v" + std::to_string(this->header1.version); + " | Version v" + std::to_string(this->header1.version); } bool VPK::generateKeyPairFiles(const std::string& name) {