/*
 * meg4/bin2h.c
 *
 * Copyright (C) 2023 bzt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * @brief Small utility to convert binary to C header, and do compile-time conversions
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBI_WRITE_ONLY_PNG
#define STBI_WRITE_NO_FAILURE_STRINGS
#define STBI_WRITE_NO_SIMD
#define STBI_WRITE_NO_STDIO
#define STBIW_ASSERT(x)
#include "stb_image_write.h"
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_FAILURE_STRINGS
#define STBI_NO_SIMD
#define STBI_NO_STDIO
#define STBI_ASSERT(x)
#include "stb_image.h"
#define sizeof_binary_game 589824

int filesort(const void *a, const void *b) { return strcmp(*((char**)a), *(char**)b); }

int main(int argc, char **argv)
{
    DIR *dir;
    struct dirent *ent;
    FILE *f, *c, *h = NULL;
    unsigned int pal[256], theme[256], col;
    unsigned long int size, len;
    unsigned char *buff = NULL, *buff2 = NULL, *ptr;
    char p[255], *fn, *md = NULL, *s, *d, *par, *pt, *api = NULL, *dis = NULL, *defs = NULL, **files;
    int x, y, W, H, i, j, k, l, m, n, o, r, q, a, pa, pp, ps, pf, pu, prt = 0, inp = 0, ext = 0, out = 0, cpy = 0, ati = 0, val = 0;
    int file = 1, nmd = 0, napi = 0, nbd = 0;

    if(argc < 2){
        printf("bin2h <bin file> [bin file2...]\r\n");
        exit(1);
    }

    if(!strcmp(argv[1], "-o")) {
        c = fopen(argv[2], "w"); if(!c) { fprintf(stderr, "bin2h: unable to open %s\r\n", argv[2]); return 1; }
        file = 3;
    } else {
        c = fopen("data.c", "w"); if(!c) { fprintf(stderr, "bin2h: unable to open data.c\r\n"); return 1; }
        h = fopen("data.h", "w"); if(!h) { fprintf(stderr, "bin2h: unable to open data.h\r\n"); return 1; }
        fprintf(h, "/* generated by bin2h, do not edit */\n\n");
    }
    fprintf(c, "/* generated by bin2h, do not edit */\n\n");
    for(; file < argc; file++){
        size = 0;
        if(!strcmp(argv[file], "game")) {
            if(h) fprintf(h, "extern unsigned char binary_game[%u];\n", sizeof_binary_game);
            fprintf(c, "unsigned char binary_game[%u] = { 'M','E','G','4','G','A','M','E'", sizeof_binary_game);
            /* we must fill it with garbage because of stupid emscripten; we will clean up after compilation */
            for(i = 0; i < sizeof_binary_game - 8; i++) fprintf(c, ",%u", i & 7);
            fprintf(c, " };\n");
            continue;
        }
        f = fopen(argv[file],"rb");
        if(f) {
            fseek(f, 0, SEEK_END);
            size = ftell(f);
            fseek(f, 0, SEEK_SET);
            buff = (unsigned char*)malloc(size + 1);
            if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
            fread(buff, 1, size, f);
            fclose(f);
            fn = strrchr(argv[file], '/');
            if(!fn) fn = strrchr(argv[file], '\\');
            if(!fn) fn = argv[file]; else fn++;
            for(i = 0; fn[i]; i++)
                p[i] = fn[i] == '.' || fn[i] <= ' ' ? '_' : fn[i];
            p[i] = 0;
            if(!strcmp(p, "memmap_md")) {
                f = fopen("meg4.h","rb");
                if(f) {
                    fseek(f, 0, SEEK_END);
                    len = ftell(f);
                    fseek(f, 0, SEEK_SET);
                    buff2 = (unsigned char*)malloc(len + 1);
                    if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                    fread(buff2, 1, len, f); buff2[len] = 0;
                    fclose(f);
                    pt = (char*)malloc(size);
                    if(!pt) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                    fn = strstr(buff, "@SCANCODES@");
                    k = size - ((uintptr_t)fn - (uintptr_t)buff) - 11; memcpy(pt, fn + 11, k);
                    size -= k + 11;
                    if((s = strstr(buff2, "MEG4_KEY_"))) {
                        defs = (char*)realloc(defs, len);
                        if(!defs) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                        buff = (char*)realloc(buff, size + len);
                        if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                        j = q = 0; nbd = 26;
                        /* special defines */
                        q += sprintf(defs + q, "    { \"__FUNC__\", 0 },\n");
                        q += sprintf(defs + q, "    { \"__LINE__\", 0 },\n");
                        q += sprintf(defs + q, "    { \"__MEG4__\", 1 },\n");
                        /* not in meg4.h, but scripts need these */
                        q += sprintf(defs + q, "    { \"NULL\",     0 },\n");
                        q += sprintf(defs + q, "    { \"false\",    0 },\n");
                        q += sprintf(defs + q, "    { \"true\",     1 },\n");
                        /* not specified by enums */
                        q += sprintf(defs + q, "    { \"MEM_USER\", 0x30000 },\n");
                        q += sprintf(defs + q, "    { \"MEM_LIMIT\", 0xC0000 },\n");
                        q += sprintf(defs + q, "    { \"PTR_NORM\", 0x03fb },\n");
                        q += sprintf(defs + q, "    { \"PTR_TEXT\", 0x93fc },\n");
                        q += sprintf(defs + q, "    { \"PTR_HAND\", 0x0bfd },\n");
                        q += sprintf(defs + q, "    { \"PTR_ERR\",  0x93fe },\n");
                        q += sprintf(defs + q, "    { \"PTR_NONE\", 0xffff },\n");
                        q += sprintf(defs + q, "    { \"BTN_L\", 1 },\n");
                        q += sprintf(defs + q, "    { \"BTN_M\", 2 },\n");
                        q += sprintf(defs + q, "    { \"BTN_U\", 2 },\n");
                        q += sprintf(defs + q, "    { \"BTN_R\", 4 },\n");
                        q += sprintf(defs + q, "    { \"BTN_D\", 8 },\n");
                        q += sprintf(defs + q, "    { \"SCR_U\", 8 },\n");
                        q += sprintf(defs + q, "    { \"BTN_A\", 16 },\n");
                        q += sprintf(defs + q, "    { \"SCR_D\", 16 },\n");
                        q += sprintf(defs + q, "    { \"BTN_B\", 32 },\n");
                        q += sprintf(defs + q, "    { \"SCR_L\", 32 },\n");
                        q += sprintf(defs + q, "    { \"BTN_X\", 64 },\n");
                        q += sprintf(defs + q, "    { \"SCR_R\", 64 },\n");
                        q += sprintf(defs + q, "    { \"BTN_Y\", 128 },\n");
                        /* dynamically query all the rest */
                        while(*s && s < (char*)buff2 + len) {
                            while(*s == ',' || *s == ' ' || *s == '\r' || *s == '\n') s++;
                            if(s[0] == '/' && s[1] == '*') { s += 2; while(*s && s < (char*)buff2 + len && memcmp(s, "*/", 2)) { s++; } s += 2; continue; }
                            if(!memcmp(s, "MEG4_KEY_", 9)) {
                                q += sprintf(defs + q, "    { \"");
                                size += sprintf(buff + size, "| %8u |   %05X | %7u | `", j, 0x5c + (j >> 3), 1<<(j & 7));
                                s += 5; m = size;
                                while(*s && s < (char*)buff2 + len && *s != ',' && *s != ' ' && *s != '\r' && *s != '\n') {
                                    defs[q++] = *s;
                                    buff[size++] = *s++;
                                }
                                buff[size++] = '`'; while(size - m < 16) buff[size++] = ' ';
                                memcpy(buff + size, " |\n", 3); size += 3;
                                q += sprintf(defs + q, "\", %u },\n", j);
                                j++; nbd++;
                            } else break;
                        }
                        if((s = strstr(buff2, "MEG4_NOTE_"))) {
                            j = 0;
                            while (*s && s < (char*)buff2 + len) {
                                while(*s == ',' || *s == ' ' || *s == '\r' || *s == '\n') s++;
                                if(s[0] == '/' && s[1] == '*') { s += 2; while(*s && s < (char*)buff2 + len && memcmp(s, "*/", 2)) { s++; } s += 2; continue; }
                                if(!memcmp(s, "MEG4_NOTE_", 9)) {
                                    q += sprintf(defs + q, "    { \"");
                                    s += 5;
                                    while(*s && s < (char*)buff2 + len && *s != ',' && *s != ' ' && *s != '\r' && *s != '\n') defs[q++] = *s++;
                                    q += sprintf(defs + q, "\", %u },\n", j);
                                    j++; nbd++;
                                } else break;
                            }
                        }
                    }
                    memcpy(buff + size, pt, k); size += k;
                    free(pt);
                    free(buff2);
                }
                if((dir = opendir("."))) {
                    files = NULL; j = napi = m = n = 0;
                    while((ent = readdir(dir))) {
                        if(ent->d_name[0] == '.' || !strcmp(ent->d_name, "bin2h.c") || strcmp(ent->d_name + strlen(ent->d_name) - 2, ".c")) continue;
                        files = (char**)realloc(files, (j + 1) * sizeof(char*));
                        if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                        files[j] = (char*)malloc(strlen(ent->d_name) + 1);
                        if(!files[j]) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                        strcpy(files[j], ent->d_name);
                        j++;
                    }
                    closedir(dir);
                    qsort(files, j, sizeof(char*), filesort);
                    for(i = 0; i < j; i++) {
                        f = fopen(files[i],"rb");
                        if(f) {
                            fseek(f, 0, SEEK_END);
                            len = ftell(f);
                            fseek(f, 0, SEEK_SET);
                            buff2 = (unsigned char*)malloc(len + 1);
                            if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                            fread(buff2, 1, len, f); buff2[len] = 0;
                            fclose(f);
                            if((s = strstr(buff2, "@""chapter"))) {
                                s += 9; for(d = s; *d && *d != '\r' && *d != '\n'; d++);
                                buff = (char*)realloc(buff, size + len + 1024);
                                if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                                memcpy(buff + size, "\n# ", 3); size += 3;
                                memcpy(buff + size, s, d - s); size += d - s;
                                buff[size++] = '\n'; k = 0;
                                while(d < (char*)buff2 + len && (d = strstr(d + 1, "/**"))) {
                                    s = strstr(d, "*/"); for(s += 2; *s == '\r' || *s == '\n'; s++);
                                    pt = s; while(*s && *s != ' ') { s++; } while(*s == ' ') s++;
                                    if(!memcmp(s, "meg4_api_", 9)) {
                                        api = (char*)realloc(api, m + 256);
                                        if(!api) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                                        dis = (char*)realloc(dis, n + 1024);
                                        if(!dis) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                                        s += 9; if(k) { memcpy(buff + size, "<hr>", 4); size += 4; }
                                        memcpy(buff + size, "\n## ", 4); size += 4; k++;
                                        r = !memcmp(pt, "void", 4) && pt[4] != '*' && pt[5] != '*' ? 0 :
                                            (!memcmp(pt, "addr_t", 6) ? 2 : ((!memcmp(pt, "str_t", 5) ? 3 :
                                            (!memcmp(pt, "float", 5) ? 4 : 1))));
                                        memcpy(api + m, "    { \"", 7); m += 7;
                                        n += sprintf(dis + n, "    case %2u: %smeg4_api_", napi, r == 4 ? "fval = " : (r ? " val = " : ""));
                                        if(!memcmp(s, "print", 5)) prt = napi;
                                        if(!memcmp(s, "gets", 4)) inp = napi;
                                        if(!memcmp(s, "exit", 4)) ext = napi;
                                        if(!memcmp(s, "outb", 4)) out = napi;
                                        if(!memcmp(s, "memcpy", 6)) cpy = napi;
                                        if(!memcmp(s, "atoi", 4)) ati = napi;
                                        if(!memcmp(s, "val", 3)) val = napi;
                                        while(*s && *s != ' ' && *s != '(') { buff[size++] = *s++; }
                                        /*memcpy(api + m, "\", ", 3); m += 3;*/
                                        memcpy(buff + size, "\n\n```c\n", 7); size += 7;
                                        while(*pt != ' ') buff[size++] = *pt++;
                                        buff[size++] = ' '; while(*pt == ' ') pt++;
                                        pt += 9; for(l = 0; pt[l] && pt[l] != '('; l++) api[m++] = pt[l];
                                        while(pt[l] == ' ') l++;
                                        a = !memcmp(pt + l, "()", 2) || !memcmp(pt + l, "(void)", 6) ? 0 : 1;
                                        pa = pp = ps = pf = pu = 0; o = 1;
                                        while(*pt && *pt != ')') {
                                            if(a && a < 16) {
                                                if(!memcmp(pt, "...", 3)) pa = a;
                                                if(!memcmp(pt, "addr_t ", 7)) pp |= (1<<(a - 1));
                                                if(!memcmp(pt, "str_t ", 6)) ps |= (1<<(a - 1));
                                                if(!memcmp(pt, "float ", 6)) pf |= (1<<(a - 1));
                                                if(!memcmp(pt, "uint", 4)) pu |= (1<<(a - 1));
                                            }
                                            buff[size++] = *pt;
                                            if(*pt == '(') { m += sprintf(api + m, "\", %u", r); dis[n++] = '('; o = memcmp(pt + 1, "void)", 5); }
                                            if(o && *pt != '\r' && *pt != '\n' && *pt != ' ') { dis[n++] = *pt; o = 1; }
                                            if(o == 1 && *pt == ' ' && pt[-1] != ',') { n += sprintf(dis + n, ")cpu_top%c(%u)",
                                                    pf & (1<<(a - 1)) ? 'f' : 'i', (a - 1) << 2); o = 0; }
                                            if(*pt == ',') {
                                                a++;
                                                if(memcmp(pt + 1, "...", 3) && memcmp(pt + 2, "...", 3)) { dis[n++] = ','; dis[n++] = '('; o = 2; }
                                            }
                                            *pt++;
                                        }
                                        m += sprintf(api + m, ", %u, %u, 0x%x, 0x%x, 0x%x, 0x%x },\n", a, pa, (pp | ps), ps, pf, pu);
                                        if(o && a) n += sprintf(dis + n, ")cpu_top%c(%u)", pf & (1<<(a - 1)) ? 'f' : (ps & (1<<(a - 1)) ? 's' : 'i'),
                                            (a - 1) << 2);
                                        n += sprintf(dis + n, "); break; \\\n");
                                        napi++;
                                        memcpy(buff + size, ")\n```\n<dl>\n<dt>Description</dt><dd>\n", 36); size += 36;
                                        d = strstr(d, " * ") + 3;
                                        while(*d && *d != '@' && memcmp(d, "*/", 2)) {
                                            while(*d && *d != '\r' && *d != '\n') buff[size++] = *d++;
                                            buff[size++] = '\n';
                                            while(*d == '\r' || *d == '\n' || *d == ' ') d++;
                                            if(!memcmp(d, "* ", 2)) d += 2;
                                        }
                                        memcpy(buff + size, "</dd>\n", 6); size += 6;
                                        if(!memcmp(d, "@param", 6)) {
                                            memcpy(buff + size, "<dt>Parameters</dt><dd>\n| Argument | Description |\n", 51); size += 51;
                                            while(!memcmp(d, "@param", 6)) {
                                                d += 7; buff[size++] = '|'; buff[size++] = ' ';
                                                while(*d && *d != ' ') buff[size++] = *d++;
                                                while(*d == ' ') d++;
                                                memcpy(buff + size, " | ", 3); size += 3;
                                                while(*d && *d != '\r' && *d != '\n') buff[size++] = *d++;
                                                memcpy(buff + size, " |\n", 3); size += 3;
                                                while(*d == '\r' || *d == '\n' || *d == ' ') d++;
                                                if(!memcmp(d, "* ", 2)) d += 2;
                                            }
                                            memcpy(buff + size, "</dd>\n", 6); size += 6;
                                        }
                                        if(!memcmp(d, "@return", 7)) {
                                            d += 8; memcpy(buff + size, "<dt>Return Value</dt><dd>\n", 26); size += 26;
                                            while(*d && *d != '\r' && *d != '\n') buff[size++] = *d++;
                                            while(*d == '\r' || *d == '\n' || *d == ' ') d++;
                                            if(!memcmp(d, "* ", 2)) d += 2;
                                            memcpy(buff + size, "\n</dd>\n", 7); size += 7;
                                        }
                                        if(!memcmp(d, "@see", 4)) {
                                            d += 5; memcpy(buff + size, "<dt>See Also</dt><dd>\n", 22); size += 22;
                                            while(*d && *d != '\r' && *d != '\n') buff[size++] = *d++;
                                            while(*d == '\r' || *d == '\n' || *d == ' ') d++;
                                            if(!memcmp(d, "* ", 2)) d += 2;
                                            memcpy(buff + size, "\n</dd>\n", 7); size += 7;
                                        }
                                        memcpy(buff + size, "</dl>\n", 6); size += 6;
                                    }
                                }
                            }
                            free(buff2); buff2 = NULL;
                        }
                        free(files[i]);
                    }
                    free(files);
                }
                f = fopen("api.h", "wb");
                if(f) {
                    api[m] = dis[n - 2] = 0;
                    fprintf(f, "/*\n * meg4/api.h\n *\n * Generated by bin2h, DO NOT edit\n *\n"
                        " * @brief API function calls and built-in defines available to scripts\n *\n */\n\n"
                        "#define MEG4_NUM_API %u\n"
                        "#define MEG4_NUM_BDEF %u\n"
                        "#ifndef API_IMPL\n"
                        "extern meg4_api_t meg4_api[%u];\n"
                        "extern bdef_t meg4_bdefs[%u];\n"
                        "#else\n"
                        "meg4_api_t meg4_api[%u] = {\n%s    { NULL, 0, 0, 0, 0, 0, 0, 0 }\n};\n"
                        "#endif\n"
                        "#ifdef BDEF_IMPL\n"
                        "bdef_t meg4_bdefs[%u] = {\n%s    { NULL, 0 }\n};\n"
                        "#endif\n\n"
                        "#define MEG4_PRINT %u\n"
                        "#define MEG4_INPUT %u\n"
                        "#define MEG4_OUTB %u\n"
                        "#define MEG4_EXIT %u\n"
                        "#define MEG4_MEMCPY %u\n"
                        "#define MEG4_ATOI %u\n"
                        "#define MEG4_VAL %u\n"
                        "#define MEG4_DISPATCH \\\n%s\n", napi, nbd, napi + 1, nbd + 1, napi + 1, api, nbd + 1, defs,
                            prt, inp, out, ext, cpy, ati, val, dis);
                    fclose(f);
                }
                f = fopen("lang/en.md", "wb");
                if(f) {
                    fwrite("<!-- Generated by bin2h, DO NOT edit -->\r\n", 1, 42, f);
                    fwrite(buff, 1, size, f);
                    fclose(f);
                }
                continue;
            } else
            if(!strcmp(p, "palette_png")) {
                buff2 = (uint8_t*)stbi_load_from_memory(buff, size, &W, &H, &j, 4);
                if(buff2) {
                    memset(pal, 0, sizeof(pal));
                    for(k = 1, y = 0; k < 256 && y < H; y += 8)
                        for(x = 0; k < 256 && x < (W > 64 ? 64 : W); x += 8) {
                            col = *((uint32_t*)(buff2 + (y * W + x) * 4));
                            if(col != 0xff000000) pal[k++] = col;
                        }
                    free(buff2);
                    if(h) fprintf(h, "extern const unsigned int default_pal[%u];\n", k);
                    fprintf(c, "const unsigned int default_pal[%u] = { ", k);
                    for(i = 0; i < k; i++)
                        /* same channel order as in a HTML color code */
                        fprintf(c,"%s(0x%02x<<24)|(0x%02x)|(0x%02x<<8)|(0x%02x<<16)", i?", ":"",
                            (pal[i] >> 24) & 0xff, (pal[i]) & 0xff, (pal[i] >> 8) & 0xff, (pal[i] >> 16) & 0xff);
                    fprintf(c," };\n");
                }
                free(buff);
                continue;
            } else
            if(!strcmp(p, "theme_gpl")) {
                memset(theme, 0, sizeof(theme));
                for(buff2 = buff; buff2 < buff + size && (*buff2 < '0' || *buff2 > '9'); buff2++);
                for(k = 0; k < 256 && buff2 < buff + size; k++) {
                    theme[k] = 0xff000000;
                    while(buff2 < buff + size && *buff2 == ' ') buff2++;
                    theme[k] |= (atoi(buff2) & 0xff); while(buff2 < buff + size && *buff2 >= '0' && *buff2 <= '9') buff2++;
                    while(buff2 < buff + size && *buff2 == ' ') buff2++;
                    theme[k] |= (atoi(buff2) & 0xff) << 8; while(buff2 < buff + size && *buff2 >= '0' && *buff2 <= '9') buff2++;
                    while(buff2 < buff + size && *buff2 == ' ') buff2++;
                    theme[k] |= (atoi(buff2) & 0xff) << 16; while(buff2 < buff + size && *buff2 >= '0' && *buff2 <= '9') buff2++;
                    while(buff2 < buff + size && *buff2 != '\n') buff2++;
                    while(buff2 < buff + size && (*buff2 == '\r' || *buff2 == '\n')) buff2++;
                }
                if(h) fprintf(h, "extern unsigned int theme[%u];\n", k);
                fprintf(c, "unsigned int theme[%u] = { ", k);
                for(i = 0; i < k; i++)
                    fprintf(c,"%s(0x%02x<<24)|(0x%02x)|(0x%02x<<8)|(0x%02x<<16)", i?", ":"",
                        (theme[i] >> 24) & 0xff, (theme[i]) & 0xff, (theme[i] >> 8) & 0xff, (theme[i] >> 16) & 0xff);
                fprintf(c," };\n");
                free(buff);
                continue;
            } else
            if(!strcmp(p, "sounds_mod")) {
                buff2 = (uint8_t*)malloc(32 * 16384);
                if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                for(i = 952, m = 0; i < 952 + buff[950] && 1084 + 127; i++) if(buff[i] > m) m = buff[i];
                m++;
                for(ptr = buff + 1084 + m * 1024, r = i = 0; i < 31 && ptr < buff + size - 1; i++) {
                    l = k = (buff[42 + i * 30] << 8) | buff[43 + i * 30];
                    if(l < 1) continue;
                    if(k >= 16376) k = 16376;
                    if(ptr + k * 2 > buff + size) k = (buff + size - ptr) / 2;
                    if(k > 0) {
                        buff2[r + 0] = k & 0xff; buff2[r + 1] = (k >> 8) & 0xff;
                        buff2[r + 2] = buff[47 + i * 30]; buff2[r + 3] = buff[46 + i * 30];
                        buff2[r + 4] = buff[49 + i * 30]; buff2[r + 5] = buff[48 + i * 30];
                        if(!buff2[r + 5] && buff2[r + 4] == 1) buff2[r + 4] = 0;
                        buff2[r + 6] = buff[44 + i * 30] & 0xf;
                        buff2[r + 7] = (buff[45 + i * 30] > 64 ? 64 : buff[45 + i * 30]);
                        if(buff2[r + 7] > 64) buff2[r + 7] = 64;
                        r += 8;
                        for(j = 0; j < k; j++, r++) buff2[r] = ((int)(int8_t)ptr[j * 2] + (int)(int8_t)ptr[j * 2 + 1]) / 2;
                    }
                    ptr += 2 * l;
                }
                buff2[r++] = 0; buff2[r++] = 0;
                free(buff);
                buff = stbi_zlib_compress(buff2, r, &i, 9);
                if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                free(buff2); size = i;
            } else
            if(!strcmp(p, "hl_json")) {
                for(d = s = (char*)buff; s < (char*)buff + size;) {
                    if(s[0] == '/' && s[1] == '*') { while(memcmp(s, "*/", 2)) { s++; } s += 2; } else
                    if(s[0] == '\"') { *d++ = *s++; while(s[0] != '\"') { if(s[0] == '\\') { *d++ = *s++; } *d++ = *s++; } *d++ = *s++; } else
                    if(s[0] != '\r' && s[0] != '\n' && s[0] != '\t' && s[0] != ' ') *d++ = *s++; else s++;
                }
                *d++ = 0;
                buff2 = stbi_zlib_compress(buff, d - (char*)buff, &i, 9);
                if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                free(buff); buff = buff2; size = i;
            } else
            if(!strcmp(p, "advgame_c")) {
                buff[size++] = 0;
                buff2 = stbi_zlib_compress(buff, size, &i, 9);
                if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                free(buff); buff = buff2; size = i;
            } else
            if(!memcmp(p, "meg4_", 5) || !strcmp(p, "meg4")) {
                if(!p[4] || p[5] == 'w' || p[5] == 'e') {
                    for(i = 0; i < size - 8 && memcmp(buff + i, "MEG4GAME", 8); i++);
                    fprintf(c, "#define offsetof_%s %u\n", p, i);
                    if(!p[4]) fprintf(c, "#define sizeof_game %u\n", sizeof_binary_game);
                    memset(buff + i, 0, sizeof_binary_game);
                } else
                if(p[4] == '_' && p[5] == 'j') {
                    for(i = 0; i < size - 9 && memcmp(buff + i, "meg4.wasm", 9); i++);
                    memcpy(buff + i, "game", 4);
                }
                buff2 = stbi_zlib_compress(buff, size, &i, 9);
                if(!buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                free(buff); buff = buff2; size = i;
            } else
            if(strlen(p) == 5 && !strcmp(p + 2, "_md")) {
                md = (char*)realloc(md, (nmd + 1) * 2);
                if(md) { md[nmd * 2] = p[0]; md[nmd * 2 + 1] = p[1]; nmd++; }
                else nmd = 0;
                buff = (uint8_t*)realloc(buff, size + 1); if(buff) buff[size] = 0;
                len = size; d = buff; if(!memcmp(buff, "<!-- Generated", 14)) { len -= 42; d += 42; }
                buff2 = stbi_zlib_compress(d, len + 1, &i, 9);
                if(!buff || !buff2) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                size = i + 3;
                buff = (uint8_t*)realloc(buff, size);
                if(!buff) { fprintf(stderr, "bin2h: memory allocation error\r\n"); exit(2); }
                buff[0] = size & 0xff; buff[1] = (size >> 8) & 0xff; buff[2] = (size >> 16) & 0xff;
                memcpy(buff + 3, buff2, i);
            }
            if(h) fprintf(h, "extern const unsigned char binary_%s[%ld];\n", p, size);
            fprintf(c, "const unsigned char binary_%s[%ld] = { ", p, size);
            for(i = 0; i < size; i++)
                fprintf(c,"%s%d", i?",":"", buff[i]);
            fprintf(c," };\n");
            free(buff);
            buff = NULL;
        } else
            fprintf(stderr, "bin2h: unable to open input file: %s\r\n", argv[file]);
    }
    if(md) {
        if(h) fprintf(h, "extern const char *help_md[%u];\n", nmd * 2 + 1);
        fprintf(c, "const char *help_md[%u] = { ", nmd * 2 + 1);
        for(i = 0; i < nmd; i++) fprintf(c, "%s\"%c%c\",(const char*)&binary_%c%c_md", i ? ", " : "", md[i * 2], md[i * 2 + 1], md[i * 2], md[i * 2 + 1]);
        fprintf(c, ", (const char*)0 };\n");
        free(md);
    }
    if(h) fclose(h);
    fclose(c);
}
