mirror of
https://github.com/Redot-Engine/redot-engine.git
synced 2025-12-06 07:17:42 -05:00
Implement sparse bundle PCK support.
This commit is contained in:
committed by
Rémi Verschelde
parent
d89f4ab32f
commit
42733a2a5c
@@ -625,6 +625,7 @@ bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, c
|
||||
".cfb", // Don't let small config files slow-down startup
|
||||
".scn", // Binary scenes are usually already compressed
|
||||
".ctex", // Streamable textures are usually already compressed
|
||||
".pck", // Pack.
|
||||
// Trailer for easier processing
|
||||
nullptr
|
||||
};
|
||||
@@ -800,14 +801,25 @@ Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObj
|
||||
Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) {
|
||||
APKExportData *ed = static_cast<APKExportData *>(p_userdata);
|
||||
|
||||
String path = p_path.simplify_path();
|
||||
if (path.begins_with("uid://")) {
|
||||
path = ResourceUID::uid_to_path(path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, path));
|
||||
String simplified_path = p_path.simplify_path();
|
||||
if (simplified_path.begins_with("uid://")) {
|
||||
simplified_path = ResourceUID::uid_to_path(simplified_path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, simplified_path));
|
||||
}
|
||||
const String dst_path = path.replace_first("res://", "assets/");
|
||||
|
||||
store_in_apk(ed, dst_path, p_data, _should_compress_asset(path, p_data) ? Z_DEFLATED : 0);
|
||||
Vector<uint8_t> enc_data;
|
||||
EditorExportPlatform::SavedData sd;
|
||||
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, enc_data, sd);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
const String dst_path = String("assets/") + simplified_path.trim_prefix("res://");
|
||||
print_verbose("Saving project files from " + simplified_path + " into " + dst_path);
|
||||
store_in_apk(ed, dst_path, enc_data, _should_compress_asset(simplified_path, enc_data) ? Z_DEFLATED : 0);
|
||||
|
||||
ed->pd.file_ofs.push_back(sd);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -3409,6 +3421,68 @@ Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset>
|
||||
return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
|
||||
}
|
||||
|
||||
Error EditorExportPlatformAndroid::_generate_sparse_pck_metadata(const Ref<EditorExportPreset> &p_preset, PackData &p_pack_data, Vector<uint8_t> &r_data) {
|
||||
Error err;
|
||||
Ref<FileAccess> ftmp = FileAccess::create_temp(FileAccess::WRITE_READ, "export_index", "tmp", false, &err);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Could not create temporary file!"));
|
||||
return err;
|
||||
}
|
||||
int64_t pck_start_pos = ftmp->get_position();
|
||||
uint64_t file_base_ofs = 0;
|
||||
uint64_t dir_base_ofs = 0;
|
||||
EditorExportPlatform::_store_header(ftmp, p_preset->get_enc_pck() && p_preset->get_enc_directory(), true, file_base_ofs, dir_base_ofs);
|
||||
|
||||
// Write directory.
|
||||
uint64_t dir_offset = ftmp->get_position();
|
||||
ftmp->seek(dir_base_ofs);
|
||||
ftmp->store_64(dir_offset - pck_start_pos);
|
||||
ftmp->seek(dir_offset);
|
||||
|
||||
Vector<uint8_t> key;
|
||||
if (p_preset->get_enc_pck() && p_preset->get_enc_directory()) {
|
||||
String script_key = _get_script_encryption_key(p_preset);
|
||||
key.resize(32);
|
||||
if (script_key.length() == 64) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
int v = 0;
|
||||
if (i * 2 < script_key.length()) {
|
||||
char32_t ct = script_key[i * 2];
|
||||
if (is_digit(ct)) {
|
||||
ct = ct - '0';
|
||||
} else if (ct >= 'a' && ct <= 'f') {
|
||||
ct = 10 + ct - 'a';
|
||||
}
|
||||
v |= ct << 4;
|
||||
}
|
||||
|
||||
if (i * 2 + 1 < script_key.length()) {
|
||||
char32_t ct = script_key[i * 2 + 1];
|
||||
if (is_digit(ct)) {
|
||||
ct = ct - '0';
|
||||
} else if (ct >= 'a' && ct <= 'f') {
|
||||
ct = 10 + ct - 'a';
|
||||
}
|
||||
v |= ct;
|
||||
}
|
||||
key.write[i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!EditorExportPlatform::_encrypt_and_store_directory(ftmp, p_pack_data, key, p_preset->get_seed(), 0)) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't create encrypted file."));
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
r_data.resize(ftmp->get_length());
|
||||
ftmp->seek(0);
|
||||
ftmp->get_buffer(r_data.ptrw(), r_data.size());
|
||||
ftmp.unref();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
@@ -3536,7 +3610,22 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
||||
if (p_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT)) {
|
||||
err = export_project_files(p_preset, p_debug, ignore_apk_file, nullptr, &user_data, copy_gradle_so);
|
||||
} else {
|
||||
user_data.pd.path = "assets.sparsepck";
|
||||
user_data.pd.use_sparse_pck = true;
|
||||
err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, nullptr, &user_data, copy_gradle_so);
|
||||
|
||||
Vector<uint8_t> enc_data;
|
||||
err = _generate_sparse_pck_metadata(p_preset, user_data.pd, enc_data);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Could not generate sparse pck metadata!"));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = store_file_at_path(user_data.assets_directory + "/assets.sparsepck", enc_data);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Could not write PCK directory!"));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project."));
|
||||
@@ -3988,7 +4077,18 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
|
||||
APKExportData ed;
|
||||
ed.ep = &ep;
|
||||
ed.apk = unaligned_apk;
|
||||
ed.pd.path = "assets.sparsepck";
|
||||
ed.pd.use_sparse_pck = true;
|
||||
err = export_project_files(p_preset, p_debug, save_apk_file, nullptr, &ed, save_apk_so);
|
||||
|
||||
Vector<uint8_t> enc_data;
|
||||
err = _generate_sparse_pck_metadata(p_preset, ed.pd, enc_data);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Could not generate sparse pck metadata!"));
|
||||
return err;
|
||||
}
|
||||
|
||||
store_in_apk(&ed, "assets/assets.sparsepck", enc_data, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||
};
|
||||
|
||||
struct APKExportData {
|
||||
EditorExportPlatform::PackData pd;
|
||||
zipFile apk;
|
||||
EditorProgress *ep = nullptr;
|
||||
};
|
||||
@@ -193,6 +194,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||
|
||||
bool _uses_vulkan(const Ref<EditorExportPreset> &p_preset) const;
|
||||
|
||||
Error _generate_sparse_pck_metadata(const Ref<EditorExportPreset> &p_preset, PackData &p_pack_data, Vector<uint8_t> &r_data);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
|
||||
@@ -172,15 +172,24 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
|
||||
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed) {
|
||||
CustomExportData *export_data = static_cast<CustomExportData *>(p_userdata);
|
||||
|
||||
String path = p_path.simplify_path();
|
||||
if (path.begins_with("uid://")) {
|
||||
path = ResourceUID::uid_to_path(path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, path));
|
||||
String simplified_path = p_path.simplify_path();
|
||||
if (simplified_path.begins_with("uid://")) {
|
||||
simplified_path = ResourceUID::uid_to_path(simplified_path).simplify_path();
|
||||
print_verbose(vformat(R"(UID referenced exported file name "%s" was replaced with "%s".)", p_path, simplified_path));
|
||||
}
|
||||
const String dst_path = path.replace_first("res://", export_data->assets_directory + "/");
|
||||
|
||||
print_verbose("Saving project files from " + path + " into " + dst_path);
|
||||
Error err = store_file_at_path(dst_path, p_data);
|
||||
Vector<uint8_t> enc_data;
|
||||
EditorExportPlatform::SavedData sd;
|
||||
Error err = _store_temp_file(simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, enc_data, sd);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
const String dst_path = export_data->assets_directory + String("/") + simplified_path.trim_prefix("res://");
|
||||
print_verbose("Saving project files from " + simplified_path + " into " + dst_path);
|
||||
err = store_file_at_path(dst_path, enc_data);
|
||||
|
||||
export_data->pd.file_ofs.push_back(sd);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -354,3 +363,34 @@ String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform,
|
||||
manifest_application_text += " </application>\n";
|
||||
return manifest_application_text;
|
||||
}
|
||||
|
||||
Error _store_temp_file(const String &p_simplified_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd) {
|
||||
Error err = OK;
|
||||
Ref<FileAccess> ftmp = FileAccess::create_temp(FileAccess::WRITE_READ, "export", "tmp", false, &err);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
r_sd.path_utf8 = p_simplified_path.trim_prefix("res://").utf8();
|
||||
r_sd.ofs = 0;
|
||||
r_sd.size = p_data.size();
|
||||
err = EditorExportPlatform::_encrypt_and_store_data(ftmp, p_simplified_path, p_data, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed, r_sd.encrypted);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
r_enc_data.resize(ftmp->get_length());
|
||||
ftmp->seek(0);
|
||||
ftmp->get_buffer(r_enc_data.ptrw(), r_enc_data.size());
|
||||
ftmp.unref();
|
||||
|
||||
// Store MD5 of original file.
|
||||
{
|
||||
unsigned char hash[16];
|
||||
CryptoCore::md5(p_data.ptr(), p_data.size(), hash);
|
||||
r_sd.md5.resize(16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
r_sd.md5.write[i] = hash[i];
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/zip_io.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
||||
const String GODOT_PROJECT_NAME_XML_STRING = R"(<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
|
||||
@@ -62,6 +64,7 @@ static const int XR_MODE_REGULAR = 0;
|
||||
static const int XR_MODE_OPENXR = 1;
|
||||
|
||||
struct CustomExportData {
|
||||
EditorExportPlatform::PackData pd;
|
||||
String assets_directory;
|
||||
String libs_directory;
|
||||
bool debug;
|
||||
@@ -81,6 +84,8 @@ int _get_app_category_value(int category_index);
|
||||
|
||||
String _get_app_category_label(int category_index);
|
||||
|
||||
Error _store_temp_file(const String &p_simplified_path, const Vector<uint8_t> &p_data, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key, uint64_t p_seed, Vector<uint8_t> &r_enc_data, EditorExportPlatform::SavedData &r_sd);
|
||||
|
||||
// Utility method used to create a directory.
|
||||
Error create_directory(const String &p_dir);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user