SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
REZ.cpp
Go to the documentation of this file.
1#include <vpkpp/format/REZ.h>
2
3#include <filesystem>
4
5#include <FileStream.h>
6
7using namespace sourcepp;
8using namespace vpkpp;
9
10std::unique_ptr<PackFile> REZ::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* rez = new REZ{path};
17 auto packFile = std::unique_ptr<PackFile>(rez);
18
19 FileStream reader{rez->fullFilePath};
20 reader.seek_in(0);
21
22 if (const auto c = reader.read<uint8_t>(); c != '\r' && c != '&') {
23 return nullptr;
24 }
25 if (const auto c = reader.read<uint8_t>(); c != '\n' && c != '#') {
26 return nullptr;
27 }
28 reader.read(rez->fileType, 60, false);
29 string::rtrim(rez->fileType);
30
31 if (const auto c = reader.read<uint8_t>(); c != '\r' && c != '!') {
32 return nullptr;
33 }
34 if (const auto c = reader.read<uint8_t>(); c != '\n' && c != '\"') {
35 return nullptr;
36 }
37 reader.read(rez->userTitle, 60, false);
38 string::rtrim(rez->userTitle);
39
40 if (const auto c = reader.read<uint8_t>(); c != '\r' && c != '%') {
41 return nullptr;
42 }
43 if (const auto c = reader.read<uint8_t>(); c != '\n' && c != '\'') {
44 return nullptr;
45 }
46 rez->version = reader.read<uint8_t>();
47
48 switch (rez->version) {
49 case 0x1A: // EOF (1)
50 reader.read(rez->version);
51 if (rez->version != 1) {
52 reader.skip_in(7).read(rez->version);
53 if (rez->version != 2) {
54 return nullptr;
55 }
56 }
57 break;
58 case 0x2A: { // EOF (2)
59 reader.skip_in(68).read(rez->version);
60 if (rez->version != 1) {
61 return nullptr;
62 }
63 }
64 default:
65 return nullptr;
66 }
67
68 const auto rootDirOffset = reader.read<uint32_t>();
69 const auto rootDirSize = reader.read<uint32_t>();
70 reader.skip_in<uint32_t>(); // rootDirTime
71 reader.skip_in<uint32_t>(); // nextWriteOffset
72 reader.skip_in<uint32_t>(); // time
73 reader.skip_in<uint32_t>(); // largestKeyArray
74 reader.skip_in<uint32_t>(); // largestDirNameSize
75 reader.skip_in<uint32_t>(); // largestFileNameSize
76 reader.skip_in<uint32_t>(); // largestCommentSize
77 reader.skip_in<uint8_t>(); // isSorted
78
79 // Read the file tree recursively
80 std::function<bool(const std::string&, std::span<const std::byte>)> readBlock;
81 readBlock = [&callback, &rez, &reader, &readBlock](const std::string& parentPath, std::span<const std::byte> blockData) {
82 BufferStreamReadOnly blockStream{blockData.data(), blockData.size()};
83
84 while (blockStream.tell() != blockStream.size()) {
85 const auto blockType = blockStream.read<uint32_t>();
86 const auto blockOffset = blockStream.read<uint32_t>();
87 const auto blockLength = blockStream.read<uint32_t>();
88 blockStream.skip<uint32_t>(); // blockTime
89
90 if (blockType == 0) {
91 // File
92 Entry entry = createNewEntry();
93
94 //const auto resourceID = blockStream.read<uint32_t>();
95 blockStream.skip<uint32_t>();
96 auto resourceExtension = blockStream.read_string(4);
97 const auto resourceKeyCount = blockStream.read<uint32_t>();
98 const auto resourceName = blockStream.read_string();
99 const auto resourceDesc = blockStream.read_string();
100 blockStream.skip<uint32_t>(resourceKeyCount);
101
102 auto entryPath = parentPath;
103 entryPath += '/' + resourceName;
104 if (!resourceExtension.empty()) {
105 std::ranges::reverse(resourceExtension);
106 entryPath += '.' + resourceExtension;
107 }
108 entryPath = rez->cleanEntryPath(entryPath);
109
110 entry.length = blockLength;
111 entry.offset = blockOffset;
112
113 rez->entries.emplace(entryPath, entry);
114
115 if (callback) {
116 callback(entryPath, entry);
117 }
118 } else if (blockType == 1) {
119 // Directory
120 const auto dirName = blockStream.read_string();
121 if (blockLength) {
122 std::string newParentPath;
123 if (!parentPath.empty()) {
124 newParentPath = parentPath;
125 newParentPath += '/' + dirName;
126 } else {
127 newParentPath = dirName;
128 }
129 readBlock(newParentPath, reader.seek_in(blockOffset).read_bytes(blockLength));
130 }
131 } else {
132 // Invalid
133 return false;
134 }
135 }
136 return true;
137 };
138 if (!readBlock("", reader.seek_in(rootDirOffset).read_bytes(rootDirSize))) {
139 return nullptr;
140 }
141
142 return packFile;
143}
144
145std::optional<std::vector<std::byte>> REZ::readEntry(const std::string& path_) const {
146 const auto path = this->cleanEntryPath(path_);
147 const auto entry = this->findEntry(path);
148 if (!entry) {
149 return std::nullopt;
150 }
151 if (entry->unbaked) {
152 return readUnbakedEntry(*entry);
153 }
154
155 // It's baked into the file on disk
156 FileStream stream{this->fullFilePath};
157 if (!stream) {
158 return std::nullopt;
159 }
160 stream.seek_in_u(entry->offset);
161 return stream.read_bytes(entry->length);
162}
163
165 using enum Attribute;
166 return LENGTH;
167}
168
169std::string_view REZ::getFileType() const {
170 return this->fileType;
171}
172
173std::string_view REZ::getUserTitle() const {
174 return this->userTitle;
175}
176
177uint32_t REZ::getVersion() const {
178 return this->version;
179}
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
EntryCallbackBase< void > EntryCallback
Definition PackFile.h:38
std::optional< Entry > findEntry(const std::string &path_, bool includeUnbaked=true) const
Try to find an entry given the file path.
Definition PackFile.cpp:172
std::string fullFilePath
Definition PackFile.h:231
std::string cleanEntryPath(const std::string &path) const
Definition PackFile.cpp:706
static Entry createNewEntry()
Definition PackFile.cpp:715
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
Definition PackFile.cpp:719
std::string userTitle
Definition REZ.h:30
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
Definition REZ.cpp:164
std::string_view getFileType() const
Definition REZ.cpp:169
uint32_t getVersion() const
Definition REZ.cpp:177
std::string_view getUserTitle() const
Definition REZ.cpp:173
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
Definition REZ.cpp:145
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
Open a REZ file.
Definition REZ.cpp:10
std::string fileType
Definition REZ.h:29
uint32_t version
Definition REZ.h:31
void rtrim(std::string &s)
Definition String.cpp:80
Attribute
Definition Attribute.h:7