mirror of
https://github.com/godotengine/godot.git
synced 2024-12-21 10:25:24 +08:00
339 lines
11 KiB
C++
339 lines
11 KiB
C++
/**************************************************************************/
|
|
/* 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++) {
|
|
{
|
|
MachO mh;
|
|
if (!mh.open_file(p_files[i])) {
|
|
ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s\".", p_files[i]));
|
|
}
|
|
|
|
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;
|
|
|
|
archs.push_back(arch);
|
|
}
|
|
|
|
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]));
|
|
}
|
|
}
|
|
|
|
// 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::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();
|
|
}
|