SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
VPP.cpp
Go to the documentation of this file.
1#include <vpkpp/format/VPP.h>
2
3#include <filesystem>
4
5#include <FileStream.h>
6
7#include <miniz.h>
8
9using namespace sourcepp;
10using namespace vpkpp;
11
12std::unique_ptr<PackFile> VPP::open(const std::string& path, const EntryCallback& callback) {
13 if (!std::filesystem::exists(path)) {
14 // File does not exist
15 return nullptr;
16 }
17
18 auto* vpp = new VPP{path};
19 auto packFile = std::unique_ptr<PackFile>(vpp);
20
21 FileStream reader{vpp->fullFilePath};
22 reader.seek_in(0);
23
24 // Verify signature
25 if (const auto signature = reader.read<uint32_t>(); signature == VPP_SIGNATURE_BIG) {
26 reader.set_big_endian(true);
27 } else if (signature != VPP_SIGNATURE_LIL) {
28 return nullptr;
29 }
30
31 // Create entries
32 const auto version = reader.read<uint32_t>();
33 if (version == 1) {
34 // Get number of entries
35 const auto entryCount = reader.read<uint32_t>();
36
37 // Verify file size
38 if (reader.read<uint32_t>() != std::filesystem::file_size(path)) {
39 return nullptr;
40 }
41
42 // Get file table offset
43 static constexpr uint32_t headerSize = sizeof(uint32_t) * 4;
44 reader.seek_in(headerSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, headerSize));
45
46 // Get base file offset
47 const uint32_t fileTableSize = (60 + sizeof(uint32_t)) * entryCount;
48 vpp->entryBaseOffset = headerSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, headerSize)
49 + fileTableSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, fileTableSize);
50
51 // Get first file offset
52 uint32_t entryOffset = 0;
53
54 // Read file entries
55 for (uint32_t i = 0; i < entryCount; i++) {
56 Entry entry = createNewEntry();
57
58 // Get file path
59 const auto entryPath = vpp->cleanEntryPath(reader.read_string(60));
60
61 // Get file size
62 entry.length = reader.read<uint32_t>();
63
64 // Calculate file offset
65 entry.offset = entryOffset;
67
68 // Put it in
69 vpp->entries.emplace(entryPath, entry);
70
71 if (callback) {
72 callback(entryPath, entry);
73 }
74 }
75 } else if (version == 2) {
76 // Get number of entries
77 const auto entryCount = reader.read<uint32_t>();
78
79 // Verify file size
80 if (reader.read<uint32_t>() != std::filesystem::file_size(path)) {
81 return nullptr;
82 }
83
84 // Get file table offset
85 static constexpr uint32_t headerSize = sizeof(uint32_t) * 4;
86 reader.seek_in(headerSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, headerSize));
87
88 // Get base file offset
89 const uint32_t fileTableSize = (24 + sizeof(uint32_t) * 2) * entryCount;
90 vpp->entryBaseOffset = headerSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, headerSize)
91 + fileTableSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, fileTableSize);
92
93 // Get first file offset
94 uint32_t entryOffset = 0;
95
96 // Read file entries
97 for (uint32_t i = 0; i < entryCount; i++) {
98 Entry entry = createNewEntry();
99
100 // Get file path
101 const auto entryPath = vpp->cleanEntryPath(reader.read_string(24));
102
103 // Get file size
104 entry.length = reader.read<uint32_t>();
105
106 // There's a duplicate size field here
107 // Probably one means compressed, the other uncompressed
108 // Just bail out for now
109 if (reader.read<uint32_t>() != entry.length) {
110 return nullptr;
111 }
112
113 // Calculate file offset
114 entry.offset = entryOffset;
115 entryOffset += entry.length + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, entry.length);
116
117 // Put it in
118 vpp->entries.emplace(entryPath, entry);
119
120 if (callback) {
121 callback(entryPath, entry);
122 }
123 }
124 } else if (version == 3) {
125 // Skip unused header data
126 reader.skip_in(64 + 256 + sizeof(uint32_t));
127
128 // Get package flags
129 reader >> vpp->flags;
130
131 // Remove compressed flag if we're also condensed
132 if (vpp->flags & FLAG_CONDENSED) {
133 vpp->flags &= ~FLAG_COMPRESSED;
134 }
135
136 // ??
137 reader.skip_in<uint32_t>();
138
139 // Get number of entries
140 const auto entryCount = reader.read<uint32_t>();
141
142 // Verify file size
143 if (reader.read<uint32_t>() != std::filesystem::file_size(path)) {
144 return nullptr;
145 }
146
147 // Get sizes
148 const auto entryDirectorySize = reader.read<uint32_t>();
149 const auto entryNamesSize = reader.read<uint32_t>();
150
151 // Check if we have compression
152 const auto entryDataSizeUncompressed = reader.read<uint32_t>();
153 const auto entryDataSizeCompressed = reader.read<uint32_t>();
154
155 // Set base data offset
156 vpp->entryBaseOffset = VPP_ALIGNMENT
157 + entryDirectorySize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, entryDirectorySize)
158 + entryNamesSize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, entryNamesSize);
159
160 // Seek to file directory (alignment boundary)
161 reader.seek_in(VPP_ALIGNMENT);
162
163 // Read file entries
164 for (uint32_t i = 0; i < entryCount; i++) {
165 Entry entry = createNewEntry();
166
167 // Get file name offset
168 const auto entryNameOffset = reader.read<uint32_t>();
169
170 // ??
171 reader.skip_in<uint32_t>();
172
173 // Get file offset
174 entry.offset = reader.read<uint32_t>();
175
176 // ??
177 reader.skip_in<uint32_t>();
178
179 // Get file size
180 entry.length = reader.read<uint32_t>();
181 entry.compressedLength = reader.read<uint32_t>();
182
183 // Not compressed
184 if (entry.compressedLength == 0xFFFFFFFF || !(vpp->flags & FLAG_COMPRESSED)) {
185 entry.compressedLength = 0;
186 }
187
188 // ??
189 reader.skip_in<uint32_t>();
190
191 // Get file name
192 const auto lastPos = reader.tell_in();
193 reader.seek_in(VPP_ALIGNMENT + entryDirectorySize + sourcepp::math::paddingForAlignment(VPP_ALIGNMENT, entryDirectorySize) + entryNameOffset);
194 const auto entryPath = vpp->cleanEntryPath(reader.read_string(entryNamesSize - entryNameOffset));
195 reader.seek_in_u(lastPos);
196
197 // Put it in
198 vpp->entries.emplace(entryPath, entry);
199
200 if (callback) {
201 callback(entryPath, entry);
202 }
203 }
204
205 // Uncondense data
206 if (vpp->flags & FLAG_CONDENSED) {
207 reader.seek_in(vpp->entryBaseOffset);
208 vpp->uncondensedData.resize(entryDataSizeUncompressed);
209 auto compressedData = reader.read_bytes(entryDataSizeCompressed);
210 mz_ulong uncompressedLength = entryDataSizeUncompressed;
211 mz_uncompress(reinterpret_cast<unsigned char*>(vpp->uncondensedData.data()), &uncompressedLength, reinterpret_cast<const unsigned char*>(compressedData.data()), entryDataSizeCompressed);
212 }
213 } else {
214 return nullptr;
215 }
216
217 return packFile;
218}
219
220std::optional<std::vector<std::byte>> VPP::readEntry(const std::string& path_) const {
221 auto path = this->cleanEntryPath(path_);
222 auto entry = this->findEntry(path);
223 if (!entry) {
224 return std::nullopt;
225 }
226 if (entry->unbaked) {
227 return readUnbakedEntry(*entry);
228 }
229
230 if (this->flags & FLAG_CONDENSED) {
231 // Condensed entry
232 BufferStreamReadOnly stream{this->uncondensedData.data(), this->uncondensedData.size()};
233 stream.seek_u(entry->offset);
234 return stream.read_bytes(entry->length);
235 } else if (this->flags & FLAG_COMPRESSED) {
236 // Compressed entry
237 if (!entry->compressedLength) {
238 return std::nullopt;
239 }
240 FileStream stream{this->fullFilePath};
241 if (!stream) {
242 return std::nullopt;
243 }
244 stream.seek_in_u(this->entryBaseOffset + entry->offset);
245 auto compressedData = stream.read_bytes(entry->compressedLength);
246 mz_ulong uncompressedLength = entry->length;
247 std::vector<std::byte> uncompressedData(uncompressedLength);
248 mz_uncompress(reinterpret_cast<unsigned char*>(uncompressedData.data()), &uncompressedLength, reinterpret_cast<const unsigned char*>(compressedData.data()), entry->compressedLength);
249 return uncompressedData;
250 } else {
251 // Uncompressed entry
252 FileStream stream{this->fullFilePath};
253 if (!stream) {
254 return std::nullopt;
255 }
256 stream.seek_in_u(this->entryBaseOffset + entry->offset);
257 return stream.read_bytes(entry->length);
258 }
259}
260
262 using enum Attribute;
263 return LENGTH;
264}
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 compressedLength
If the format supports compression, this is the compressed length.
Definition: Entry.h:30
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length)
Definition: Entry.h:26
EntryCallbackBase< void > EntryCallback
Definition: PackFile.h:30
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
std::string fullFilePath
Definition: PackFile.h:219
std::string cleanEntryPath(const std::string &path) const
Definition: PackFile.cpp:677
static Entry createNewEntry()
Definition: PackFile.cpp:686
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition: PackFile.cpp:690
Definition: VPP.h:14
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a VPP file.
Definition: VPP.cpp:12
Flags flags
Definition: VPP.h:38
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition: VPP.cpp:261
@ FLAG_COMPRESSED
Definition: VPP.h:18
@ FLAG_CONDENSED
Definition: VPP.h:19
std::vector< std::byte > uncondensedData
Definition: VPP.h:40
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
Definition: VPP.cpp:220
uint32_t entryBaseOffset
Definition: VPP.h:39
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
Definition: Math.h:57
Definition: LZMA.h:11
Definition: Attribute.h:5
constexpr uint32_t VPP_ALIGNMENT
Definition: VPP.h:9
constexpr uint32_t VPP_SIGNATURE_BIG
Definition: VPP.h:8
Attribute
Definition: Attribute.h:7
constexpr uint32_t VPP_SIGNATURE_LIL
Definition: VPP.h:7