SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
PackFile.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <memory>
5#include <optional>
6#include <string>
7#include <string_view>
8#include <unordered_map>
9#include <vector>
10
11#include <sourcepp/Macros.h>
12#include <tsl/htrie_map.h>
13
14#include "Attribute.h"
15#include "Entry.h"
16#include "Options.h"
17
18namespace vpkpp {
19
20// Executable extensions - mostly just for Godot exports
21constexpr std::string_view EXECUTABLE_EXTENSION0 = ".exe"; // - Windows
22constexpr std::string_view EXECUTABLE_EXTENSION1 = ".bin"; // - Linux + Godot 3 and below (and Generic)
23constexpr std::string_view EXECUTABLE_EXTENSION2 = ".x86_64"; // | Godot 4 (64-bit)
24
25class PackFile {
26public:
27 enum class OpenProperty {
28 DECRYPTION_KEY, // The pack file is encrypted
29 };
30
31 // Accepts the pack file GUID and request property, returns the requested value
32 using OpenPropertyRequest = std::function<std::vector<std::byte>(PackFile* packFile, OpenProperty property)>;
33
35 template<typename R>
36 using EntryCallbackBase = std::function<R(const std::string& path, const Entry& entry)>;
39
40 // Accepts the entry's path
41 using EntryCreation = std::function<EntryOptions(const std::string& path)>;
42
43 using EntryTrie = tsl::htrie_map<char, Entry>;
44
45 PackFile(const PackFile& other) = delete;
46 PackFile& operator=(const PackFile& other) = delete;
47
48 PackFile(PackFile&& other) noexcept = default;
49 PackFile& operator=(PackFile&& other) noexcept = default;
50
51 virtual ~PackFile() = default;
52
54 [[nodiscard]] static std::unique_ptr<PackFile> open(const std::string& path, const EntryCallback& callback = nullptr, const OpenPropertyRequest& requestProperty = nullptr);
55
57 [[nodiscard]] static std::vector<std::string> getOpenableExtensions();
58
60 [[nodiscard]] virtual constexpr std::string_view getGUID() const = 0;
61
63 template<typename T>
64 requires requires (const T&) {{T::GUID} -> std::convertible_to<std::string_view>;}
65 [[nodiscard]] bool isInstanceOf() const {
66 return this->getGUID() == T::GUID;
67 }
68
70 [[nodiscard]] virtual constexpr bool hasEntryChecksums() const {
71 return false;
72 }
73
76 [[nodiscard]] virtual std::vector<std::string> verifyEntryChecksums() const;
77
79 [[nodiscard]] virtual bool hasPackFileChecksum() const;
80
83 [[nodiscard]] virtual bool verifyPackFileChecksum() const;
84
86 [[nodiscard]] virtual bool hasPackFileSignature() const;
87
90 [[nodiscard]] virtual bool verifyPackFileSignature() const;
91
93 [[nodiscard]] virtual constexpr bool isCaseSensitive() const {
94 return false;
95 }
96
98 [[nodiscard]] bool hasEntry(const std::string& path, bool includeUnbaked = true) const;
99
101 [[nodiscard]] std::optional<Entry> findEntry(const std::string& path_, bool includeUnbaked = true) const;
102
104 [[nodiscard]] virtual std::optional<std::vector<std::byte>> readEntry(const std::string& path_) const = 0;
105
107 [[nodiscard]] std::optional<std::string> readEntryText(const std::string& path) const;
108
109 [[nodiscard]] virtual constexpr bool isReadOnly() const noexcept {
110 return false;
111 }
112
114 void addEntry(const std::string& entryPath, const std::string& filepath, EntryOptions options = {});
115
117 void addEntry(const std::string& path, std::vector<std::byte>&& buffer, EntryOptions options = {});
118
120 void addEntry(const std::string& path, const std::byte* buffer, uint64_t bufferLen, EntryOptions options = {});
121
123 void addDirectory(const std::string& entryBaseDir, const std::string& dir, EntryOptions options = {});
124
126 void addDirectory(const std::string& entryBaseDir_, const std::string& dir, const EntryCreation& creation);
127
129 virtual bool renameEntry(const std::string& oldPath_, const std::string& newPath_); // NOLINT(*-use-nodiscard)
130
132 virtual bool renameDirectory(const std::string& oldDir_, const std::string& newDir_); // NOLINT(*-use-nodiscard)
133
135 virtual bool removeEntry(const std::string& path_);
136
138 virtual std::size_t removeDirectory(const std::string& dirName_);
139
141 virtual bool bake(const std::string& outputDir_ /*= ""*/, BakeOptions options /*= {}*/, const EntryCallback& callback /*= nullptr*/) = 0;
142
144 bool extractEntry(const std::string& entryPath, const std::string& filepath) const; // NOLINT(*-use-nodiscard)
145
147 bool extractDirectory(const std::string& dir_, const std::string& outputDir) const; // NOLINT(*-use-nodiscard)
148
150 bool extractAll(const std::string& outputDir, bool createUnderPackFileDir = true) const; // NOLINT(*-use-nodiscard)
151
153 bool extractAll(const std::string& outputDir, const EntryPredicate& predicate, bool stripSharedDirs = true) const; // NOLINT(*-use-nodiscard)
154
156 [[nodiscard]] const EntryTrie& getBakedEntries() const;
157
159 [[nodiscard]] const EntryTrie& getUnbakedEntries() const;
160
162 [[nodiscard]] std::size_t getEntryCount(bool includeUnbaked = true) const;
163
165 void runForAllEntries(const EntryCallback& operation, bool includeUnbaked = true) const;
166
168 void runForAllEntries(const std::string& parentDir, const EntryCallback& operation, bool recursive = true, bool includeUnbaked = true) const;
169
171 [[nodiscard]] std::string_view getFilepath() const;
172
174 [[nodiscard]] std::string getTruncatedFilepath() const;
175
177 [[nodiscard]] std::string getFilename() const;
178
180 [[nodiscard]] std::string getTruncatedFilename() const;
181
183 [[nodiscard]] std::string getFilestem() const;
184
186 [[nodiscard]] virtual std::string getTruncatedFilestem() const;
187
190 [[nodiscard]] virtual Attribute getSupportedEntryAttributes() const;
191
192 [[nodiscard]] virtual explicit operator std::string() const;
193
195 [[nodiscard]] static std::string escapeEntryPathForWrite(const std::string& path);
196
197protected:
198 explicit PackFile(std::string fullFilePath_);
199
200 void runForAllEntriesInternal(const std::function<void(const std::string&, Entry&)>& operation, bool includeUnbaked = true);
201
202 void runForAllEntriesInternal(const std::string& parentDir, const std::function<void(const std::string&, Entry&)>& operation, bool recursive = true, bool includeUnbaked = true);
203
204 [[nodiscard]] std::vector<std::string> verifyEntryChecksumsUsingCRC32() const;
205
206 virtual void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) = 0;
207
208 [[nodiscard]] std::string getBakeOutputDir(const std::string& outputDir) const;
209
210 void mergeUnbakedEntries();
211
212 void setFullFilePath(const std::string& outputDir);
213
214 [[nodiscard]] std::string cleanEntryPath(const std::string& path) const;
215
216 [[nodiscard]] static Entry createNewEntry();
217
218 [[nodiscard]] static std::optional<std::vector<std::byte>> readUnbakedEntry(const Entry& entry);
219
220 using OpenFactoryFunctionBasic = std::function<std::unique_ptr<PackFile>(const std::string& path, const EntryCallback& callback)>;
221 using OpenFactoryFunction = std::function<std::unique_ptr<PackFile>(const std::string& path, const EntryCallback& callback, const OpenPropertyRequest& requestProperty)>;
222
223 static std::unordered_map<std::string, std::vector<OpenFactoryFunction>>& getOpenExtensionRegistry();
224
225 static const OpenFactoryFunction& registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunctionBasic& factory);
226
227 static const OpenFactoryFunction& registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunction& factory);
228
229 std::string fullFilePath;
232};
233
235public:
236 [[nodiscard]] constexpr bool isReadOnly() const noexcept final {
237 return true;
238 }
239
240 [[nodiscard]] explicit operator std::string() const override;
241
242protected:
243 explicit PackFileReadOnly(const std::string& fullFilePath_);
244
245 void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) final;
246
247 bool bake(const std::string& outputDir_, BakeOptions options, const EntryCallback& callback) final;
248};
249
250} // namespace vpkpp
251
252#define VPKPP_REGISTER_PACKFILE_OPEN(extension, function) \
253 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenTypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(extension, function)
254
255#define VPKPP_REGISTER_PACKFILE_OPEN_EXECUTABLE(function) \
256 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable0TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION0, function); \
257 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable1TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION1, function); \
258 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable2TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION2, function)
This class represents the metadata that a file has inside a PackFile.
Definition: Entry.h:14
bool bake(const std::string &outputDir_, BakeOptions options, const EntryCallback &callback) final
If output folder is an empty string, it will overwrite the original.
Definition: PackFile.cpp:740
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) final
Definition: PackFile.cpp:736
constexpr bool isReadOnly() const noexcept final
Definition: PackFile.h:236
virtual constexpr bool hasEntryChecksums() const
Returns true if the format has a checksum for each entry.
Definition: PackFile.h:70
std::function< std::unique_ptr< PackFile >(const std::string &path, const EntryCallback &callback)> OpenFactoryFunctionBasic
Definition: PackFile.h:220
tsl::htrie_map< char, Entry > EntryTrie
Definition: PackFile.h:43
std::optional< std::string > readEntryText(const std::string &path) const
Try to read the entry's data to a string.
Definition: PackFile.cpp:179
bool extractAll(const std::string &outputDir, bool createUnderPackFileDir=true) const
Extract the contents of the pack file to disk at the given directory.
Definition: PackFile.cpp:399
virtual ~PackFile()=default
virtual bool hasPackFileSignature() const
Returns true if the file is signed.
Definition: PackFile.cpp:154
EntryCallbackBase< void > EntryCallback
Definition: PackFile.h:37
virtual std::size_t removeDirectory(const std::string &dirName_)
Remove a directory.
Definition: PackFile.cpp:337
static std::unordered_map< std::string, std::vector< OpenFactoryFunction > > & getOpenExtensionRegistry()
Definition: PackFile.cpp:708
static const OpenFactoryFunction & registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunctionBasic &factory)
Definition: PackFile.cpp:713
virtual bool renameDirectory(const std::string &oldDir_, const std::string &newDir_)
Rename an existing directory.
Definition: PackFile.cpp:291
void mergeUnbakedEntries()
Definition: PackFile.cpp:659
virtual constexpr std::string_view getGUID() const =0
Get the GUID corresponding to the pack file type.
bool isInstanceOf() const
Check if the pack file is an instance of the given pack file class.
Definition: PackFile.h:65
std::optional< Entry > findEntry(const std::string &path_, bool includeUnbaked=true) const
Try to find an entry given the file path.
Definition: PackFile.cpp:166
virtual bool verifyPackFileChecksum() const
Verify the checksum of the entire file, returns true on success Will return true if there is no check...
Definition: PackFile.cpp:150
bool extractDirectory(const std::string &dir_, const std::string &outputDir) const
Extract the given directory to disk under the given output directory.
Definition: PackFile.cpp:374
virtual bool verifyPackFileSignature() const
Verify the file signature, returns true on success Will return true if there is no signature ability ...
Definition: PackFile.cpp:158
virtual void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options)=0
virtual bool bake(const std::string &outputDir_, BakeOptions options, const EntryCallback &callback)=0
If output folder is an empty string, it will overwrite the original.
bool extractEntry(const std::string &entryPath, const std::string &filepath) const
Extract the given entry to disk at the given file path.
Definition: PackFile.cpp:355
std::string fullFilePath
Definition: PackFile.h:229
virtual std::string getTruncatedFilestem() const
/home/user/pak01_dir.vpk -> pak01
Definition: PackFile.cpp:608
virtual std::vector< std::string > verifyEntryChecksums() const
Verify the checksums of each file, if a file fails the check its path will be added to the vector If ...
Definition: PackFile.cpp:142
EntryTrie entries
Definition: PackFile.h:230
PackFile & operator=(const PackFile &other)=delete
virtual bool hasPackFileChecksum() const
Returns true if the entire file has a checksum.
Definition: PackFile.cpp:146
std::vector< std::string > verifyEntryChecksumsUsingCRC32() const
Definition: PackFile.cpp:630
virtual constexpr bool isReadOnly() const noexcept
Definition: PackFile.h:109
EntryCallbackBase< bool > EntryPredicate
Definition: PackFile.h:38
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr, const OpenPropertyRequest &requestProperty=nullptr)
Open a generic pack file. The parser is selected based on the file extension.
Definition: PackFile.cpp:117
PackFile(PackFile &&other) noexcept=default
virtual constexpr bool isCaseSensitive() const
Does the format support case-sensitive file names?
Definition: PackFile.h:93
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
Definition: PackFile.cpp:545
std::function< std::vector< std::byte >(PackFile *packFile, OpenProperty property)> OpenPropertyRequest
Definition: PackFile.h:32
std::string getFilestem() const
/home/user/pak01_dir.vpk -> pak01_dir
Definition: PackFile.cpp:604
bool hasEntry(const std::string &path, bool includeUnbaked=true) const
Check if an entry exists given the file path.
Definition: PackFile.cpp:162
virtual bool renameEntry(const std::string &oldPath_, const std::string &newPath_)
Rename an existing entry.
Definition: PackFile.cpp:271
std::string getFilename() const
/home/user/pak01_dir.vpk -> pak01_dir.vpk
Definition: PackFile.cpp:595
std::string getBakeOutputDir(const std::string &outputDir) const
Definition: PackFile.cpp:644
static std::string escapeEntryPathForWrite(const std::string &path)
On Windows, some characters and file names are invalid - this escapes the given entry path.
Definition: PackFile.cpp:620
std::string getTruncatedFilepath() const
/home/user/pak01_dir.vpk -> /home/user/pak01
Definition: PackFile.cpp:591
void runForAllEntries(const EntryCallback &operation, bool includeUnbaked=true) const
Run a callback for each entry in the pack file.
Definition: PackFile.cpp:503
void setFullFilePath(const std::string &outputDir)
Definition: PackFile.cpp:675
std::function< std::unique_ptr< PackFile >(const std::string &path, const EntryCallback &callback, const OpenPropertyRequest &requestProperty)> OpenFactoryFunction
Definition: PackFile.h:221
void addDirectory(const std::string &entryBaseDir, const std::string &dir, EntryOptions options={})
Adds new entries using the contents of a given directory.
Definition: PackFile.cpp:235
std::function< EntryOptions(const std::string &path)> EntryCreation
Definition: PackFile.h:41
void addEntry(const std::string &entryPath, const std::string &filepath, EntryOptions options={})
Add a new entry from a file path - the first parameter is the path in the PackFile,...
Definition: PackFile.cpp:193
std::string cleanEntryPath(const std::string &path) const
Definition: PackFile.cpp:680
virtual Attribute getSupportedEntryAttributes() const
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition: PackFile.cpp:612
virtual std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const =0
Try to read the entry's data to a bytebuffer.
PackFile(const PackFile &other)=delete
static std::vector< std::string > getOpenableExtensions()
Returns a sorted list of supported extensions for opening, e.g. {".bsp", ".vpk"}.
Definition: PackFile.cpp:131
PackFile & operator=(PackFile &&other) noexcept=default
const EntryTrie & getBakedEntries() const
Get entries saved to disk.
Definition: PackFile.cpp:486
static Entry createNewEntry()
Definition: PackFile.cpp:689
EntryTrie unbakedEntries
Definition: PackFile.h:231
std::string getTruncatedFilename() const
/home/user/pak01_dir.vpk -> pak01.vpk
Definition: PackFile.cpp:599
std::size_t getEntryCount(bool includeUnbaked=true) const
Get the number of entries in the pack file.
Definition: PackFile.cpp:494
virtual bool removeEntry(const std::string &path_)
Remove an entry.
Definition: PackFile.cpp:320
std::string_view getFilepath() const
/home/user/pak01_dir.vpk
Definition: PackFile.cpp:587
std::function< R(const std::string &path, const Entry &entry)> EntryCallbackBase
Accepts the entry's path and metadata.
Definition: PackFile.h:36
const EntryTrie & getUnbakedEntries() const
Get entries that have been added but not yet baked.
Definition: PackFile.cpp:490
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition: PackFile.cpp:693
Definition: Attribute.h:5
constexpr std::string_view EXECUTABLE_EXTENSION1
Definition: PackFile.h:22
constexpr std::string_view EXECUTABLE_EXTENSION0
Definition: PackFile.h:21
Attribute
Definition: Attribute.h:7
constexpr std::string_view EXECUTABLE_EXTENSION2
Definition: PackFile.h:23