[PCK] Move directory to the end of file, write exported/saved PCK in place.

This commit is contained in:
Pāvels Nadtočajevs
2025-04-25 19:11:06 +03:00
parent 64b09905c7
commit 8cb2817976
6 changed files with 153 additions and 190 deletions

View File

@ -95,14 +95,28 @@ Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const Stri
file->store_32(GODOT_VERSION_MINOR);
file->store_32(GODOT_VERSION_PATCH);
uint32_t pack_flags = 0;
uint32_t pack_flags = PACK_REL_FILEBASE;
if (enc_dir) {
pack_flags |= PACK_DIR_ENCRYPTED;
}
file->store_32(pack_flags); // flags
file_base_ofs = file->get_position();
file->store_64(0); // Files base.
dir_base_ofs = file->get_position();
file->store_64(0); // Directory offset.
for (int i = 0; i < 16; i++) {
file->store_32(0); // Reserved.
}
file_base = file->get_position();
file->seek(file_base_ofs);
file->store_64(file_base); // Update files base.
file->seek(file_base);
files.clear();
ofs = 0;
return OK;
}
@ -114,7 +128,7 @@ Error PCKPacker::add_file_removal(const String &p_target_path) {
// Simplify path here and on every 'files' access so that paths that have extra '/'
// symbols or 'res://' in them still match the MD5 hash for the saved path.
pf.path = p_target_path.simplify_path().trim_prefix("res://");
pf.ofs = ofs;
pf.ofs = file->get_position();
pf.size = 0;
pf.removal = true;
@ -138,7 +152,7 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa
// symbols or 'res://' in them still match the MD5 hash for the saved path.
pf.path = p_target_path.simplify_path().trim_prefix("res://");
pf.src_path = p_source_path;
pf.ofs = ofs;
pf.ofs = file->get_position();
pf.size = f->get_length();
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_path);
@ -152,18 +166,29 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa
}
pf.encrypted = p_encrypt;
uint64_t _size = pf.size;
if (p_encrypt) { // Add encryption overhead.
if (_size % 16) { // Pad to encryption block size.
_size += 16 - (_size % 16);
}
_size += 16; // hash
_size += 8; // data size
_size += 16; // iv
Ref<FileAccess> ftmp = file;
Ref<FileAccessEncrypted> fae;
if (p_encrypt) {
fae.instantiate();
ERR_FAIL_COND_V(fae.is_null(), ERR_CANT_CREATE);
Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
ftmp = fae;
}
int pad = _get_pad(alignment, ofs + _size);
ofs = ofs + _size + pad;
ftmp->store_buffer(data);
if (fae.is_valid()) {
ftmp.unref();
fae.unref();
}
int pad = _get_pad(alignment, file->get_position());
for (int j = 0; j < pad; j++) {
file->store_8(0);
}
files.push_back(pf);
@ -173,14 +198,17 @@ Error PCKPacker::add_file(const String &p_target_path, const String &p_source_pa
Error PCKPacker::flush(bool p_verbose) {
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
int64_t file_base_ofs = file->get_position();
file->store_64(0); // files base
for (int i = 0; i < 16; i++) {
file->store_32(0); // reserved
int dir_padding = _get_pad(alignment, file->get_position());
for (int i = 0; i < dir_padding; i++) {
file->store_8(0);
}
// write the index
// Write directory.
uint64_t dir_offset = file->get_position();
file->seek(dir_base_ofs);
file->store_64(dir_offset);
file->seek(dir_offset);
file->store_32(uint32_t(files.size()));
Ref<FileAccessEncrypted> fae;
@ -196,7 +224,8 @@ Error PCKPacker::flush(bool p_verbose) {
fhead = fae;
}
for (int i = 0; i < files.size(); i++) {
const int file_num = files.size();
for (int i = 0; i < file_num; i++) {
CharString utf8_string = files[i].path.utf8();
int string_len = utf8_string.length();
int pad = _get_pad(4, string_len);
@ -207,9 +236,9 @@ Error PCKPacker::flush(bool p_verbose) {
fhead->store_8(0);
}
fhead->store_64(files[i].ofs);
fhead->store_64(files[i].size); // pay attention here, this is where file is
fhead->store_buffer(files[i].md5.ptr(), 16); //also save md5 for file
fhead->store_64(files[i].ofs - file_base);
fhead->store_64(files[i].size);
fhead->store_buffer(files[i].md5.ptr(), 16);
uint32_t flags = 0;
if (files[i].encrypted) {
@ -219,6 +248,10 @@ Error PCKPacker::flush(bool p_verbose) {
flags |= PACK_FILE_REMOVAL;
}
fhead->store_32(flags);
if (p_verbose) {
print_line(vformat("[%d/%d - %d%%] PCKPacker flush: %s -> %s", i, file_num, float(i) / file_num * 100, files[i].src_path, files[i].path));
}
}
if (fae.is_valid()) {
@ -226,63 +259,12 @@ Error PCKPacker::flush(bool p_verbose) {
fae.unref();
}
int header_padding = _get_pad(alignment, file->get_position());
for (int i = 0; i < header_padding; i++) {
file->store_8(0);
}
uint64_t file_base = file->get_position();
file->seek(file_base_ofs);
file->store_64(file_base); // update files base
file->seek(file_base);
const uint32_t buf_max = 65536;
uint8_t *buf = memnew_arr(uint8_t, buf_max);
int count = 0;
for (int i = 0; i < files.size(); i++) {
if (files[i].removal) {
continue;
}
Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ);
uint64_t to_write = files[i].size;
Ref<FileAccess> ftmp = file;
if (files[i].encrypted) {
fae.instantiate();
ERR_FAIL_COND_V(fae.is_null(), ERR_CANT_CREATE);
Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
ftmp = fae;
}
while (to_write > 0) {
uint64_t read = src->get_buffer(buf, MIN(to_write, buf_max));
ftmp->store_buffer(buf, read);
to_write -= read;
}
if (fae.is_valid()) {
ftmp.unref();
fae.unref();
}
int pad = _get_pad(alignment, file->get_position());
for (int j = 0; j < pad; j++) {
file->store_8(0);
}
count += 1;
const int file_num = files.size();
if (p_verbose && (file_num > 0)) {
print_line(vformat("[%d/%d - %d%%] PCKPacker flush: %s -> %s", count, file_num, float(count) / file_num * 100, files[i].src_path, files[i].path));
}
}
file.unref();
memdelete_arr(buf);
return OK;
}
PCKPacker::~PCKPacker() {
if (file.is_valid()) {
flush();
}
}