1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <cstddef>
16 #include <string.h>
17 #include <inttypes.h>
18 #include <algorithm>
19 #include <memory>
20 #include <string>
21 #include <string_view>
22 #include <vector>
23 #include <set>
24 #include "dir.h"
25 #include "coff.h"
26 #include "elf32.h"
27 #include "elf64.h"
28 
29 #ifdef __APPLE__
30 #include <mach-o/loader.h>
31 #include <mach-o/nlist.h>
32 #include <mach-o/fat.h>
33 #endif
34 
35 struct FsEntry {
36     char fname[256];
37     uint64_t offset;
38     uint64_t size;
39 };
40 
41 std::vector<FsEntry> g_directory;
42 std::vector<uint8_t> g_bin;
43 std::vector<std::string_view> g_validExts;
44 
AppendFile(const std::string & filename,const std::string & storename)45 void AppendFile(const std::string& filename, const std::string& storename)
46 {
47     std::string_view ext;
48     auto pos = filename.find_last_of(".");
49     if (pos != std::string::npos) {
50         // found it.
51         ext = std::string_view(filename).substr(pos);
52     }
53     bool valid = false;
54     for (const auto& e : g_validExts) {
55         if (ext.compare(e) == 0) {
56             valid = true;
57             break;
58         }
59     }
60     if (!valid) {
61         printf("Skipped %s\n", storename.c_str());
62         return;
63     }
64     if (storename.size() > (sizeof(FsEntry::fname) - 1u)) {
65         printf("Filename too long [%s]\n", storename.c_str());
66         exit(-1);
67     }
68     FsEntry tmp{};
69     tmp.fname[storename.copy(tmp.fname, sizeof(tmp.fname) - 1u)] = '\0';
70 #if _WIN32
71     struct _stat64 fileStat;
72     if (_stat64(filename.c_str(), &fileStat) == -1) {
73 #else
74     struct stat fileStat;
75     if (stat(filename.c_str(), &fileStat) == -1) {
76 #endif
77         printf("File [%s] not found\n", tmp.fname);
78         exit(-1);
79     }
80     tmp.size = fileStat.st_size;
81     auto padding = (8 - (g_bin.size() & 7)) & 7;
82     tmp.offset = g_bin.size() + padding;
83     g_directory.push_back(tmp);
84     FILE* f = fopen(filename.c_str(), "rb");
85     if (f == nullptr) {
86         printf("Could not open %s.\n", filename.c_str());
87         exit(-1);
88     }
89     g_bin.resize(static_cast<size_t>(g_bin.size() + padding + tmp.size));
90     fread(g_bin.data() + tmp.offset, 1, static_cast<size_t>(tmp.size), f);
91     fclose(f);
92     printf("Stored: %s [%" PRIu64 " , %" PRIu64 "]\n", tmp.fname, tmp.offset, tmp.size);
93 }
94 
95 void AddDirectory(const std::string& path, const std::string& outpath)
96 {
97     struct dirent* pDirent = nullptr;
98     DIR* pDir = opendir(path.c_str());
99     if (pDir == nullptr) {
100         printf("Cannot open directory '%s'\n", path.c_str());
101         exit(1);
102     }
103     std::string p, op;
104     p = path;
105     if (!p.empty()) {
106         if (p.back() != '/') {
107             p += "/";
108         }
109     }
110     op = outpath;
111     if (!op.empty()) {
112         if (op.back() != '/') {
113             op += "/";
114         }
115     }
116 
117     // sort the readdir result
118     auto alphaSort = [](dirent* x, dirent* y) { return std::string(x->d_name) < std::string(y->d_name); };
119     auto dirSet = std::set<dirent*, decltype(alphaSort)>(alphaSort);
120 
121     while ((pDirent = readdir(pDir)) != nullptr) {
122         // This structure may be statically allocated
123         dirSet.insert(pDirent);
124     }
125 
126     for (auto &d : dirSet) {
127         if (d->d_type == DT_DIR) {
128             if (d->d_name[0] == '.') {
129                 continue;
130             }
131             AddDirectory(p + d->d_name, op + d->d_name);
132             continue;
133         }
134         AppendFile(p + d->d_name, op + d->d_name);
135     }
136 
137     closedir(pDir);
138 }
139 /*
140  *   //Pseudo code for accessing files in blob
141  *   struct fs_entry
142  *   {
143  *       char fname[256];
144  *       uint64_t offset;
145  *       uint64_t size;
146  *   };
147  *   extern "C" uint64_t SizeOfDataForReadOnlyFileSystem;
148  *   extern "C" struct fs_entry BinaryDataForReadOnlyFileSystem[];
149  *   void dump_files()
150  *   {
151  *       for (int i = 0; i < SizeOfDataForReadOnlyFileSystem; i++)
152  *       {
153  *           if (BinaryDataForReadOnlyFileSystem[i].fname[0] == 0) break;
154  *           printf("%s\n", BinaryDataForReadOnlyFileSystem[i].fname);
155  *           char* data = (char*)(BinaryDataForReadOnlyFileSystem[i].offset +
156  * (uintptr_t)BinaryDataForReadOnlyFileSystem); printf("%lld\n", BinaryDataForReadOnlyFileSystem[i].offset);
157  *       }
158  *   }
159  */
160 
161 std::string g_dataName = "BinaryDataForReadOnlyFileSystem";
162 std::string g_sizeName = "SizeOfDataForReadOnlyFileSystem";
163 
164 void WriteObj(const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data, bool x64)
165 {
166     size_t sizeOfSection = sizeof(uint64_t) + sizeOfData;
167 #pragma pack(push, 1)
168     // Using headers and defines from winnt.h
169     struct ObjFile {
170         IMAGE_FILE_HEADER coffHead;
171         IMAGE_SECTION_HEADER sections[1];
172         IMAGE_SYMBOL symtab[2];
173     } obj{};
174 #pragma pack(pop)
175 
176     // fill coff header.
177     if (!x64) {
178         obj.coffHead.Machine = IMAGE_FILE_MACHINE_I386;
179     } else {
180         obj.coffHead.Machine = IMAGE_FILE_MACHINE_AMD64;
181     }
182     obj.coffHead.NumberOfSections = sizeof(obj.sections) / sizeof(IMAGE_SECTION_HEADER);
183     obj.coffHead.TimeDateStamp = 0; // duh.
184     obj.coffHead.PointerToSymbolTable = offsetof(decltype(obj), symtab);
185     obj.coffHead.NumberOfSymbols = sizeof(obj.symtab) / sizeof(IMAGE_SYMBOL);
186     obj.coffHead.SizeOfOptionalHeader = 0;
187     obj.coffHead.Characteristics = 0; // if x86 use IMAGE_FILE_32BIT_MACHINE ?
188 
189     // create stringtable
190     char stringtable[256]{ 0 };
191     char* dst = stringtable;
192     auto addString = [&dst, &stringtable](std::string_view a) -> Elf32_Word {
193         const auto offset = dst - stringtable;
194         dst += a.copy(dst, 256u - offset);
195         *dst++ = '\0';
196         return static_cast<uint32_t>(offset + 4);
197     };
198     if (!x64) {
199         // in win32 the symbols have extra "_" ?
200         std::string t = "_";
201         t += g_dataName;
202         std::string t2 = "_";
203         t2 += g_sizeName;
204         obj.symtab[1].N.Name.Long = addString(t.c_str() /*"BinaryDataForReadOnlyFileSystem"*/);
205         obj.symtab[0].N.Name.Long = addString(t2.c_str() /*"SizeOfDataForReadOnlyFileSystem"*/);
206     } else {
207         obj.symtab[1].N.Name.Long = addString(g_dataName.c_str() /*"BinaryDataForReadOnlyFileSystem"*/);
208         obj.symtab[0].N.Name.Long = addString(g_sizeName.c_str() /*"SizeOfDataForReadOnlyFileSystem"*/);
209     }
210     uint32_t stringTableSize = static_cast<uint32_t>((dst - stringtable) + 4);
211 
212     // fill the section.
213     std::copy(secname.c_str(), secname.c_str() + secname.size(), &obj.sections[0].Name[0]);
214     obj.sections[0].Name[secname.size()] = '\0';
215     obj.sections[0].Misc.VirtualSize = 0;
216     obj.sections[0].VirtualAddress = 0;
217     obj.sections[0].SizeOfRawData = static_cast<uint32_t>(sizeOfSection); // sizeof the data on disk.
218     obj.sections[0].PointerToRawData =
219         ((sizeof(obj) + stringTableSize + 3u) / 4u) * 4u; // DWORD align the data directly after the headers..
220     obj.sections[0].PointerToLinenumbers = 0;
221     obj.sections[0].NumberOfRelocations = 0;
222     obj.sections[0].NumberOfLinenumbers = 0;
223     obj.sections[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
224     // fill symbols
225     obj.symtab[1].Value = static_cast<uint32_t>(sizeof(uint64_t));
226     obj.symtab[1].SectionNumber = 1; // first section.. (one based)
227     obj.symtab[1].Type = IMAGE_SYM_TYPE_CHAR | (IMAGE_SYM_DTYPE_ARRAY << 8);
228     obj.symtab[1].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
229     obj.symtab[1].NumberOfAuxSymbols = 0;
230 
231     obj.symtab[0].Value = static_cast<uint32_t>(0u);
232     obj.symtab[0].SectionNumber = 1; // first section.. (one based)
233     // obj.symtab[0].Type = IMAGE_SYM_TYPE_UINT;  //(just use IMAGE_SYM_TYPE_NULL like mstools?)
234     obj.symtab[0].StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
235     obj.symtab[0].NumberOfAuxSymbols = 0;
236 
237     FILE* d = fopen(fname.c_str(), "wb");
238     if (d == nullptr) {
239         printf("Could not open %s.\n", fname.c_str());
240         exit(-1);
241     }
242     // write headers
243     fwrite(&obj, sizeof(obj), 1u, d);
244     // write string table
245     fwrite(&stringTableSize, 1, sizeof(stringTableSize), d);
246     fwrite(stringtable, 1, stringTableSize - 4, d);
247     // write sections..
248     size_t p = ftell(d);
249     uint32_t pad = 0;
250     size_t padcount = obj.sections[0].PointerToRawData - p;
251     fwrite(&pad, padcount, 1u, d);
252     fwrite(data, sizeOfSection, 1u, d);
253     fclose(d);
254 #undef addString
255 }
256 
257 template<class type>
258 void WriteElf(
259     type o, uint8_t arch, const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data)
260 {
261     size_t sizeOfSection = sizeOfData + sizeof(uint64_t);
262     char stringtable[256];
263     o.head.e_type = ET_REL;
264     o.head.e_machine = arch; // machine id..
265     o.head.e_version = EV_CURRENT;
266     o.head.e_ehsize = sizeof(o.head);
267     o.head.e_shentsize = sizeof(o.sections[0]);
268     o.head.e_shnum = sizeof(o.sections) / sizeof(o.sections[0]);
269     o.head.e_shoff = sizeof(o.head);
270     o.head.e_shstrndx = 1;
271 
272     // initialize stringtable.
273     char* dst = stringtable;
274     *dst = 0;
275     dst++;
276     auto addString = [&dst, &stringtable](std::string_view a) -> Elf32_Word {
277         const auto offset = dst - stringtable;
278         dst += a.copy(dst, 256u - offset);
279         *dst++ = '\0';
280         return static_cast<Elf32_Word>(offset);
281     };
282 
283     // create symbols
284     o.symbs[2].st_name = addString(g_dataName); // ?BinaryDataForReadOnlyFileSystem@@3PADA");
285     o.symbs[2].st_value = sizeof(uint64_t);
286     o.symbs[2].st_size = static_cast<Elf32_Word>(sizeOfData);
287     o.symbs[2].st_info = o.symbs[1].st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
288     o.symbs[2].st_other = o.symbs[1].st_other = STV_HIDDEN;
289     o.symbs[2].st_shndx = o.symbs[1].st_shndx = 3;
290 
291     o.symbs[1].st_name = addString(g_sizeName);
292     o.symbs[1].st_value = 0;
293     o.symbs[1].st_size = sizeof(uint64_t);
294 
295     o.sections[2].sh_name = addString(".symtab");
296     o.sections[2].sh_type = SHT_SYMTAB;
297     o.sections[2].sh_offset = offsetof(decltype(o), symbs); // sizeof(o) + sizeOfSection + stringtable_size;
298     o.sections[2].sh_addralign = 8;
299     o.sections[2].sh_size = sizeof(o.symbs);
300     o.sections[2].sh_entsize = sizeof(o.symbs[0]);
301     o.sections[2].sh_link = 1;
302     o.sections[2].sh_info = 1; // index of first non-local symbol.
303 
304     std::string tmp = ".rodata.";
305     tmp += secname;
306     /*sec = ".rodata.rofs"*/
307     o.sections[3].sh_name = addString(tmp);
308     o.sections[3].sh_type = SHT_PROGBITS;
309     o.sections[3].sh_flags = SHF_ALLOC | SHF_MERGE;
310     o.sections[3].sh_offset = sizeof(o);
311     o.sections[3].sh_addralign = 8;
312     o.sections[3].sh_size = static_cast<Elf32_Word>(sizeOfSection);
313 
314     o.sections[1].sh_name = addString(".strtab");
315     o.sections[1].sh_type = SHT_STRTAB;
316     o.sections[1].sh_offset = static_cast<Elf32_Off>(sizeof(o) + sizeOfSection);
317     o.sections[1].sh_addralign = 1;
318     o.sections[1].sh_size = static_cast<Elf32_Word>(dst - stringtable);
319 
320     FILE* e = fopen(fname.c_str(), "wb");
321     if (e == nullptr) {
322         printf("Could not open %s.\n", fname.c_str());
323         exit(-1);
324     }
325     fwrite(&o, sizeof(o), 1, e);
326     fwrite(data, sizeOfSection, 1, e);
327     fwrite(stringtable, static_cast<size_t>(o.sections[1].sh_size), 1, e);
328     fclose(e);
329 }
330 
331 void WriteMacho(const std::string& fname, const std::string& secname, size_t sizeOfData, const void* data)
332 {
333 #ifdef __APPLE__
334     // Write fat header for X64_64 and ARM64 object
335     struct fat_header fathdr;
336     fathdr.magic = FAT_CIGAM;
337     fathdr.nfat_arch = htonl(2); // big-endian values in fat header
338 
339     struct fat_arch archs[2] = {
340         {
341             htonl(CPU_TYPE_X86_64),
342             htonl(CPU_SUBTYPE_X86_64_ALL),
343             0,
344             0, // size of data
345             htonl(3)
346         },
347         {
348             htonl(CPU_TYPE_ARM64),
349             htonl(CPU_SUBTYPE_ARM64_ALL),
350             0, // offset,
351             0, // size of data
352             htonl(3)
353         },
354     };
355 
356     size_t fpos = 0; // "file" offsets are actually relative to the architecture header
357     archs[0].offset = htonl(sizeof(fathdr) + sizeof(archs));
358 
359     size_t sizeOfSection = sizeOfData + sizeof(uint64_t);
360 
361     struct mach_header_64 x64_header = {
362         MH_MAGIC_64,
363         CPU_TYPE_X86_64,
364         CPU_SUBTYPE_X86_64_ALL,
365         MH_OBJECT,
366         2, // ncmds
367         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
368         0, // flags
369         0  // reserved
370     };
371 
372     struct mach_header_64 arm64_header = {
373         MH_MAGIC_64,
374         CPU_TYPE_ARM64,
375         CPU_SUBTYPE_ARM64_ALL,
376         MH_OBJECT,
377         2, // ncmds
378         sizeof(segment_command_64) + sizeof(section_64) + sizeof(symtab_command), // sizeofcmds
379         0, // flags
380         0  // reserved
381     };
382 
383     struct segment_command_64 data_seg = {
384         LC_SEGMENT_64,
385         sizeof(data_seg) + sizeof(section_64),
386         "", // for object files name is empty
387         0, // vmaddress
388         (sizeOfSection + 7) & ~7, // vmsize aligned to 8 bytes
389         0, // fileoffset
390         sizeOfSection, // filesize
391         VM_PROT_READ, // maxprot
392         VM_PROT_READ, // initprot
393         1, // nsects
394         SG_NORELOC  // flags
395     };
396 
397     struct section_64 data_sect = {
398         "__const",
399         "__DATA",
400         0, // addr
401         sizeOfSection, // vmsize aligned to 8 bytes
402         0, // offset
403         3, // alignment = 2^3 = 8 bytes
404         0, // reloff
405         0, // nreloc
406         S_REGULAR, // flags
407         0, // reserved1
408         0, // reserved2
409     };
410 
411     std::string dataName = "_";
412     dataName += g_dataName;
413     std::string sizeName = "_";
414     sizeName += g_sizeName;
415 
416     uint32_t string_size = dataName.size() + sizeName.size() + 3; // prepending plus two terminating nulls
417 
418     struct symtab_command symtab = {
419         LC_SYMTAB,
420         sizeof(symtab),
421         0, // symoff
422         2, // nsyms
423         0, // stroff
424         string_size, // strsize
425     };
426 
427     fpos += sizeof(x64_header) + sizeof(data_seg) + sizeof(data_sect) + sizeof(symtab);
428 
429     data_seg.fileoff = static_cast<uint32_t>(fpos);
430     data_sect.offset = static_cast<uint32_t>(fpos);
431     fpos += sizeOfSection;
432 
433     size_t sectionAligned = (fpos + 7) & ~7;
434     uint32_t sectionAlign = sectionAligned - fpos;
435     fpos = sectionAligned;
436 
437     symtab.symoff = static_cast<uint32_t>(fpos);
438 
439     struct nlist_64 syms[2] = {
440     {
441         1, // first string
442         N_EXT | N_SECT,
443         1, // segment
444         REFERENCE_FLAG_DEFINED,
445         0
446     },
447     {
448         static_cast<uint32_t>(sizeName.size() + 2), // second string
449         N_EXT | N_SECT,
450         1, // segment
451         REFERENCE_FLAG_DEFINED,
452         8
453     }
454     };
455 
456     fpos += sizeof(syms);
457 
458     symtab.stroff = static_cast<uint32_t>(fpos);
459     fpos += string_size;
460 
461     archs[0].size = htonl(fpos);
462 
463     size_t aligned = (fpos + 7) & ~7;
464     uint32_t align = aligned - fpos;
465     archs[1].offset = htonl(aligned + sizeof(fathdr) + sizeof(archs));
466     archs[1].size = archs[0].size;
467 
468     FILE* e = fopen(fname.c_str(), "wb");
469     if (e == NULL) {
470         printf("Could not open %s.\n", fname.c_str());
471         exit(-1);
472     }
473 
474     fwrite(&fathdr, sizeof(fathdr), 1, e);
475     fwrite(&archs, sizeof(archs), 1, e);
476     fwrite(&x64_header, sizeof(x64_header), 1, e);
477     fwrite(&data_seg, sizeof(data_seg), 1, e);
478     fwrite(&data_sect, sizeof(data_sect), 1, e);
479     fwrite(&symtab, sizeof(symtab), 1, e);
480     fwrite(data, sizeOfSection, 1, e);
481     for (int i = 0; i < sectionAlign; i++) {
482         fputc(0, e); // alignment byte to begin string table 8 byte boundary
483     }
484     fwrite(&syms, sizeof(syms), 1, e);
485     fputc(0, e); // filler byte to begin string table as it starts indexing at 1
486     fwrite(sizeName.c_str(), sizeName.size() + 1, 1, e);
487     fwrite(dataName.c_str(), dataName.size() + 1, 1, e);
488 
489     for (int i = 0; i < align; i++) {
490         fputc(0, e); // alignment byte to begin arm64 architecture at 8 byte boundary
491     }
492 
493     fwrite(&arm64_header, sizeof(arm64_header), 1, e);
494     fwrite(&data_seg, sizeof(data_seg), 1, e);
495     fwrite(&data_sect, sizeof(data_sect), 1, e);
496     fwrite(&symtab, sizeof(symtab), 1, e);
497     fwrite(data, sizeOfSection, 1, e);
498     for (int i = 0; i < sectionAlign; i++) {
499         fputc(0, e); // alignment byte to begin string table 8 byte boundary
500     }
501     fwrite(&syms, sizeof(syms), 1, e);
502     fputc(0, e); // filler byte to begin string table
503     fwrite(sizeName.c_str(), sizeName.size() + 1, 1, e);
504     fwrite(dataName.c_str(), dataName.size() + 1, 1, e);
505     fclose(e);
506 #endif
507 }
508 
509 int main(int argc, char* argv[])
510 {
511     std::string obj32Name = "rofs_32.obj";
512     std::string obj64Name = "rofs_64.obj";
513     std::string o32Name = "rofs_32.o";
514     std::string o64Name = "rofs_64.o";
515     std::string x32Name = "rofs_x86.o";
516     std::string x64Name = "rofs_x64.o";
517     std::string macName = "rofs_mac.o";
518     std::string secName = "rofs";
519 
520     bool buildX86 = true, buildX64 = true, buildV7 = true, buildV8 = true;
521     bool buildWindows = true, buildAndroid = true, buildMac = true;
522 
523     std::string inPath = { "" }, roPath = { "" };
524 
525     int baseArg = 0;
526     if (argc >= 2) {
527         if (argv[1][0] == '-') {
528             buildAndroid = false;
529             buildWindows = false;
530             buildMac = false;
531             buildX86 = buildX64 = buildV7 = buildV8 = false;
532         }
533         bool platset = false;
534         for (;;) {
535             if (baseArg + 1 >= argc) {
536                 printf("Invalid argument!\n");
537                 return 0;
538             }
539             if (argv[baseArg + 1][0] != '-')
540                 break;
541             if (strcmp(argv[baseArg + 1], "-linux") == 0) {
542                 platset = true;
543                 buildAndroid = true;
544                 baseArg++;
545             } else if (strcmp(argv[baseArg + 1], "-android") == 0) {
546                 platset = true;
547                 buildAndroid = true;
548                 baseArg++;
549             } else if (strcmp(argv[baseArg + 1], "-windows") == 0) {
550                 platset = true;
551                 buildWindows = true;
552                 baseArg++;
553             } else if (strcmp(argv[baseArg + 1], "-mac") == 0) {
554                 platset = true;
555                 buildMac = true;
556                 baseArg++;
557             } else if (strcmp(argv[baseArg + 1], "-x86") == 0) {
558                 buildX86 = true;
559                 baseArg++;
560             } else if (strcmp(argv[baseArg + 1], "-x86_64") == 0) {
561                 buildAndroid = true;
562                 buildX64 = true;
563                 baseArg++;
564             } else if (strcmp(argv[baseArg + 1], "-x64") == 0) {
565                 buildX64 = true;
566                 buildWindows = true;
567                 baseArg++;
568             } else if (strcmp(argv[baseArg + 1], "-armeabi-v7a") == 0) {
569                 buildV7 = true;
570                 buildAndroid = true;
571                 platset = true;
572                 baseArg++;
573             } else if (strcmp(argv[baseArg + 1], "-arm64-v8a") == 0) {
574                 buildV8 = true;
575                 buildAndroid = true;
576                 platset = true;
577                 baseArg++;
578             } else if (strcmp(argv[baseArg + 1], "-extensions") == 0) {
579                 baseArg++;
580                 if (baseArg + 1 >= argc) {
581                     printf("Invalid argument!\n");
582                     return 0;
583                 }
584                 auto exts = std::string_view(argv[baseArg + 1]);
585                 while (!exts.empty()) {
586                     auto pos = exts.find(';');
587                     pos = std::min(pos, exts.size());
588                     g_validExts.push_back(exts.substr(0, pos));
589                     exts.remove_prefix(std::min(pos + 1, exts.size()));
590                 }
591                 baseArg++;
592             } else {
593                 printf("Invalid argument!\n");
594                 return 0;
595             }
596         }
597         if (!platset) {
598             buildWindows = true;
599             buildAndroid = true;
600             buildMac = true;
601         }
602 
603         if (argc < baseArg + 3) {
604             printf("invalid args");
605             return 0;
606         }
607 
608         inPath = argv[baseArg + 1];
609     } else {
610         printf("Not enough arguments!\n");
611         return 0;
612     }
613     if (argc >= baseArg + 3) {
614         if (argv[baseArg + 2][0] == '/') {
615             roPath = argv[baseArg + 2] + 1;
616         } else {
617             roPath = argv[baseArg + 2];
618         }
619     }
620     if (argc >= baseArg + 5) {
621         g_dataName = argv[baseArg + 3];
622         g_sizeName = argv[baseArg + 4];
623     }
624     if (argc == baseArg + 6) {
625         secName = obj32Name = obj64Name = o32Name = o64Name = x32Name = x64Name = macName = argv[baseArg + 5];
626         obj32Name += "_32.obj";
627         obj64Name += "_64.obj";
628         o32Name += "_32.o";
629         o64Name += "_64.o";
630         x32Name += "_x86.o";
631         x64Name += "_x64.o";
632         macName += "_mac.o";
633     }
634     AddDirectory(inPath, roPath);
635 
636     // fix offsets
637     size_t baseOffset = sizeof(FsEntry) * (g_directory.size() + 1);
638     for (auto& d : g_directory) {
639         d.offset += baseOffset;
640     }
641 
642     // add terminator
643     g_directory.push_back({ { 0 }, 0, 0 });
644 
645     const size_t sizeOfDir = (sizeof(FsEntry) * g_directory.size());
646     const size_t sizeOfData = g_bin.size() + sizeOfDir;
647     auto data = std::make_unique<uint8_t[]>(sizeOfData + sizeof(uint64_t));
648     *reinterpret_cast<uint64_t*>(data.get()) = sizeOfData;
649     std::copy(g_directory.cbegin(), g_directory.cend(), reinterpret_cast<FsEntry*>(data.get() + sizeof(uint64_t)));
650     std::copy(g_bin.cbegin(), g_bin.cend(), data.get() + sizeof(uint64_t) + sizeOfDir);
651     // Build obj
652     if (buildWindows) {
653         if (buildX86) {
654             WriteObj(obj32Name, secName, sizeOfData, data.get(), false);
655         }
656         if (buildX64) {
657             WriteObj(obj64Name, secName, sizeOfData, data.get(), true);
658         }
659     }
660     // Create .elf (.o)
661     if (buildAndroid) {
662         struct {
663             Elf32_Ehdr head{};
664             Elf32_Shdr sections[4]{};
665             Elf32_Sym symbs[3]{};
666         } o32;
667         struct {
668             Elf64_Ehdr head{};
669             Elf64_Shdr sections[4]{};
670             Elf64_Sym symbs[3]{};
671         } o64;
672         if (buildV7) {
673             WriteElf(o32, EM_ARM, o32Name, secName, sizeOfData, data.get());
674         }
675         if (buildV8) {
676             WriteElf(o64, EM_AARCH64, o64Name, secName, sizeOfData, data.get());
677         }
678         if (buildX86) {
679             WriteElf(o32, EM_386, x32Name, secName, sizeOfData, data.get());
680         }
681         if (buildX64) {
682             WriteElf(o64, EM_X86_64, x64Name, secName, sizeOfData, data.get());
683         }
684     }
685     // Create mach-o (.o)
686     if (buildMac) {
687         WriteMacho(macName, secName, sizeOfData, data.get());
688     }
689 }
690