virtualx-engine/editor/export/lipo.cpp

339 lines
11 KiB
C++
Raw Normal View History

/**************************************************************************/
/* lipo.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "lipo.h"
bool LipO::is_lipo(const String &p_path) {
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
uint32_t magic = fb->get_32();
return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf);
}
bool LipO::create_file(const String &p_output_path, const Vector<String> &p_files) {
close();
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
uint64_t max_size = 0;
for (int i = 0; i < p_files.size(); i++) {
2023-06-16 10:07:56 +02:00
{
MachO mh;
if (!mh.open_file(p_files[i])) {
ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s\".", p_files[i]));
}
2023-06-16 10:07:56 +02:00
FatArch arch;
arch.cputype = mh.get_cputype();
arch.cpusubtype = mh.get_cpusubtype();
arch.offset = 0;
arch.size = mh.get_size();
arch.align = mh.get_align();
max_size += arch.size;
2023-06-16 10:07:56 +02:00
archs.push_back(arch);
}
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
if (fb.is_null()) {
close();
2023-06-16 10:07:56 +02:00
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
}
}
// Write header.
bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
if (is_64) {
fa->store_32(0xbfbafeca);
} else {
fa->store_32(0xbebafeca);
}
fa->store_32(BSWAP32(archs.size()));
uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
for (int i = 0; i < archs.size(); i++) {
archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
if (is_64) {
fa->store_32(BSWAP32(archs[i].cputype));
fa->store_32(BSWAP32(archs[i].cpusubtype));
fa->store_64(BSWAP64(archs[i].offset));
fa->store_64(BSWAP64(archs[i].size));
fa->store_32(BSWAP32(archs[i].align));
fa->store_32(0);
} else {
fa->store_32(BSWAP32(archs[i].cputype));
fa->store_32(BSWAP32(archs[i].cpusubtype));
fa->store_32(BSWAP32(archs[i].offset));
fa->store_32(BSWAP32(archs[i].size));
fa->store_32(BSWAP32(archs[i].align));
}
offset = archs[i].offset + archs[i].size;
}
// Write files and padding.
for (int i = 0; i < archs.size(); i++) {
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
if (fb.is_null()) {
close();
2023-06-16 10:07:56 +02:00
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
}
uint64_t cur = fa->get_position();
for (uint64_t j = cur; j < archs[i].offset; j++) {
fa->store_8(0);
}
int pages = archs[i].size / 4096;
int remain = archs[i].size % 4096;
unsigned char step[4096];
for (int j = 0; j < pages; j++) {
uint64_t br = fb->get_buffer(step, 4096);
if (br > 0) {
fa->store_buffer(step, br);
}
}
uint64_t br = fb->get_buffer(step, remain);
if (br > 0) {
fa->store_buffer(step, br);
}
}
return true;
}
bool LipO::create_file(const String &p_output_path, const Vector<String> &p_files, const Vector<Vector2i> &p_cputypes) {
close();
fa = FileAccess::open(p_output_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
ERR_FAIL_COND_V(p_files.size() != p_cputypes.size(), false);
uint64_t max_size = 0;
for (int i = 0; i < p_files.size(); i++) {
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
if (fb.is_null()) {
close();
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
}
{
FatArch arch;
MachO mh;
if (MachO::is_macho(p_files[i]) && mh.open_file(p_files[i])) {
arch.cputype = mh.get_cputype();
arch.cpusubtype = mh.get_cpusubtype();
arch.offset = 0;
arch.size = mh.get_size();
arch.align = mh.get_align();
ERR_FAIL_V_MSG(arch.cputype != (uint32_t)p_cputypes[i].x || arch.cpusubtype != (uint32_t)p_cputypes[i].y, vformat("Mismatching MachO architecture: \"%s\".", p_files[i]));
} else {
arch.cputype = (uint32_t)p_cputypes[i].x;
arch.cpusubtype = (uint32_t)p_cputypes[i].y;
arch.offset = 0;
arch.size = fb->get_length();
arch.align = 0x03;
}
max_size += arch.size;
archs.push_back(arch);
}
}
// Write header.
bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
if (is_64) {
fa->store_32(0xbfbafeca);
} else {
fa->store_32(0xbebafeca);
}
fa->store_32(BSWAP32(archs.size()));
uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
for (int i = 0; i < archs.size(); i++) {
archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
if (is_64) {
fa->store_32(BSWAP32(archs[i].cputype));
fa->store_32(BSWAP32(archs[i].cpusubtype));
fa->store_64(BSWAP64(archs[i].offset));
fa->store_64(BSWAP64(archs[i].size));
fa->store_32(BSWAP32(archs[i].align));
fa->store_32(0);
} else {
fa->store_32(BSWAP32(archs[i].cputype));
fa->store_32(BSWAP32(archs[i].cpusubtype));
fa->store_32(BSWAP32(archs[i].offset));
fa->store_32(BSWAP32(archs[i].size));
fa->store_32(BSWAP32(archs[i].align));
}
offset = archs[i].offset + archs[i].size;
}
// Write files and padding.
for (int i = 0; i < archs.size(); i++) {
Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
if (fb.is_null()) {
close();
ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
}
uint64_t cur = fa->get_position();
for (uint64_t j = cur; j < archs[i].offset; j++) {
fa->store_8(0);
}
int pages = archs[i].size / 4096;
int remain = archs[i].size % 4096;
unsigned char step[4096];
for (int j = 0; j < pages; j++) {
uint64_t br = fb->get_buffer(step, 4096);
if (br > 0) {
fa->store_buffer(step, br);
}
}
uint64_t br = fb->get_buffer(step, remain);
if (br > 0) {
fa->store_buffer(step, br);
}
}
return true;
}
bool LipO::open_file(const String &p_path) {
close();
fa = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
uint32_t magic = fa->get_32();
if (magic == 0xbebafeca) {
// 32-bit fat binary, bswap.
uint32_t nfat_arch = BSWAP32(fa->get_32());
for (uint32_t i = 0; i < nfat_arch; i++) {
FatArch arch;
arch.cputype = BSWAP32(fa->get_32());
arch.cpusubtype = BSWAP32(fa->get_32());
arch.offset = BSWAP32(fa->get_32());
arch.size = BSWAP32(fa->get_32());
arch.align = BSWAP32(fa->get_32());
archs.push_back(arch);
}
} else if (magic == 0xcafebabe) {
// 32-bit fat binary.
uint32_t nfat_arch = fa->get_32();
for (uint32_t i = 0; i < nfat_arch; i++) {
FatArch arch;
arch.cputype = fa->get_32();
arch.cpusubtype = fa->get_32();
arch.offset = fa->get_32();
arch.size = fa->get_32();
arch.align = fa->get_32();
archs.push_back(arch);
}
} else if (magic == 0xbfbafeca) {
// 64-bit fat binary, bswap.
uint32_t nfat_arch = BSWAP32(fa->get_32());
for (uint32_t i = 0; i < nfat_arch; i++) {
FatArch arch;
arch.cputype = BSWAP32(fa->get_32());
arch.cpusubtype = BSWAP32(fa->get_32());
arch.offset = BSWAP64(fa->get_64());
arch.size = BSWAP64(fa->get_64());
arch.align = BSWAP32(fa->get_32());
fa->get_32(); // Skip, reserved.
archs.push_back(arch);
}
} else if (magic == 0xcafebabf) {
// 64-bit fat binary.
uint32_t nfat_arch = fa->get_32();
for (uint32_t i = 0; i < nfat_arch; i++) {
FatArch arch;
arch.cputype = fa->get_32();
arch.cpusubtype = fa->get_32();
arch.offset = fa->get_64();
arch.size = fa->get_64();
arch.align = fa->get_32();
fa->get_32(); // Skip, reserved.
archs.push_back(arch);
}
} else {
close();
ERR_FAIL_V_MSG(false, vformat("LipO: Invalid fat binary: \"%s\".", p_path));
}
return true;
}
int LipO::get_arch_count() const {
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
return archs.size();
}
uint32_t LipO::get_arch_cputype(int p_index) const {
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
ERR_FAIL_INDEX_V(p_index, archs.size(), 0);
return archs[p_index].cputype;
}
uint32_t LipO::get_arch_cpusubtype(int p_index) const {
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
ERR_FAIL_INDEX_V(p_index, archs.size(), 0);
return archs[p_index].cpusubtype;
}
bool LipO::extract_arch(int p_index, const String &p_path) {
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "LipO: File not opened.");
ERR_FAIL_INDEX_V(p_index, archs.size(), false);
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
fa->seek(archs[p_index].offset);
int pages = archs[p_index].size / 4096;
int remain = archs[p_index].size % 4096;
unsigned char step[4096];
for (int i = 0; i < pages; i++) {
uint64_t br = fa->get_buffer(step, 4096);
if (br > 0) {
fb->store_buffer(step, br);
}
}
uint64_t br = fa->get_buffer(step, remain);
if (br > 0) {
fb->store_buffer(step, br);
}
return true;
}
void LipO::close() {
archs.clear();
}
LipO::~LipO() {
close();
}