Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Improve embedded PCK loading and exporting. #60580

Merged
merged 1 commit into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions core/io/file_access_pack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "file_access_pack.h"

#include "core/os/os.h"
#include "core/version.h"

#include <stdio.h>
Expand Down Expand Up @@ -130,40 +131,76 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
return false;
}

bool pck_header_found = false;

// Search for the header at the start offset - standalone PCK file.
f->seek(p_offset);

uint32_t magic = f->get_32();
if (magic == PACK_HEADER_MAGIC) {
pck_header_found = true;
}

if (magic != PACK_HEADER_MAGIC) {
// loading with offset feature not supported for self contained exe files
// Search for the header in the executable "pck" section - self contained executable.
if (!pck_header_found) {
// Loading with offset feature not supported for self contained exe files.
if (p_offset != 0) {
f->close();
memdelete(f);
ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported.");
}

//maybe at the end.... self contained exe
f->seek_end();
f->seek(f->get_position() - 4);
magic = f->get_32();
if (magic != PACK_HEADER_MAGIC) {
int64_t pck_off = OS::get_singleton()->get_embedded_pck_offset();
if (pck_off != 0) {
// Search for the header, in case PCK start and section have different alignment.
for (int i = 0; i < 8; i++) {
f->seek(pck_off);
magic = f->get_32();
if (magic == PACK_HEADER_MAGIC) {
#ifdef DEBUG_ENABLED
print_verbose("PCK header found in executable pck section, loading from offset 0x" + String::num_int64(pck_off - 4, 16));
#endif
pck_header_found = true;
break;
}
pck_off++;
}
}
}

// Search for the header at the end of file - self contained executable.
if (!pck_header_found) {
// Loading with offset feature not supported for self contained exe files.
if (p_offset != 0) {
f->close();
memdelete(f);
return false;
ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported.");
}
f->seek(f->get_position() - 12);

uint64_t ds = f->get_64();
f->seek(f->get_position() - ds - 8);
f->seek_end();
f->seek(f->get_position() - 4);

magic = f->get_32();
if (magic != PACK_HEADER_MAGIC) {
f->close();
memdelete(f);
return false;
if (magic == PACK_HEADER_MAGIC) {
f->seek(f->get_position() - 12);
uint64_t ds = f->get_64();
f->seek(f->get_position() - ds - 8);
magic = f->get_32();
if (magic == PACK_HEADER_MAGIC) {
#ifdef DEBUG_ENABLED
print_verbose("PCK header found at the end of executable, loading from offset 0x" + String::num_int64(f->get_position() - 4, 16));
#endif
pck_header_found = true;
}
}
}

if (!pck_header_found) {
f->close();
memdelete(f);
return false;
}

uint32_t version = f->get_32();
uint32_t ver_major = f->get_32();
uint32_t ver_minor = f->get_32();
Expand Down
5 changes: 5 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ String OS::get_locale_language() const {
return get_locale().left(3).replace("_", "");
}

// Embedded PCK offset.
uint64_t OS::get_embedded_pck_offset() const {
return 0;
}

// Helper function to ensure that a dir name/path will be valid on the OS
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator) const {
Vector<String> invalid_chars = String(": * ? \" < > |").split(" ");
Expand Down
2 changes: 2 additions & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ class OS {
virtual String get_locale() const;
String get_locale_language() const;

virtual uint64_t get_embedded_pck_offset() const;

String get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator = false) const;
virtual String get_godot_dir_name() const;

Expand Down
70 changes: 43 additions & 27 deletions editor/editor_export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,19 @@ List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExpor
Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);

Error err = prepare_template(p_preset, p_debug, p_path, p_flags);
if (err == OK) {
err = export_project_data(p_preset, p_debug, p_path, p_flags);
}

return err;
}

Error EditorExportPlatformPC::prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
if (!DirAccess::exists(p_path.get_base_dir())) {
return ERR_FILE_BAD_PATH;
}

String custom_debug = p_preset->get("custom_template/debug");
String custom_release = p_preset->get("custom_template/release");

Expand Down Expand Up @@ -1657,38 +1670,41 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr
da->make_dir_recursive(p_path.get_base_dir());
Error err = da->copy(template_path, p_path, get_chmod_flags());

if (err == OK) {
String pck_path;
if (p_preset->get("binary_format/embed_pck")) {
pck_path = p_path;
} else {
pck_path = p_path.get_basename() + ".pck";
}
return err;
}

Vector<SharedObject> so_files;
Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
String pck_path;
if (p_preset->get("binary_format/embed_pck")) {
pck_path = p_path;
} else {
pck_path = p_path.get_basename() + ".pck";
}

int64_t embedded_pos;
int64_t embedded_size;
err = save_pack(p_preset, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
if (err == OK && p_preset->get("binary_format/embed_pck")) {
if (embedded_size >= 0x100000000 && !p_preset->get("binary_format/64_bits")) {
EditorNode::get_singleton()->show_warning(TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
return ERR_INVALID_PARAMETER;
}
Vector<SharedObject> so_files;

FixUpEmbeddedPckFunc fixup_func = get_fixup_embedded_pck_func();
if (fixup_func) {
err = fixup_func(p_path, embedded_pos, embedded_size);
}
int64_t embedded_pos;
int64_t embedded_size;
Error err = save_pack(p_preset, pck_path, &so_files, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size);
if (err == OK && p_preset->get("binary_format/embed_pck")) {
if (embedded_size >= 0x100000000 && !p_preset->get("binary_format/64_bits")) {
EditorNode::get_singleton()->show_warning(TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB."));
return ERR_INVALID_PARAMETER;
}

if (err == OK && !so_files.empty()) {
// If shared object files, copy them.
for (int i = 0; i < so_files.size() && err == OK; i++) {
err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
if (err == OK) {
err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
}
FixUpEmbeddedPckFunc fixup_func = get_fixup_embedded_pck_func();
if (fixup_func) {
err = fixup_func(p_path, embedded_pos, embedded_size);
}
}

if (err == OK && !so_files.empty()) {
// If shared object files, copy them.
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < so_files.size() && err == OK; i++) {
err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
if (err == OK) {
err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file()));
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions editor/editor_export.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ class EditorExportPlatformPC : public EditorExportPlatform {
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);

Error prepare_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);
Error export_project_data(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags);

void set_extension(const String &p_extension, const String &p_feature_key = "default");
void set_name(const String &p_name);
void set_os_name(const String &p_name);
Expand Down
66 changes: 45 additions & 21 deletions platform/windows/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);

class EditorExportPlatformWindows : public EditorExportPlatformPC {
void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);

public:
Expand All @@ -60,16 +60,27 @@ Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPres
}

Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
String pck_path = p_path;
if (p_preset->get("binary_format/embed_pck")) {
pck_path = p_path.get_basename() + ".tmp";
}

if (err != OK) {
return err;
Error err = EditorExportPlatformPC::prepare_template(p_preset, p_debug, pck_path, p_flags);
if (p_preset->get("application/modify_resources") && err == OK) {
err = _rcedit_add_data(p_preset, pck_path);
}

_rcedit_add_data(p_preset, p_path);
if (err == OK) {
err = EditorExportPlatformPC::export_project_data(p_preset, p_debug, pck_path, p_flags);
}

if (p_preset->get("codesign/enable") && err == OK) {
err = _code_sign(p_preset, p_path);
err = _code_sign(p_preset, pck_path);
}

if (p_preset->get("binary_format/embed_pck") && err == OK) {
DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->rename(pck_path, p_path);
}

return err;
Expand All @@ -96,6 +107,7 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray()));

r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/modify_resources"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
Expand All @@ -106,26 +118,25 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
}

void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");

if (rcedit_path.empty()) {
WARN_PRINT("The rcedit tool is not configured in the Editor Settings (Export > Windows > Rcedit). No custom icon or app information data will be embedded in the exported executable.");
return;
if (rcedit_path != String() && !FileAccess::exists(rcedit_path)) {
ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", aborting.");
return ERR_FILE_NOT_FOUND;
}

if (!FileAccess::exists(rcedit_path)) {
ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included.");
return;
if (rcedit_path == String()) {
rcedit_path = "rcedit"; // try to run rcedit from PATH
}

#ifndef WINDOWS_ENABLED
// On non-Windows we need WINE to run rcedit
String wine_path = EditorSettings::get_singleton()->get("export/windows/wine");

if (wine_path != String() && !FileAccess::exists(wine_path)) {
ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included.");
return;
ERR_PRINT("Could not find wine executable at " + wine_path + ", aborting.");
return ERR_FILE_NOT_FOUND;
}

if (wine_path == String()) {
Expand Down Expand Up @@ -183,13 +194,22 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
args.push_back(trademarks);
}

#ifdef WINDOWS_ENABLED
OS::get_singleton()->execute(rcedit_path, args, true);
#else
#ifndef WINDOWS_ENABLED
// On non-Windows we need WINE to run rcedit
args.push_front(rcedit_path);
OS::get_singleton()->execute(wine_path, args, true);
rcedit_path = wine_path;
#endif

String str;
Error err = OS::get_singleton()->execute(rcedit_path, args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not start rcedit executable, configure rcedit path in the Editor Settings (Export > Windows > Rcedit).");
print_line("rcedit (" + p_path + "): " + str);

if (str.find("Fatal error") != -1) {
return FAILED;
}

return OK;
}

Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
Expand Down Expand Up @@ -326,7 +346,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p

String str;
Error err = OS::get_singleton()->execute(signtool_path, args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not start signtool executable, configure signtool path in the Editor Settings (Export > Windows > Signtool).");

print_line("codesign (" + p_path + "): " + str);
#ifndef WINDOWS_ENABLED
Expand Down Expand Up @@ -355,7 +375,7 @@ bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_pr
bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates);

String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
if (rcedit_path.empty()) {
if (p_preset->get("application/modify_resources") && rcedit_path.empty()) {
err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > Rcedit) to change the icon or app information data.") + "\n";
}

Expand Down Expand Up @@ -430,6 +450,10 @@ void register_windows_exporter() {
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data

if (p_embedded_size + p_embedded_start >= 0x100000000) { // Check for total executable size
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Windows executables cannot be >= 4 GiB.");
}

FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE);
if (!f) {
return ERR_CANT_OPEN;
Expand Down
18 changes: 12 additions & 6 deletions platform/windows/godot_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,18 @@
#ifndef TOOLS_ENABLED
#if defined _MSC_VER
#pragma section("pck", read)
__declspec(allocate("pck")) static const char dummy[8] = { 0 };
__declspec(allocate("pck")) static char dummy[8] = { 0 };

// Dummy function to prevent LTO from discarding "pck" section.
extern "C" char *__cdecl pck_section_dummy_call() {
return &dummy[0];
};
#if defined _AMD64_
#pragma comment(linker, "/include:pck_section_dummy_call")
#elif defined _X86_
#pragma comment(linker, "/include:_pck_section_dummy_call")
#endif

#elif defined __GNUC__
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
#endif
Expand Down Expand Up @@ -140,11 +151,6 @@ __declspec(dllexport) int widechar_main(int argc, wchar_t **argv) {

setlocale(LC_CTYPE, "");

#ifndef TOOLS_ENABLED
// Workaround to prevent LTCG (MSVC LTO) from removing "pck" section
const char *dummy_guard = dummy;
#endif

char **argv_utf8 = new char *[argc];

for (int i = 0; i < argc; ++i) {
Expand Down
Loading