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:
28 template<typename R>
29 using EntryCallbackBase = std::function<R(const std::string& path, const Entry& entry)>;
32
33 // Accepts the entry's path
34 using EntryCreation = std::function<EntryOptions(const std::string& path)>;
35
36 using EntryTrie = tsl::htrie_map<char, Entry>;
37
38 PackFile(const PackFile& other) = delete;
39 PackFile& operator=(const PackFile& other) = delete;
40
41 PackFile(PackFile&& other) noexcept = default;
42 PackFile& operator=(PackFile&& other) noexcept = default;
43
44 virtual ~PackFile() = default;
45
47 [[nodiscard]] static std::unique_ptr<PackFile> open(const std::string& path, const EntryCallback& callback = nullptr);
48
50 [[nodiscard]] static std::vector<std::string> getOpenableExtensions();
51
53 [[nodiscard]] virtual constexpr std::string_view getGUID() const = 0;
54
56 template<typename T>
57 requires requires (const T&) {{T::GUID} -> std::convertible_to<std::string_view>;}
58 [[nodiscard]] bool isInstanceOf() const {
59 return this->getGUID() == T::GUID;
60 }
61
63 [[nodiscard]] virtual constexpr bool hasEntryChecksums() const {
64 return false;
65 }
66
69 [[nodiscard]] virtual std::vector<std::string> verifyEntryChecksums() const;
70
72 [[nodiscard]] virtual bool hasPackFileChecksum() const;
73
76 [[nodiscard]] virtual bool verifyPackFileChecksum() const;
77
79 [[nodiscard]] virtual bool hasPackFileSignature() const;
80
83 [[nodiscard]] virtual bool verifyPackFileSignature() const;
84
86 [[nodiscard]] virtual constexpr bool isCaseSensitive() const {
87 return false;
88 }
89
91 [[nodiscard]] bool hasEntry(const std::string& path, bool includeUnbaked = true) const;
92
94 [[nodiscard]] std::optional<Entry> findEntry(const std::string& path_, bool includeUnbaked = true) const;
95
97 [[nodiscard]] virtual std::optional<std::vector<std::byte>> readEntry(const std::string& path_) const = 0;
98
100 [[nodiscard]] std::optional<std::string> readEntryText(const std::string& path) const;
101
102 [[nodiscard]] virtual constexpr bool isReadOnly() const noexcept {
103 return false;
104 }
105
107 void addEntry(const std::string& entryPath, const std::string& filepath, EntryOptions options = {});
108
110 void addEntry(const std::string& path, std::vector<std::byte>&& buffer, EntryOptions options = {});
111
113 void addEntry(const std::string& path, const std::byte* buffer, uint64_t bufferLen, EntryOptions options = {});
114
116 void addDirectory(const std::string& entryBaseDir, const std::string& dir, EntryOptions options = {});
117
119 void addDirectory(const std::string& entryBaseDir, const std::string& dir, const EntryCreation& creation);
120
122 virtual bool renameEntry(const std::string& oldPath_, const std::string& newPath_); // NOLINT(*-use-nodiscard)
123
125 virtual bool renameDirectory(const std::string& oldDir_, const std::string& newDir_); // NOLINT(*-use-nodiscard)
126
128 virtual bool removeEntry(const std::string& path_);
129
131 virtual std::size_t removeDirectory(const std::string& dirName_);
132
134 virtual bool bake(const std::string& outputDir_ /*= ""*/, BakeOptions options /*= {}*/, const EntryCallback& callback /*= nullptr*/) = 0;
135
137 bool extractEntry(const std::string& entryPath, const std::string& filepath) const; // NOLINT(*-use-nodiscard)
138
140 bool extractDirectory(const std::string& dir_, const std::string& outputDir) const; // NOLINT(*-use-nodiscard)
141
143 bool extractAll(const std::string& outputDir, bool createUnderPackFileDir = true) const; // NOLINT(*-use-nodiscard)
144
146 bool extractAll(const std::string& outputDir, const EntryPredicate& predicate, bool stripSharedDirs = true) const; // NOLINT(*-use-nodiscard)
147
149 [[nodiscard]] const EntryTrie& getBakedEntries() const;
150
152 [[nodiscard]] const EntryTrie& getUnbakedEntries() const;
153
155 [[nodiscard]] std::size_t getEntryCount(bool includeUnbaked = true) const;
156
158 void runForAllEntries(const EntryCallback& operation, bool includeUnbaked = true) const;
159
161 void runForAllEntries(const std::string& parentDir, const EntryCallback& operation, bool recursive = true, bool includeUnbaked = true) const;
162
164 [[nodiscard]] std::string_view getFilepath() const;
165
167 [[nodiscard]] std::string getTruncatedFilepath() const;
168
170 [[nodiscard]] std::string getFilename() const;
171
173 [[nodiscard]] std::string getTruncatedFilename() const;
174
176 [[nodiscard]] std::string getFilestem() const;
177
179 [[nodiscard]] virtual std::string getTruncatedFilestem() const;
180
183 [[nodiscard]] virtual Attribute getSupportedEntryAttributes() const;
184
185 [[nodiscard]] virtual explicit operator std::string() const;
186
188 [[nodiscard]] static std::string escapeEntryPathForWrite(const std::string& path);
189
190protected:
191 explicit PackFile(std::string fullFilePath_);
192
193 void runForAllEntriesInternal(const std::function<void(const std::string&, Entry&)>& operation, bool includeUnbaked = true);
194
195 void runForAllEntriesInternal(const std::string& parentDir, const std::function<void(const std::string&, Entry&)>& operation, bool recursive = true, bool includeUnbaked = true);
196
197 [[nodiscard]] std::vector<std::string> verifyEntryChecksumsUsingCRC32() const;
198
199 virtual void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) = 0;
200
201 [[nodiscard]] std::string getBakeOutputDir(const std::string& outputDir) const;
202
203 void mergeUnbakedEntries();
204
205 void setFullFilePath(const std::string& outputDir);
206
207 [[nodiscard]] std::string cleanEntryPath(const std::string& path) const;
208
209 [[nodiscard]] static Entry createNewEntry();
210
211 [[nodiscard]] static std::optional<std::vector<std::byte>> readUnbakedEntry(const Entry& entry);
212
213 using OpenFactoryFunction = std::function<std::unique_ptr<PackFile>(const std::string& path, const EntryCallback& callback)>;
214
215 static std::unordered_map<std::string, std::vector<OpenFactoryFunction>>& getOpenExtensionRegistry();
216
217 static const OpenFactoryFunction& registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunction& factory);
218
219 std::string fullFilePath;
222};
223
225public:
226 [[nodiscard]] constexpr bool isReadOnly() const noexcept final {
227 return true;
228 }
229
230 [[nodiscard]] explicit operator std::string() const override;
231
232protected:
233 explicit PackFileReadOnly(const std::string& fullFilePath_);
234
235 void addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) final;
236
237 bool bake(const std::string& outputDir_, BakeOptions options, const EntryCallback& callback) final;
238};
239
240} // namespace vpkpp
241
242#define VPKPP_REGISTER_PACKFILE_OPEN(extension, function) \
243 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenTypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(extension, function)
244
245#define VPKPP_REGISTER_PACKFILE_OPEN_EXECUTABLE(function) \
246 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable0TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION0, function); \
247 static inline const OpenFactoryFunction& SOURCEPP_UNIQUE_NAME(packFileOpenExecutable1TypeFactoryFunction) = PackFile::registerOpenExtensionForTypeFactory(vpkpp::EXECUTABLE_EXTENSION1, function); \
248 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:731
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) final
Definition: PackFile.cpp:727
constexpr bool isReadOnly() const noexcept final
Definition: PackFile.h:226
virtual constexpr bool hasEntryChecksums() const
Returns true if the format has a checksum for each entry.
Definition: PackFile.h:63
tsl::htrie_map< char, Entry > EntryTrie
Definition: PackFile.h:36
std::optional< std::string > readEntryText(const std::string &path) const
Try to read the entry's data to a string.
Definition: PackFile.cpp:176
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:395
virtual ~PackFile()=default
virtual bool hasPackFileSignature() const
Returns true if the file is signed.
Definition: PackFile.cpp:151
EntryCallbackBase< void > EntryCallback
Definition: PackFile.h:30
virtual std::size_t removeDirectory(const std::string &dirName_)
Remove a directory.
Definition: PackFile.cpp:333
std::function< std::unique_ptr< PackFile >(const std::string &path, const EntryCallback &callback)> OpenFactoryFunction
Definition: PackFile.h:213
static std::unordered_map< std::string, std::vector< OpenFactoryFunction > > & getOpenExtensionRegistry()
Definition: PackFile.cpp:705
virtual bool renameDirectory(const std::string &oldDir_, const std::string &newDir_)
Rename an existing directory.
Definition: PackFile.cpp:287
void mergeUnbakedEntries()
Definition: PackFile.cpp:656
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:58
std::optional< Entry > findEntry(const std::string &path_, bool includeUnbaked=true) const
Try to find an entry given the file path.
Definition: PackFile.cpp:163
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:147
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:370
virtual bool verifyPackFileSignature() const
Verify the file signature, returns true on success Will return true if there is no signature ability ...
Definition: PackFile.cpp:155
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:351
std::string fullFilePath
Definition: PackFile.h:219
virtual std::string getTruncatedFilestem() const
/home/user/pak01_dir.vpk -> pak01
Definition: PackFile.cpp:604
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:139
EntryTrie entries
Definition: PackFile.h:220
PackFile & operator=(const PackFile &other)=delete
virtual bool hasPackFileChecksum() const
Returns true if the entire file has a checksum.
Definition: PackFile.cpp:143
std::vector< std::string > verifyEntryChecksumsUsingCRC32() const
Definition: PackFile.cpp:626
virtual constexpr bool isReadOnly() const noexcept
Definition: PackFile.h:102
EntryCallbackBase< bool > EntryPredicate
Definition: PackFile.h:31
PackFile(PackFile &&other) noexcept=default
virtual constexpr bool isCaseSensitive() const
Does the format support case-sensitive file names?
Definition: PackFile.h:86
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
Definition: PackFile.cpp:541
static const OpenFactoryFunction & registerOpenExtensionForTypeFactory(std::string_view extension, const OpenFactoryFunction &factory)
Definition: PackFile.cpp:710
std::string getFilestem() const
/home/user/pak01_dir.vpk -> pak01_dir
Definition: PackFile.cpp:600
bool hasEntry(const std::string &path, bool includeUnbaked=true) const
Check if an entry exists given the file path.
Definition: PackFile.cpp:159
virtual bool renameEntry(const std::string &oldPath_, const std::string &newPath_)
Rename an existing entry.
Definition: PackFile.cpp:268
std::string getFilename() const
/home/user/pak01_dir.vpk -> pak01_dir.vpk
Definition: PackFile.cpp:591
std::string getBakeOutputDir(const std::string &outputDir) const
Definition: PackFile.cpp:640
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:616
std::string getTruncatedFilepath() const
/home/user/pak01_dir.vpk -> /home/user/pak01
Definition: PackFile.cpp:587
void runForAllEntries(const EntryCallback &operation, bool includeUnbaked=true) const
Run a callback for each entry in the pack file.
Definition: PackFile.cpp:499
void setFullFilePath(const std::string &outputDir)
Definition: PackFile.cpp:672
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:232
std::function< EntryOptions(const std::string &path)> EntryCreation
Definition: PackFile.h:34
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:190
std::string cleanEntryPath(const std::string &path) const
Definition: PackFile.cpp:677
virtual Attribute getSupportedEntryAttributes() const
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition: PackFile.cpp:608
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:128
PackFile & operator=(PackFile &&other) noexcept=default
const EntryTrie & getBakedEntries() const
Get entries saved to disk.
Definition: PackFile.cpp:482
static Entry createNewEntry()
Definition: PackFile.cpp:686
EntryTrie unbakedEntries
Definition: PackFile.h:221
std::string getTruncatedFilename() const
/home/user/pak01_dir.vpk -> pak01.vpk
Definition: PackFile.cpp:595
std::size_t getEntryCount(bool includeUnbaked=true) const
Get the number of entries in the pack file.
Definition: PackFile.cpp:490
virtual bool removeEntry(const std::string &path_)
Remove an entry.
Definition: PackFile.cpp:316
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a generic pack file. The parser is selected based on the file extension.
Definition: PackFile.cpp:114
std::string_view getFilepath() const
/home/user/pak01_dir.vpk
Definition: PackFile.cpp:583
std::function< R(const std::string &path, const Entry &entry)> EntryCallbackBase
Accepts the entry's path and metadata.
Definition: PackFile.h:29
const EntryTrie & getUnbakedEntries() const
Get entries that have been added but not yet baked.
Definition: PackFile.cpp:486
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition: PackFile.cpp:690
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