From 7e1c6ce8f0b0bf1c84b5cc339cb5afac582eeff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:17:59 +0200 Subject: [PATCH] Fix "Pack Project as Zip" not saving permissions and time. --- editor/export/editor_export_platform.cpp | 4 +-- editor/export/project_zip_packer.cpp | 31 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index b2ff4d6feb..f384a582d6 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -1784,7 +1784,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_ // 0000755: permissions rwxr-xr-x // 0000644: permissions rw-r--r-- uint32_t _mode = 0120644; - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.external_fa = (_mode << 16L) | ((_mode & 0200) ? 0 : 1); // UUUUUUUUUUUUUUUU0000000000ADVSHR: Unix permissions (U) + DOS read-only flag (R). zipfi.internal_fa = 0; zipOpenNewFileInZip4(p_zip, @@ -1828,7 +1828,7 @@ void EditorExportPlatform::zip_folder_recursive(zipFile &p_zip, const String &p_ // 0000755: permissions rwxr-xr-x // 0000644: permissions rw-r--r-- uint32_t _mode = (_is_executable ? 0100755 : 0100644); - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.external_fa = (_mode << 16L) | ((_mode & 0200) ? 0 : 1); // UUUUUUUUUUUUUUUU0000000000ADVSHR: Unix permissions (U) + DOS read-only flag (R). zipfi.internal_fa = 0; zipOpenNewFileInZip4(p_zip, diff --git a/editor/export/project_zip_packer.cpp b/editor/export/project_zip_packer.cpp index 4e4639b107..6619170031 100644 --- a/editor/export/project_zip_packer.cpp +++ b/editor/export/project_zip_packer.cpp @@ -72,10 +72,39 @@ void ProjectZIPPacker::_zip_file(const String &p_path, const String &p_base_path data.resize(len); f->get_buffer(data.ptrw(), len); + uint64_t time = FileAccess::get_modified_time(p_path); + if (time == 0) { + time = Time::get_singleton()->get_unix_time_from_system(); + } + Dictionary tz = Time::get_singleton()->get_time_zone_from_system(); + time += tz["bias"].operator int() * 60; + Dictionary dt = Time::get_singleton()->get_datetime_dict_from_unix_time(time); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_year = dt["year"]; + zipfi.tmz_date.tm_mon = dt["month"].operator int() - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_mday = dt["day"]; + zipfi.tmz_date.tm_hour = dt["hour"]; + zipfi.tmz_date.tm_min = dt["minute"]; + zipfi.tmz_date.tm_sec = dt["second"]; + zipfi.dosDate = 0; + + // 0100000: regular file type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = FileAccess::get_unix_permissions(p_path); + if (_mode == 0) { + _mode = 0100644; + } else { + _mode |= 0100000; + } + zipfi.external_fa = (_mode << 16L) | ((_mode & 0200) ? 0 : 1); // UUUUUUUUUUUUUUUU0000000000ADVSHR: Unix permissions (U) + DOS read-only flag (R). + zipfi.internal_fa = 0; + String path = p_path.trim_prefix(p_base_path); zipOpenNewFileInZip4(p_zip, path.utf8().get_data(), - nullptr, + &zipfi, nullptr, 0, nullptr,