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