16 if (!std::filesystem::exists(path)) {
21 auto* gcf =
new GCF{path};
22 auto packFile = std::unique_ptr<PackFile>(gcf);
24 FileStream reader(gcf->fullFilePath);
28 reader.read(gcf->header);
29 if (gcf->header.dummy1 != 1 && gcf->header.dummy2 != 1 && gcf->header.gcfversion != 6) {
39 if (gcf->header.filesize != std::filesystem::file_size(gcf->fullFilePath)) {
44 reader.read(gcf->blockheader);
45 if (gcf->blockheader.count != gcf->header.blockcount) {
52 if (gcf->blockheader.count +
53 gcf->blockheader.used +
54 gcf->blockheader.dummy1 +
55 gcf->blockheader.dummy2 +
56 gcf->blockheader.dummy3 +
57 gcf->blockheader.dummy4 +
58 gcf->blockheader.dummy5 != gcf->blockheader.checksum) {
63 for (
int i = 0; i < gcf->header.blockcount; i++) {
64 Block& block = gcf->blockdata.emplace_back();
80 auto blkcount = reader.read<uint32_t>();
81 auto d1 = reader.read<uint32_t>();
82 auto d2 = reader.read<uint32_t>();
83 auto checksum = reader.read<uint32_t>();
85 if (blkcount + d1 + d2 != checksum || blkcount != gcf->blockheader.count) {
91 for (
int i = 0; i < blkcount; i++) {
92 gcf->fragmap.push_back(reader.read<uint32_t>());
98 uint64_t temp = reader.tell_in();
99 reader.read(gcf->dirheader);
101 uint64_t diroffset = reader.tell_in() + (gcf->dirheader.itemcount * 28);
103 std::vector<DirectoryEntry2> direntries{};
105 for (
int i = 0; i < gcf->dirheader.itemcount; i++) {
108 auto currentoffset = reader.tell_in();
116 dirname.insert(0,
"/");
117 dirname.insert(0, current_filename_entry.
filename);
120 auto gcfEntryPath = gcf->cleanEntryPath(dirname);
121 if (!gcfEntryPath.empty()) {
131 gcf->entries.emplace(gcfEntryPath, gcfEntry);
134 callback(gcfEntryPath, gcfEntry);
137 reader.seek_in_u(currentoffset);
143 reader.seek_in_u(temp + gcf->dirheader.dirsize);
149 for (
int i = 0; i < gcf->dirheader.itemcount; i++) {
156 reader.skip_in<uint32_t>();
157 auto checksumsize = reader.read<uint32_t>();
158 std::size_t checksums_start = reader.tell_in();
165 if (chksummapheader.dummy1 != 0x14893721 || chksummapheader.dummy2 != 0x1) {
171 for (
int i = 0; i < chksummapheader.item_count; i++) {
172 auto& cur_entry = gcf->chksum_map.emplace_back();
173 reader.read(cur_entry);
176 for (
int i = 0; i < chksummapheader.checksum_count; i++) {
177 auto& currentChecksum = gcf->checksums.emplace_back();
178 reader.read(currentChecksum);
183 reader.seek_in_u(checksums_start + checksumsize);
185 reader.read(gcf->datablockheader);
190 std::vector<std::string> bad;
193 if (!bytes || bytes->empty()) {
196 std::size_t tocheck = bytes->size();
197 uint32_t idx = entry.
crc32;
199 uint32_t checksumstart = this->
chksum_map[idx].firstindex;
200 for (
int i = 0; i < count; i++) {
201 uint32_t csum = this->
checksums[checksumstart + i];
202 std::size_t toread = std::min(
static_cast<std::size_t
>(0x8000), tocheck);
203 const auto* data = bytes->data() + (i * 0x8000);
205 if (checksum != csum) {
214std::optional<std::vector<std::byte>>
GCF::readEntry(
const std::string& path_)
const {
220 if (entry->unbaked) {
224 std::vector<std::byte> filedata;
225 if (entry->length == 0) {
230 uint32_t dir_index = entry->offset;
233 std::vector<Block> toread;
235 if (v.dir_index == dir_index) {
240 if (toread.empty()) {
244 std::sort(toread.begin(), toread.end(), [](
const Block& lhs,
const Block& rhs) {
245 return lhs.file_data_offset < rhs.file_data_offset;
253 uint64_t remaining = entry->length;
255 for (
const auto& block : toread) {
256 uint32_t currindex = block.first_data_block_index;
258 uint64_t curfilepos =
static_cast<uint64_t
>(this->
datablockheader.
firstblockoffset) + (
static_cast<std::uint64_t
>(0x2000) *
static_cast<std::uint64_t
>(currindex));
259 stream.seek_in_u(curfilepos);
261 uint32_t toreadAmt = std::min(remaining,
static_cast<uint64_t
>(0x2000));
262 auto streamvec = stream.read_bytes(toreadAmt);
263 filedata.insert(filedata.end(), streamvec.begin(), streamvec.end());
264 remaining -= toreadAmt;
265 currindex = this->
fragmap[currindex];
278GCF::operator std::string()
const {
279 return PackFileReadOnly::operator std::string() +
280 " | Version v" + std::to_string(this->header.gcfversion) +
281 " | AppID " + std::to_string(this->header.appid) +
282 " | App Version v" + std::to_string(this->header.appversion);
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)
std::vector< Block > blockdata
std::vector< ChecksumMapEntry > chksum_map
std::vector< std::string > verifyEntryChecksums() const override
Verify the checksums of each file, if a file fails the check its path will be added to the vector If ...
DataBlockHeader datablockheader
Attribute getSupportedEntryAttributes() const override
Returns a list of supported entry attributes Mostly for GUI programs that show entries and their meta...
std::vector< uint32_t > checksums
std::optional< std::vector< std::byte > > readEntry(const std::string &path_) const override
Try to read the entry's data to a bytebuffer.
std::vector< uint32_t > fragmap
static std::unique_ptr< PackFile > open(const std::string &path, const EntryCallback &callback=nullptr)
EntryCallbackBase< void > EntryCallback
std::optional< Entry > findEntry(const std::string &path_, bool includeUnbaked=true) const
Try to find an entry given the file path.
void runForAllEntries(const EntryCallback &operation, bool includeUnbaked=true) const
Run a callback for each entry in the pack file.
std::string cleanEntryPath(const std::string &path) const
static Entry createNewEntry()
static std::optional< std::vector< std::byte > > readUnbakedEntry(const Entry &entry)
uint32_t computeAdler32(std::span< const std::byte > buffer)
uint32_t computeCRC32(std::span< const std::byte > buffer)
DirectoryEntry entry_real