SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
PAK.cpp
Go to the documentation of this file.
1// ReSharper disable CppRedundantQualifier
2
3#include <vpkpp/format/PAK.h>
4
5#include <filesystem>
6
7#include <FileStream.h>
8
9using namespace sourcepp;
10using namespace vpkpp;
11
12std::unique_ptr<PackFile> PAK::create(const std::string& path, Type type) {
13 {
14 FileStream stream{path, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
15 switch (type) {
16 case Type::PAK:
17 stream << PAK_SIGNATURE;
18 break;
19 case Type::SIN:
20 stream << PAK_SIN_SIGNATURE;
21 break;
22 case Type::HROT:
23 stream << PAK_HROT_SIGNATURE;
24 break;
25 }
26 stream
27 .write<uint32_t>(0)
28 .write<uint32_t>(0);
29 }
30 return PAK::open(path);
31}
32
33std::unique_ptr<PackFile> PAK::open(const std::string& path, const EntryCallback& callback) {
34 if (!std::filesystem::exists(path)) {
35 // File does not exist
36 return nullptr;
37 }
38
39 auto* pak = new PAK{path};
40 auto packFile = std::unique_ptr<PackFile>(pak);
41
42 FileStream reader{pak->fullFilePath};
43 reader.seek_in(0);
44
45 if (const auto signature = reader.read<uint32_t>(); signature == PAK_SIGNATURE) {
46 pak->type = Type::PAK;
47 } else if (signature == PAK_SIN_SIGNATURE) {
48 pak->type = Type::SIN;
49 } else if (signature == PAK_HROT_SIGNATURE) {
50 pak->type = Type::HROT;
51 } else {
52 // File is not a PAK
53 return nullptr;
54 }
55
56 const auto directoryOffset = reader.read<uint32_t>();
57 // Directory size / file entry size
58 const auto fileCount = reader.read<uint32_t>() / (sizeof(uint32_t) * 2 + pak->getFilenameLength());
59
60 reader.seek_in(directoryOffset);
61 for (uint32_t i = 0; i < fileCount; i++) {
62 Entry entry = createNewEntry();
63
64 auto entryPath = pak->cleanEntryPath(reader.read_string(pak->getFilenameLength()));
65
66 entry.offset = reader.read<uint32_t>();
67 entry.length = reader.read<uint32_t>();
68
69 pak->entries.emplace(entryPath, entry);
70
71 if (callback) {
72 callback(entryPath, entry);
73 }
74 }
75
76 return packFile;
77}
78
79std::optional<std::vector<std::byte>> PAK::readEntry(const std::string& path_) const {
80 const auto path = this->cleanEntryPath(path_);
81 const auto entry = this->findEntry(path);
82 if (!entry) {
83 return std::nullopt;
84 }
85 if (entry->unbaked) {
86 return readUnbakedEntry(*entry);
87 }
88
89 // It's baked into the file on disk
90 FileStream stream{this->fullFilePath};
91 if (!stream) {
92 return std::nullopt;
93 }
94 stream.seek_in_u(entry->offset);
95 return stream.read_bytes(entry->length);
96}
97
98void PAK::addEntryInternal(Entry& entry, const std::string& path, std::vector<std::byte>& buffer, EntryOptions options) {
99 entry.length = buffer.size();
100
101 // Offset will be reset when it's baked
102 entry.offset = 0;
103}
104
105bool PAK::bake(const std::string& outputDir_, BakeOptions options, const EntryCallback& callback) {
106 // Get the proper file output folder
107 const std::string outputDir = this->getBakeOutputDir(outputDir_);
108 const std::string outputPath = outputDir + '/' + this->getFilename();
109
110 // Reconstruct data for ease of access
111 std::vector<std::pair<std::string, Entry*>> entriesToBake;
112 this->runForAllEntriesInternal([&entriesToBake](const std::string& path, Entry& entry) {
113 entriesToBake.emplace_back(path, &entry);
114 });
115
116 // Read data before overwriting, we don't know if we're writing to ourself
117 std::vector<std::byte> fileData;
118 for (auto& [path, entry] : entriesToBake) {
119 if (auto binData = this->readEntry(path)) {
120 entry->offset = fileData.size();
121
122 fileData.insert(fileData.end(), binData->begin(), binData->end());
123 } else {
124 entry->offset = 0;
125 entry->length = 0;
126 }
127 }
128
129 {
130 FileStream stream{outputPath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
131 stream.seek_out(0);
132
133 // Signature
134 stream.write<uint32_t>(this->getSignature());
135
136 // Offset and size of directory
137 static constexpr auto HEADER_OFFSET = sizeof(uint32_t) * 3;
138 stream
139 .write<uint32_t>(HEADER_OFFSET + fileData.size())
140 .write<uint32_t>(entriesToBake.size() * (sizeof(uint32_t) * 2 + this->getFilenameLength()));
141
142 // File data
143 stream.write(fileData);
144
145 // Directory
146 for (const auto& [path, entry] : entriesToBake) {
147 stream
148 .write(path, true, this->getFilenameLength())
149 .write<uint32_t>(entry->offset + HEADER_OFFSET)
150 .write<uint32_t>(entry->length);
151
152 if (callback) {
153 callback(path, *entry);
154 }
155 }
156 }
157
158 // Clean up
159 this->mergeUnbakedEntries();
160 PackFile::setFullFilePath(outputDir);
161 return true;
162}
163
165 using enum Attribute;
166 return LENGTH;
167}
168
170 return this->type;
171}
172
173void PAK::setType(Type type_) {
174 this->type = type_;
175}
176
177uint32_t PAK::getSignature() const {
178 switch (this->type) {
179 case Type::PAK:
180 return PAK_SIGNATURE;
181 case Type::SIN:
182 return PAK_SIN_SIGNATURE;
183 case Type::HROT:
184 return PAK_HROT_SIGNATURE;
185 }
186 return PAK_SIGNATURE;
187}
188
189uint8_t PAK::getFilenameLength() const {
190 switch (this->type) {
191 case Type::PAK:
193 case Type::SIN:
195 case Type::HROT:
197 }
199}
This class represents the metadata that a file has inside a PackFile.
Definition Entry.h:14
uint64_t offset
Offset, format-specific meaning - 0 if unused, or if the offset genuinely is 0.
Definition Entry.h:33
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length)
Definition Entry.h:26
static std::unique_ptr< PackFile > create(const std::string &path, Type type=Type::PAK)
Create a PAK file.
Definition PAK.cpp:12
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a PAK file.
Definition PAK.cpp:33
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
Definition PAK.cpp:79
Type getType() const
Definition PAK.cpp:169
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition PAK.cpp:164
uint8_t getFilenameLength() const
Definition PAK.cpp:189
uint32_t getSignature() const
Definition PAK.cpp:177
Type type
Definition PAK.h:62
void addEntryInternal(Entry &entry, const std::string &path, std::vector< std::byte > &buffer, EntryOptions options) override
Definition PAK.cpp:98
void setType(Type type_)
Definition PAK.cpp:173
bool bake(const std::string &outputDir_, BakeOptions options, const EntryCallback &callback) override
If output folder is an empty string, it will overwrite the original.
Definition PAK.cpp:105
EntryCallbackBase< void > EntryCallback
Definition PackFile.h:38
void mergeUnbakedEntries()
Definition PackFile.cpp:658
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
std::string fullFilePath
Definition PackFile.h:232
void runForAllEntriesInternal(const std::function< void(const std::string &, Entry &)> &operation, bool includeUnbaked=true)
Definition PackFile.cpp:544
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
void setFullFilePath(const std::string &outputDir)
Definition PackFile.cpp:674
std::string cleanEntryPath(const std::string &path) const
Definition PackFile.cpp:679
static Entry createNewEntry()
Definition PackFile.cpp:688
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition PackFile.cpp:692
constexpr auto PAK_HROT_SIGNATURE
Definition PAK.h:18
constexpr auto PAK_SIN_SIGNATURE
Definition PAK.h:15
constexpr auto PAK_SIGNATURE
Definition PAK.h:12
Attribute
Definition Attribute.h:7
constexpr uint8_t PAK_SIN_FILENAME_MAX_SIZE
Definition PAK.h:14
constexpr uint8_t PAK_HROT_FILENAME_MAX_SIZE
Definition PAK.h:17
constexpr uint8_t PAK_FILENAME_MAX_SIZE
Definition PAK.h:11