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 <span>
7#include <string>
8#include <string_view>
9#include <unordered_map>
10#include <vector>
11
12#include <sourcepp/Macros.h>
13#include <tsl/htrie_map.h>
14
15#include "Attribute.h"
16#include "Entry.h"
17#include "Options.h"
18
19namespace vpkpp {
20
21// Executable extensions - mostly just for Godot exports
22constexpr std::string_view EXECUTABLE_EXTENSION0 = ".exe"; // - Windows
23constexpr std::string_view EXECUTABLE_EXTENSION1 = ".bin"; // - Linux + Godot 3 and below (and Generic)
24constexpr std::string_view EXECUTABLE_EXTENSION2 = ".x86_64"; // | Godot 4 (64-bit)
25
26class PackFile {
27public:
28 enum class OpenProperty {
29 DECRYPTION_KEY, // The pack file is encrypted
30 };
31
32 // Accepts the pack file GUID and request property, returns the requested value
33 using OpenPropertyRequest = std::function<std::vector<std::byte>(PackFile* packFile, OpenProperty property)>;
34
36 template<typename R>
37 using EntryCallbackBase = std::function<R(const std::string& path, const Entry& entry)>;
40
41 // Accepts the entry's path
42 using EntryCreation = std::function<EntryOptions(const std::string& path)>;
43
44 using EntryTrie = tsl::htrie_map<char, Entry>;
45
46 PackFile(const PackFile& other) = delete;
47 PackFile& operator=(const PackFile& other) = delete;
48
49 PackFile(PackFile&& other) noexcept = default;
50 PackFile& operator=(PackFile&& other) noexcept = default;
51
52 virtual ~PackFile() = default;
53
55 [[nodiscard]] static std::unique_ptr<PackFile> open(const std::string& path, const EntryCallback& callback = nullptr, const OpenPropertyRequest& requestProperty = nullptr);
56
58 [[nodiscard]] static std::vector<std::string> getOpenableExtensions();
59
61 [[nodiscard]] virtual constexpr std::string_view getGUID() const = 0;
62
64 template<typename T>
65 requires requires (const T&) {{T::GUID} -> std::convertible_to<std::string_view>;}
66 [[nodiscard]] bool isInstanceOf() const {
67 return this->getGUID() == T::GUID;
68 }
69
71 [[nodiscard]] virtual constexpr bool hasEntryChecksums() const {
72 return false;
73 }
74
77 [[nodiscard]] virtual std::vector<std::string> verifyEntryChecksums() const;
78
80 [[nodiscard]] virtual bool hasPackFileChecksum() const;
81
84 [[nodiscard]] virtual bool verifyPackFileChecksum() const;
85
87 [[nodiscard]] virtual bool hasPackFileSignature() const;
88
91 [[nodiscard]] virtual bool verifyPackFileSignature() const;
92
94 [[nodiscard]] virtual constexpr bool isCaseSensitive() const {
95 return false;
96 }
97
99 [[nodiscard]] bool hasEntry(const std::string& path, bool includeUnbaked = true) const;
100
102 [[nodiscard]] std::optional<Entry> findEntry(const std::string& path_, bool includeUnbaked = true) const;
103
105 [[nodiscard]] virtual std::optional<std::vector<std::byte>> readEntry(const std::string& path_) const = 0;
106
107 [[nodiscard]] std::optional<std::vector<std::byte>> operator[](const std::string& path_) const;
108
110 [[nodiscard]] std::optional<std::string> readEntryText(const std::string& path) const;
111
112 [[nodiscard]] virtual constexpr bool isReadOnly() const noexcept {
113 return false;
114 }
115
117 void addEntry(const std::string& entryPath, const std::string& filepath, EntryOptions options = {});
118
120 void addEntry(const std::string& path, std::vector<std::byte>&& buffer, EntryOptions options = {});
121
123 void addEntry(const std::string& path, std::span<const std::byte> buffer, EntryOptions options = {});
124
126 void addDirectory(const std::string& entryBaseDir, const std::string& dir, EntryOptions options = {});
127
129 void addDirectory(const std::string& entryBaseDir_, const std::string& dir, const EntryCreation& creation);
130
132 virtual bool renameEntry(const std::string& oldPath_, const std::string& newPath_); // NOLINT(*-use-nodiscard)
133
135 virtual bool renameDirectory(const std::string& oldDir_, const std::string& newDir_); // NOLINT(*-use-nodiscard)
136
138 virtual bool removeEntry(const std::string& path_);
139
141 virtual std::size_t removeDirectory(const std::string& dirName_);
142
144 virtual bool bake(const std::string& outputDir_ /*= ""*/, BakeOptions options /*= {}*/, const EntryCallback& callback /*= nullptr*/) = 0;
145
147 bool extractEntry(const std::string& entryPath, const std::string& filepath) const; // NOLINT(*-use-nodiscard)
148
150 bool extractDirectory(const std::string& dir_, const std::string& outputDir) const; // NOLINT(*-use-nodiscard)
151
153 bool extractAll(const std::string& outputDir, bool createUnderPackFileDir = true) const; // NOLINT(*-use-nodiscard)
154
156 bool extractAll(const std::string& outputDir, const EntryPredicate& predicate, bool stripSharedDirs = true) const; // NOLINT(*-use-nodiscard)
157
159 [[nodiscard]] const EntryTrie& getBakedEntries() const;
160
162 [[nodiscard]] const EntryTrie& getUnbakedEntries() const;
163
165 [[nodiscard]] std::size_t getEntryCount(bool includeUnbaked = true) const;
166
168 void runForAllEntries(const EntryCallback& operation, bool includeUnbaked = true) const;
169
171 void runForAllEntries(const std::string& parentDir, const EntryCallback& operation, bool recursive = true, bool includeUnbaked = true) const;
172
174 [[nodiscard]] std::string_view getFilepath() const;
175
177 [[nodiscard]] std::string getTruncatedFilepath() const;
178
180 [[nodiscard]] std::string getFilename() const;
181
183 [[nodiscard]] std::string getTruncatedFilename() const;
184
186 [[nodiscard]] std::string getFilestem() const;
187
189 [[nodiscard]] virtual std::string getTruncatedFilestem() const;
190
193 [[nodiscard]] virtual Attribute getSupportedEntryAttributes() const;
194
195 [[nodiscard]] virtual explicit operator std::string() const;
196
198 [[nodiscard]] static std::string escapeEntryPathForWrite(const std::string& path);
199
200protected:
201 explicit PackFile(std::string fullFilePath_);
202
203 void runForAllEntriesInternal(const std::function<void(const std::string&, Entry&)>& operation, bool includeUnbaked = true);
204
205 void runForAllEntriesInternal(const std::string& parentDir, const std::function<void(const std::string&, Entry&)>& operation, bool recursive = true, bool includeUnbaked = true);
206
207 [[nodiscard]] std::vector<std::string> verifyEntryChecksumsUsingCRC32() const;
208
209 virtual void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) = 0;
210
211 [[nodiscard]] std::string getBakeOutputDir(const std::string& outputDir) const;
212
213 void mergeUnbakedEntries();
214
215 void setFullFilePath(const std::string& outputDir);
216
217 [[nodiscard]] std::string cleanEntryPath(const std::string& path) const;
218
219 [[nodiscard]] static Entry createNewEntry();
220
221 [[nodiscard]] static std::optional<std::vector<std::byte>> readUnbakedEntry(const Entry& entry);
222
223 using OpenFactoryFunctionBasic = std::function<std::unique_ptr<PackFile>(const std::string& path, const EntryCallback& callback)>;
224 using OpenFactoryFunction = std::function<std::unique_ptr<PackFile>(const std::string& path, const EntryCallback& callback, const OpenPropertyRequest& requestProperty)>;
225
226 static std::unordered_map<std::string, std::vector<OpenFactoryFunction>>& getOpenExtensionRegistry();
227
228 static const OpenFactoryFunction& registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunctionBasic& factory);
229
230 static const OpenFactoryFunction& registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunction& factory);
231
232 std::string fullFilePath;
235};
236
238public:
239 [[nodiscard]] constexpr bool isReadOnly() const noexcept final {
240 return true;
241 }
242
243 [[nodiscard]] explicit operator std::string() const override;
244
245protected:
246 explicit PackFileReadOnly(const std::string& fullFilePath_);
247
248 void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) final;
249
250 bool bake(const std::string& outputDir_, BakeOptions options, const EntryCallback& callback) final;
251};
252
253} // namespace vpkpp
254
255#define VPKPP_REGISTER_PACKFILE_OPEN(extension, function) \
256 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenTypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(extension, function)
257
258#define VPKPP_REGISTER_PACKFILE_OPEN_EXECUTABLE(function) \
259 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable0TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION0, function); \
260 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable1TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION1, function); \
261 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:739
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) final
Definition: PackFile.cpp:735
constexpr bool isReadOnly() const noexcept final
Definition: PackFile.h:239
virtual constexpr bool hasEntryChecksums() const
Returns true if the format has a checksum for each entry.
Definition: PackFile.h:71
std::function< std::unique_ptr< PackFile >(const std::string &path, const EntryCallback &callback)> OpenFactoryFunctionBasic
Definition: PackFile.h:223
tsl::htrie_map< char, Entry > EntryTrie
Definition: PackFile.h:44
std::optional< std::string > readEntryText(const std::string &path) const
Try to read the entry's data to a string.
Definition: PackFile.cpp:183
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:398
virtual ~PackFile()=default
virtual bool hasPackFileSignature() const
Returns true if the file is signed.
Definition: PackFile.cpp:154
EntryCallbackBase< void > EntryCallback
Definition: PackFile.h:38
virtual std::size_t removeDirectory(const std::string &dirName_)
Remove a directory.
Definition: PackFile.cpp:336
static std::unordered_map< std::string, std::vector< OpenFactoryFunction > > & getOpenExtensionRegistry()
Definition: PackFile.cpp:707
static const OpenFactoryFunction & registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunctionBasic &factory)
Definition: PackFile.cpp:712
virtual bool renameDirectory(const std::string &oldDir_, const std::string &newDir_)
Rename an existing directory.
Definition: PackFile.cpp:290
void mergeUnbakedEntries()
Definition: PackFile.cpp:658
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:66
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:373
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:354
std::string fullFilePath
Definition: PackFile.h:232
virtual std::string getTruncatedFilestem() const
/home/user/pak01_dir.vpk -> pak01
Definition: PackFile.cpp:607
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:233
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:629
virtual constexpr bool isReadOnly() const noexcept
Definition: PackFile.h:112
EntryCallbackBase< bool > EntryPredicate
Definition: PackFile.h:39
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:94
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
Definition: PackFile.cpp:544
std::function< std::vector< std::byte >(PackFile *packFile, OpenProperty property)> OpenPropertyRequest
Definition: PackFile.h:33
std::string getFilestem() const
/home/user/pak01_dir.vpk -> pak01_dir
Definition: PackFile.cpp:603
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:270
std::string getFilename() const
/home/user/pak01_dir.vpk -> pak01_dir.vpk
Definition: PackFile.cpp:594
std::string getBakeOutputDir(const std::string &outputDir) const
Definition: PackFile.cpp:643
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:619
std::string getTruncatedFilepath() const
/home/user/pak01_dir.vpk -> /home/user/pak01
Definition: PackFile.cpp:590
void runForAllEntries(const EntryCallback &operation, bool includeUnbaked=true) const
Run a callback for each entry in the pack file.
Definition: PackFile.cpp:502
void setFullFilePath(const std::string &outputDir)
Definition: PackFile.cpp:674
std::function< std::unique_ptr< PackFile >(const std::string &path, const EntryCallback &callback, const OpenPropertyRequest &requestProperty)> OpenFactoryFunction
Definition: PackFile.h:224
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:234
std::function< EntryOptions(const std::string &path)> EntryCreation
Definition: PackFile.h:42
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:197
std::string cleanEntryPath(const std::string &path) const
Definition: PackFile.cpp:679
virtual Attribute getSupportedEntryAttributes() const
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition: PackFile.cpp:611
std::optional< std::vector< std::byte > > operator[](const std::string &path_) const
Definition: PackFile.cpp:179
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:485
static Entry createNewEntry()
Definition: PackFile.cpp:688
EntryTrie unbakedEntries
Definition: PackFile.h:234
std::string getTruncatedFilename() const
/home/user/pak01_dir.vpk -> pak01.vpk
Definition: PackFile.cpp:598
std::size_t getEntryCount(bool includeUnbaked=true) const
Get the number of entries in the pack file.
Definition: PackFile.cpp:493
virtual bool removeEntry(const std::string &path_)
Remove an entry.
Definition: PackFile.cpp:319
std::string_view getFilepath() const
/home/user/pak01_dir.vpk
Definition: PackFile.cpp:586
std::function< R(const std::string &path, const Entry &entry)> EntryCallbackBase
Accepts the entry's path and metadata.
Definition: PackFile.h:37
const EntryTrie & getUnbakedEntries() const
Get entries that have been added but not yet baked.
Definition: PackFile.cpp:489
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition: PackFile.cpp:692
Definition: Attribute.h:5
constexpr std::string_view EXECUTABLE_EXTENSION1
Definition: PackFile.h:23
constexpr std::string_view EXECUTABLE_EXTENSION0
Definition: PackFile.h:22
Attribute
Definition: Attribute.h:7
constexpr std::string_view EXECUTABLE_EXTENSION2
Definition: PackFile.h:24