79 if (!std::filesystem::exists(path)) {
84 auto* pak =
new PAK{path};
85 auto packFile = std::unique_ptr<PackFile>(pak);
87 FileStream reader{pak->fullFilePath};
91 const auto pakFileLength = std::filesystem::file_size(path);
93 if (
const auto signature = reader.read<uint32_t>(); signature ==
PAK_SIGNATURE) {
107 reader.skip_in<int32_t>();
108 const auto directoryOffset = reader.read<uint64_t>();
109 const auto namesOffset = reader.read<uint64_t>();
110 const auto fileCount = reader.read<uint32_t>();
111 const auto namesLength = reader.read<uint32_t>();
114 std::string namesBuffer;
115 reader.seek_in_u(namesOffset);
116 reader.read(namesBuffer, namesLength,
false);
119 reader.seek_in_u(directoryOffset);
120 for (uint32_t i = 0; i < fileCount; i++) {
123 const auto filePos = reader.read<uint64_t>();
124 const auto fileLen = reader.read<uint32_t>();
125 const auto nameOfs = reader.read<uint32_t>();
127 auto entryPath = pak->cleanEntryPath(namesBuffer.c_str() + nameOfs);
132 pak->entries.emplace(entryPath, entry);
135 callback(entryPath, entry);
140 const auto directoryOffset = reader.read<uint32_t>();
141 const auto directorySize = reader.read<uint32_t>();
144 bool isDaikatana = directorySize % 64 != 0;
145 const auto entrySize =
sizeof(uint32_t) * 2 + pak->getFilenameLength();
146 const auto dkEntrySize =
sizeof(uint32_t) * 4 + pak->getFilenameLength();
149 if (directorySize % 576 == 0) {
150 const auto numEntries = directorySize / dkEntrySize;
152 for (
size_t i = 0; i < numEntries; i++) {
154 auto entryPath = pak->cleanEntryPath(reader.read_string(pak->getFilenameLength()));
157 for (j = 0; j < pak->getFilenameLength(); j++) {
167 const auto offset = reader.read<uint32_t>();
168 auto length = reader.read<uint32_t>();
171 const auto compressedLength = reader.read<uint32_t>();
172 const auto compressedType = reader.read<uint32_t>();
173 if (compressedType != 0 && compressedType != 1) {
179 if (compressedType == 1) {
180 length = compressedLength;
182 if (offset + length > pakFileLength) {
195 const auto fileCount = directorySize / (isDaikatana ? dkEntrySize : entrySize);
197 reader.seek_in(directoryOffset);
198 for (uint32_t i = 0; i < fileCount; i++) {
201 auto entryPath = pak->cleanEntryPath(reader.read_string(pak->getFilenameLength()));
203 entry.
offset = reader.read<uint32_t>();
204 entry.
length = reader.read<uint32_t>();
208 const auto compressedLength = reader.read<uint32_t>();
210 const auto compressedType = reader.read<uint32_t>();
217 pak->entries.emplace(entryPath, entry);
220 callback(entryPath, entry);
268 const std::string outputPath = outputDir +
'/' + this->
getFilename();
271 std::vector<std::pair<std::string, Entry*>> entriesToBake;
273 entriesToBake.emplace_back(path, &entry);
277 std::vector<std::byte> fileData;
278 std::vector<std::byte> nameData;
279 std::vector<uint32_t> nameOffsets;
280 for (
auto& [path, entry] : entriesToBake) {
281 if (
auto binData = this->
readEntry(path)) {
282 entry->
offset = fileData.size();
284 fileData.insert(fileData.end(), binData->begin(), binData->end());
287 const auto oldSize = nameData.size();
288 nameData.resize(oldSize + path.size() + 1);
289 std::memcpy(&nameData[oldSize], path.data(), path.size());
290 nameOffsets.push_back(oldSize);
299 FileStream stream{outputPath, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
307 stream.write<int32_t>(0);
310 static constexpr auto HEADER_OFFSET =
sizeof(uint32_t) * 3 +
sizeof(uint64_t) * 2;
311 stream.write<uint64_t>(HEADER_OFFSET + fileData.size());
314 const auto numFiles = entriesToBake.size();
315 const auto directorySize = (
sizeof(uint64_t) +
sizeof(uint32_t) * 2) * numFiles;
316 stream.write<uint64_t>(HEADER_OFFSET + fileData.size() + directorySize);
319 stream.write<uint32_t>(numFiles);
322 stream.write<uint32_t>(nameData.size());
325 stream.write(fileData);
329 for (
const auto& [path, entry] : entriesToBake) {
331 .write<uint64_t>(entry->
offset + HEADER_OFFSET)
332 .write<uint32_t>(entry->
length)
333 .write<uint32_t>(nameOffsets[i++]);
336 callback(path, *entry);
341 stream.write(nameData);
344 static constexpr auto HEADER_OFFSET =
sizeof(uint32_t) * 3;
346 .write<uint32_t>(HEADER_OFFSET + fileData.size())
347 .write<uint32_t>(entriesToBake.size() * (
sizeof(uint32_t) * 2 + this->
getFilenameLength()));
350 stream.write(fileData);
353 for (
const auto& [path, entry] : entriesToBake) {
356 .write<uint32_t>(entry->
offset + HEADER_OFFSET)
357 .write<uint32_t>(entry->
length);
360 callback(path, *entry);
This class represents the metadata that a file has inside a PackFile.
uint64_t offset
Offset, format-specific meaning - 0 if unused, or if the offset genuinely is 0.
uint64_t compressedLength
If the format supports compression, this is the compressed length.
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length).