19 if (!std::filesystem::exists(path)) {
24 auto* gcf =
new GCF{path};
25 auto packFile = std::unique_ptr<PackFile>(gcf);
27 FileStream reader(gcf->fullFilePath);
31 reader.read(gcf->header);
32 if (gcf->header.dummy1 != 1 && gcf->header.dummy2 != 1 && gcf->header.gcfversion != 6) {
42 if (gcf->header.filesize != std::filesystem::file_size(gcf->fullFilePath)) {
47 reader.read(gcf->blockheader);
48 if (gcf->blockheader.count != gcf->header.blockcount) {
55 if (gcf->blockheader.count +
56 gcf->blockheader.used +
57 gcf->blockheader.dummy1 +
58 gcf->blockheader.dummy2 +
59 gcf->blockheader.dummy3 +
60 gcf->blockheader.dummy4 +
61 gcf->blockheader.dummy5 != gcf->blockheader.checksum) {
66 bool requestKey =
false;
67 for (
int i = 0; i < gcf->header.blockcount; i++) {
68 Block& block = gcf->blockdata.emplace_back();
76 if (key.size() != gcf->decryption_key.size()) {
79 std::ranges::copy(key, gcf->decryption_key.begin());
94 auto blkcount = reader.read<uint32_t>();
95 auto d1 = reader.read<uint32_t>();
96 auto d2 = reader.read<uint32_t>();
97 auto checksum = reader.read<uint32_t>();
99 if (blkcount + d1 + d2 != checksum || blkcount != gcf->blockheader.count) {
105 for (
int i = 0; i < blkcount; i++) {
106 gcf->fragmap.push_back(reader.read<uint32_t>());
112 uint64_t temp = reader.tell_in();
113 reader.read(gcf->dirheader);
115 uint64_t diroffset = reader.tell_in() + (gcf->dirheader.itemcount * 28);
117 std::vector<DirectoryEntry2> direntries{};
119 for (
int i = 0; i < gcf->dirheader.itemcount; i++) {
122 auto currentoffset = reader.tell_in();
130 dirname.insert(0,
"/");
131 dirname.insert(0, current_filename_entry.
filename);
134 auto gcfEntryPath = gcf->cleanEntryPath(dirname);
135 if (!gcfEntryPath.empty()) {
145 gcf->entries.emplace(gcfEntryPath, gcfEntry);
148 callback(gcfEntryPath, gcfEntry);
151 reader.seek_in_u(currentoffset);
157 reader.seek_in_u(temp + gcf->dirheader.dirsize);
163 for (
int i = 0; i < gcf->dirheader.itemcount; i++) {
170 reader.skip_in<uint32_t>();
171 auto checksumsize = reader.read<uint32_t>();
172 std::size_t checksums_start = reader.tell_in();
179 if (chksummapheader.dummy1 != 0x14893721 || chksummapheader.dummy2 != 0x1) {
185 for (
int i = 0; i < chksummapheader.item_count; i++) {
186 auto& cur_entry = gcf->chksum_map.emplace_back();
187 reader.read(cur_entry);
190 for (
int i = 0; i < chksummapheader.checksum_count; i++) {
191 auto& currentChecksum = gcf->checksums.emplace_back();
192 reader.read(currentChecksum);
197 reader.seek_in_u(checksums_start + checksumsize);
199 reader.read(gcf->datablockheader);
228std::optional<std::vector<std::byte>>
GCF::readEntry(
const std::string& path_)
const {
234 if (entry->unbaked) {
238 std::vector<std::byte> filedata;
239 if (entry->length == 0) {
244 uint32_t dir_index = entry->offset;
247 std::vector<Block> toread;
249 if (v.dir_index == dir_index && (v.flags & 0x8000)) {
254 if (toread.empty()) {
258 std::sort(toread.begin(), toread.end(), [](
const Block& lhs,
const Block& rhs) {
259 return lhs.file_data_offset < rhs.file_data_offset;
267 uint64_t remaining = entry->length;
268 bool needs_decrypt =
false;
270 for (
auto& block : toread) {
271 uint32_t currindex = block.first_data_block_index;
273 uint64_t curfilepos =
static_cast<uint64_t
>(this->
datablockheader.firstblockoffset) + (
static_cast<std::uint64_t
>(0x2000) *
static_cast<std::uint64_t
>(currindex));
274 stream.seek_in_u(curfilepos);
276 uint32_t toreadAmt = std::min(remaining,
static_cast<uint64_t
>(0x2000));
277 auto streamvec = stream.read_bytes(toreadAmt);
278 filedata.insert(filedata.end(), streamvec.begin(), streamvec.end());
279 remaining -= toreadAmt;
280 currindex = this->
fragmap[currindex];
283 needs_decrypt = block.isEncrypted();
284 filemode = block.getCompressionType();
293 auto real_size = filedata.size();
294 if (filedata.size() % 10 != 0) {
295 filedata.resize(filedata.size() + (0x10 - (filedata.size() % 10)), {});
297 auto remaining_encrypted = filedata.size();
299 while (remaining_encrypted) {
300 auto current_batch = std::min(remaining_encrypted,
static_cast<size_t>(0x8000));
302 remaining_encrypted -= current_batch;
303 offset +=
static_cast<int>(current_batch);
305 filedata.resize(real_size);
308 case COMPRESSED_AND_ENCRYPTED: {
309 std::vector<std::byte> processed_data;
310 BufferStream s(filedata.data(), filedata.size());
311 while (s.size() != s.tell()) {
312 static constexpr auto allocate_block = [](std::vector<std::byte>& vec,
size_t x) {
313 const size_t old = vec.size();
315 return vec.data() + old;
318 auto encrypted_size = s.read<uint32_t>();
319 mz_ulong decompressed_size = s.read<uint32_t>();
321 auto buffer = s.read_bytes(encrypted_size);
322 auto real_size = buffer.size();
323 if (buffer.size() % 10 != 0) {
324 buffer.resize(buffer.size() + (0x10 - (buffer.size() % 10)), {});
327 buffer.resize(real_size);
329 auto start = allocate_block(processed_data, decompressed_size);
330 if (mz_uncompress(
reinterpret_cast<uint8_t*
>(start), &decompressed_size,
reinterpret_cast<uint8_t*
>(buffer.data()), buffer.size()) != MZ_OK) {
335 if (entry->length == processed_data.size()) {
338 s.seek_u(s.tell() + (0x8000 - s.tell() % 0x8000));
340 filedata = processed_data;
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.
uint32_t crc32
CRC32 checksum - 0 if unused.
uint64_t length
Length in bytes (in formats with compression, this is the uncompressed length).