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