1/**************************************************************************/
2/* lipo.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "lipo.h"
32
33bool LipO::is_lipo(const String &p_path) {
34 Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
35 ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
36 uint32_t magic = fb->get_32();
37 return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf);
38}
39
40bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_files) {
41 close();
42
43 fa = FileAccess::open(p_output_path, FileAccess::WRITE);
44 ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path));
45
46 uint64_t max_size = 0;
47 for (int i = 0; i < p_files.size(); i++) {
48 {
49 MachO mh;
50 if (!mh.open_file(p_files[i])) {
51 ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s\".", p_files[i]));
52 }
53
54 FatArch arch;
55 arch.cputype = mh.get_cputype();
56 arch.cpusubtype = mh.get_cpusubtype();
57 arch.offset = 0;
58 arch.size = mh.get_size();
59 arch.align = mh.get_align();
60 max_size += arch.size;
61
62 archs.push_back(arch);
63 }
64
65 Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
66 if (fb.is_null()) {
67 close();
68 ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
69 }
70 }
71
72 // Write header.
73 bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max());
74 if (is_64) {
75 fa->store_32(0xbfbafeca);
76 } else {
77 fa->store_32(0xbebafeca);
78 }
79 fa->store_32(BSWAP32(archs.size()));
80 uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8;
81 for (int i = 0; i < archs.size(); i++) {
82 archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align);
83 if (is_64) {
84 fa->store_32(BSWAP32(archs[i].cputype));
85 fa->store_32(BSWAP32(archs[i].cpusubtype));
86 fa->store_64(BSWAP64(archs[i].offset));
87 fa->store_64(BSWAP64(archs[i].size));
88 fa->store_32(BSWAP32(archs[i].align));
89 fa->store_32(0);
90 } else {
91 fa->store_32(BSWAP32(archs[i].cputype));
92 fa->store_32(BSWAP32(archs[i].cpusubtype));
93 fa->store_32(BSWAP32(archs[i].offset));
94 fa->store_32(BSWAP32(archs[i].size));
95 fa->store_32(BSWAP32(archs[i].align));
96 }
97 offset = archs[i].offset + archs[i].size;
98 }
99
100 // Write files and padding.
101 for (int i = 0; i < archs.size(); i++) {
102 Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ);
103 if (fb.is_null()) {
104 close();
105 ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s\".", p_files[i]));
106 }
107 uint64_t cur = fa->get_position();
108 for (uint64_t j = cur; j < archs[i].offset; j++) {
109 fa->store_8(0);
110 }
111 int pages = archs[i].size / 4096;
112 int remain = archs[i].size % 4096;
113 unsigned char step[4096];
114 for (int j = 0; j < pages; j++) {
115 uint64_t br = fb->get_buffer(step, 4096);
116 if (br > 0) {
117 fa->store_buffer(step, br);
118 }
119 }
120 uint64_t br = fb->get_buffer(step, remain);
121 if (br > 0) {
122 fa->store_buffer(step, br);
123 }
124 }
125 return true;
126}
127
128bool LipO::open_file(const String &p_path) {
129 close();
130
131 fa = FileAccess::open(p_path, FileAccess::READ);
132 ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
133
134 uint32_t magic = fa->get_32();
135 if (magic == 0xbebafeca) {
136 // 32-bit fat binary, bswap.
137 uint32_t nfat_arch = BSWAP32(fa->get_32());
138 for (uint32_t i = 0; i < nfat_arch; i++) {
139 FatArch arch;
140 arch.cputype = BSWAP32(fa->get_32());
141 arch.cpusubtype = BSWAP32(fa->get_32());
142 arch.offset = BSWAP32(fa->get_32());
143 arch.size = BSWAP32(fa->get_32());
144 arch.align = BSWAP32(fa->get_32());
145
146 archs.push_back(arch);
147 }
148 } else if (magic == 0xcafebabe) {
149 // 32-bit fat binary.
150 uint32_t nfat_arch = fa->get_32();
151 for (uint32_t i = 0; i < nfat_arch; i++) {
152 FatArch arch;
153 arch.cputype = fa->get_32();
154 arch.cpusubtype = fa->get_32();
155 arch.offset = fa->get_32();
156 arch.size = fa->get_32();
157 arch.align = fa->get_32();
158
159 archs.push_back(arch);
160 }
161 } else if (magic == 0xbfbafeca) {
162 // 64-bit fat binary, bswap.
163 uint32_t nfat_arch = BSWAP32(fa->get_32());
164 for (uint32_t i = 0; i < nfat_arch; i++) {
165 FatArch arch;
166 arch.cputype = BSWAP32(fa->get_32());
167 arch.cpusubtype = BSWAP32(fa->get_32());
168 arch.offset = BSWAP64(fa->get_64());
169 arch.size = BSWAP64(fa->get_64());
170 arch.align = BSWAP32(fa->get_32());
171 fa->get_32(); // Skip, reserved.
172
173 archs.push_back(arch);
174 }
175 } else if (magic == 0xcafebabf) {
176 // 64-bit fat binary.
177 uint32_t nfat_arch = fa->get_32();
178 for (uint32_t i = 0; i < nfat_arch; i++) {
179 FatArch arch;
180 arch.cputype = fa->get_32();
181 arch.cpusubtype = fa->get_32();
182 arch.offset = fa->get_64();
183 arch.size = fa->get_64();
184 arch.align = fa->get_32();
185 fa->get_32(); // Skip, reserved.
186
187 archs.push_back(arch);
188 }
189 } else {
190 close();
191 ERR_FAIL_V_MSG(false, vformat("LipO: Invalid fat binary: \"%s\".", p_path));
192 }
193 return true;
194}
195
196int LipO::get_arch_count() const {
197 ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened.");
198 return archs.size();
199}
200
201bool LipO::extract_arch(int p_index, const String &p_path) {
202 ERR_FAIL_COND_V_MSG(fa.is_null(), false, "LipO: File not opened.");
203 ERR_FAIL_INDEX_V(p_index, archs.size(), false);
204
205 Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::WRITE);
206 ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path));
207
208 fa->seek(archs[p_index].offset);
209
210 int pages = archs[p_index].size / 4096;
211 int remain = archs[p_index].size % 4096;
212 unsigned char step[4096];
213 for (int i = 0; i < pages; i++) {
214 uint64_t br = fa->get_buffer(step, 4096);
215 if (br > 0) {
216 fb->store_buffer(step, br);
217 }
218 }
219 uint64_t br = fa->get_buffer(step, remain);
220 if (br > 0) {
221 fb->store_buffer(step, br);
222 }
223 return true;
224}
225
226void LipO::close() {
227 archs.clear();
228}
229
230LipO::~LipO() {
231 close();
232}
233