SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
XZP.cpp
Go to the documentation of this file.
1#include <vpkpp/format/XZP.h>
2
3#include <filesystem>
4
5#include <FileStream.h>
6
7using namespace sourcepp;
8using namespace vpkpp;
9
10std::unique_ptr<PackFile> XZP::open(const std::string& path, const EntryCallback& callback) {
11 if (!std::filesystem::exists(path)) {
12 // File does not exist
13 return nullptr;
14 }
15
16 auto* xzp = new XZP{path};
17 auto packFile = std::unique_ptr<PackFile>(xzp);
18
19 FileStream reader{xzp->fullFilePath};
20 reader.seek_in(0);
21
22 if (reader.read<uint32_t>() != XZP_HEADER_SIGNATURE) {
23 // File is not an XZP
24 return nullptr;
25 }
26
27 if (reader.read<uint32_t>() != 6) {
28 // Invalid version - check around for earlier formats eventually
29 return nullptr;
30 }
31
32 const auto preloadDirectoryEntryCount = reader.read<uint32_t>();
33 const auto directoryEntryCount = reader.read<uint32_t>();
34 reader.skip_in<uint32_t>(); // preloadBytes
35
36 if (reader.read<uint32_t>() != sizeof(uint32_t) * 9) {
37 // Header size - should always be 9 uints for v6
38 return nullptr;
39 }
40
41 if (const auto filepathEntryCount = reader.read<uint32_t>(); filepathEntryCount != directoryEntryCount) {
42 // We can't reverse a hash! Just bail
43 return nullptr;
44 }
45
46 const auto filepathStringsOffset = reader.read<uint32_t>();
47 reader.skip_in<uint32_t>(); // filepathStringsLength
48
49 // Add directory entries
50 std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint32_t>>> stagedEntryChunks;
51 for (uint32_t i = 0; i < directoryEntryCount; i++) {
52 const auto filepathCRC = reader.read<uint32_t>();
53 const auto chunkLength = reader.read<uint32_t>();
54 const auto chunkOffset = reader.read<uint32_t>();
55
56 if (!stagedEntryChunks.contains(filepathCRC)) {
57 stagedEntryChunks[filepathCRC] = {};
58 stagedEntryChunks[filepathCRC].emplace_back(chunkOffset, chunkLength);
59 } else if (stagedEntryChunks[filepathCRC].back().first + stagedEntryChunks[filepathCRC].back().second == chunkOffset) {
60 stagedEntryChunks[filepathCRC].back().second += chunkLength;
61 } else {
62 stagedEntryChunks[filepathCRC].emplace_back(chunkOffset, chunkLength);
63 }
64 }
65
66 // Add preload entries
67 std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> stagedEntryPreloads;
68 for (uint32_t i = 0; i < preloadDirectoryEntryCount; i++) {
69 const auto filepathCRC = reader.read<uint32_t>();
70 const auto preloadLength = reader.read<uint32_t>();
71 const auto preloadOffset = reader.read<uint32_t>();
72
73 stagedEntryPreloads[filepathCRC] = {preloadLength, preloadOffset};
74 }
75
76 // Preload size per entry
77 reader.skip_in<uint16_t>(directoryEntryCount);
78
79 // filepaths, and put it all together simultaneously
80 reader.seek_in(filepathStringsOffset);
81 std::unordered_map<uint32_t, std::string> stagedEntryFilepaths;
82 for (uint32_t i = 0; i < directoryEntryCount; i++) {
83 const auto filepathCRC = reader.read<uint32_t>();
84 const auto filepathOffset = reader.read<uint32_t>();
85 reader.skip_in<uint32_t>(); // timestamp
86
87 const auto readerPos = reader.tell_in();
88 reader.seek_in_u(filepathOffset);
89 stagedEntryFilepaths[filepathCRC] = reader.read_string();
90 reader.seek_in_u(readerPos);
91 }
92
93 // Put it all together
94 for (const auto& [filepathCRC, filepath] : stagedEntryFilepaths) {
95 Entry entry = createNewEntry();
96
97 auto entryPath = xzp->cleanEntryPath(filepath);
98
99 BufferStream stream{entry.extraData};
100
101 const auto& chunks = stagedEntryChunks[filepathCRC];
102 stream.write<uint32_t>(chunks.size());
103
104 entry.length = 0;
105 for (const auto& chunk : chunks) {
106 entry.length += chunk.second;
107 stream << chunk.first << chunk.second;
108 }
109
110 if (stagedEntryPreloads.contains(filepathCRC)) {
111 const auto& preload = stagedEntryPreloads[filepathCRC];
112 stream.write<uint32_t>(true);
113 stream << preload.first << preload.second;
114 } else {
115 stream.write<uint32_t>(false);
116 }
117
118 xzp->entries.emplace(entryPath, entry);
119
120 if (callback) {
121 callback(entryPath, entry);
122 }
123 }
124
125 return packFile;
126}
127
128std::optional<std::vector<std::byte>> XZP::readEntry(const std::string& path_) const {
129 auto path = this->cleanEntryPath(path_);
130 auto entry = this->findEntry(path);
131 if (!entry) {
132 return std::nullopt;
133 }
134 if (entry->unbaked) {
135 return readUnbakedEntry(*entry);
136 }
137
138 // It's baked into the file on disk
139 FileStream stream{this->fullFilePath};
140 if (!stream) {
141 return std::nullopt;
142 }
143 std::vector<std::byte> out;
144 BufferStreamReadOnly entryDataStream{entry->extraData};
145 const auto chunks = entryDataStream.read<uint32_t>();
146 for (uint32_t i = 0; i < chunks; i++) {
147 const auto chunkOffset = entryDataStream.read<uint32_t>();
148 const auto chunkLength = entryDataStream.read<uint32_t>();
149 const auto chunkData = stream.seek_in(chunkOffset).read_bytes(chunkLength);
150 out.insert(out.end(), chunkData.begin(), chunkData.end());
151 }
152 return out;
153}
154
156 using enum Attribute;
157 return LENGTH;
158}
This class represents the metadata that a file has inside a PackFile.
Definition: Entry.h:14
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length)
Definition: Entry.h:26
std::vector< std::byte > extraData
Format-specific (PCK: MD5 hash, VPK: Preloaded data)
Definition: Entry.h:36
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: XZP.h:13
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition: XZP.cpp:155
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open an XZP file.
Definition: XZP.cpp:10
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
Definition: XZP.cpp:128
Definition: LZMA.h:11
Definition: Attribute.h:5
Attribute
Definition: Attribute.h:7
constexpr auto XZP_HEADER_SIGNATURE
Definition: XZP.h:9