mirror of
https://github.com/Redot-Engine/redot-engine.git
synced 2025-12-06 07:17:42 -05:00
Merge pull request #105757 from bruvzg/no_temp
[PCK] Move directory to the end of file, write exported/saved PCK in place.
This commit is contained in:
@@ -256,31 +256,37 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
||||
|
||||
int64_t pck_start_pos = f->get_position() - 4;
|
||||
|
||||
// Read header.
|
||||
uint32_t version = f->get_32();
|
||||
uint32_t ver_major = f->get_32();
|
||||
uint32_t ver_minor = f->get_32();
|
||||
f->get_32(); // patch number, not used for validation.
|
||||
uint32_t ver_patch = f->get_32(); // Not used for validation.
|
||||
|
||||
ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, vformat("Pack version unsupported: %d.", version));
|
||||
ERR_FAIL_COND_V_MSG(ver_major > GODOT_VERSION_MAJOR || (ver_major == GODOT_VERSION_MAJOR && ver_minor > GODOT_VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.", ver_major, ver_minor));
|
||||
ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION_V3 && version != PACK_FORMAT_VERSION_V2, false, vformat("Pack version unsupported: %d.", version));
|
||||
ERR_FAIL_COND_V_MSG(ver_major > GODOT_VERSION_MAJOR || (ver_major == GODOT_VERSION_MAJOR && ver_minor > GODOT_VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.%d.", ver_major, ver_minor, ver_patch));
|
||||
|
||||
uint32_t pack_flags = f->get_32();
|
||||
uint64_t file_base = f->get_64();
|
||||
|
||||
bool enc_directory = (pack_flags & PACK_DIR_ENCRYPTED);
|
||||
bool rel_filebase = (pack_flags & PACK_REL_FILEBASE);
|
||||
bool rel_filebase = (pack_flags & PACK_REL_FILEBASE); // Note: Always enabled for V3.
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
//reserved
|
||||
f->get_32();
|
||||
}
|
||||
|
||||
int file_count = f->get_32();
|
||||
|
||||
if (rel_filebase) {
|
||||
uint64_t file_base = f->get_64();
|
||||
if ((version == PACK_FORMAT_VERSION_V3) || (version == PACK_FORMAT_VERSION_V2 && rel_filebase)) {
|
||||
file_base += pck_start_pos;
|
||||
}
|
||||
|
||||
if (version == PACK_FORMAT_VERSION_V3) {
|
||||
// V3: Read directory offset and skip reserved part of the header.
|
||||
uint64_t dir_offset = f->get_64() + pck_start_pos;
|
||||
f->seek(dir_offset);
|
||||
} else if (version == PACK_FORMAT_VERSION_V2) {
|
||||
// V2: Directory directly after the header.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
f->get_32(); // Reserved.
|
||||
}
|
||||
}
|
||||
|
||||
// Read directory.
|
||||
int file_count = f->get_32();
|
||||
if (enc_directory) {
|
||||
Ref<FileAccessEncrypted> fae;
|
||||
fae.instantiate();
|
||||
@@ -314,7 +320,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
||||
if (flags & PACK_FILE_REMOVAL) { // The file was removed.
|
||||
PackedData::get_singleton()->remove_path(path);
|
||||
} else {
|
||||
PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
|
||||
PackedData::get_singleton()->add_path(p_path, path, file_base + ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,12 @@
|
||||
|
||||
// Godot's packed file magic header ("GDPC" in ASCII).
|
||||
#define PACK_HEADER_MAGIC 0x43504447
|
||||
|
||||
#define PACK_FORMAT_VERSION_V2 2
|
||||
#define PACK_FORMAT_VERSION_V3 3
|
||||
|
||||
// The current packed file format version number.
|
||||
#define PACK_FORMAT_VERSION 2
|
||||
#define PACK_FORMAT_VERSION PACK_FORMAT_VERSION_V3
|
||||
|
||||
enum PackFlags {
|
||||
PACK_DIR_ENCRYPTED = 1 << 0,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,14 @@ class PCKPacker : public RefCounted {
|
||||
|
||||
Ref<FileAccess> file;
|
||||
int alignment = 0;
|
||||
uint64_t ofs = 0;
|
||||
|
||||
Vector<uint8_t> key;
|
||||
bool enc_dir = false;
|
||||
|
||||
uint64_t file_base = 0;
|
||||
uint64_t file_base_ofs = 0;
|
||||
uint64_t dir_base_ofs = 0;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
struct File {
|
||||
@@ -64,4 +67,5 @@ public:
|
||||
Error flush(bool p_verbose = false);
|
||||
|
||||
PCKPacker() {}
|
||||
~PCKPacker();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user