Skip to content

Commit

Permalink
[3.x] Improve embedded PCK loading and exporting.
Browse files Browse the repository at this point in the history
  • Loading branch information
bruvzg committed Apr 28, 2022
1 parent f882144 commit bea7c9b
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 69 deletions.
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

0 comments on commit bea7c9b

Please sign in to comment.