/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./src/generation.ts":
/*!***************************!*\
  !*** ./src/generation.ts ***!
  \***************************/
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CodeGenerator = exports.GenericCodeGenerator = exports.CodeWriter = exports.StringWriter = void 0;
class StringWriter {
    constructor() {
        this.buffer = '';
    }
    write(value) {
        this.buffer += value;
    }
    writeLine(value = '') {
        this.buffer += value + '\n';
    }
    toString() {
        return this.buffer;
    }
}
exports.StringWriter = StringWriter;
class CodeWriter extends StringWriter {
    constructor() {
        super(...arguments);
        this.indent = 0;
        this.needsIndent = true;
    }
    writeGenerator(generator) {
        const tokens = generator.iterateTokens();
        const text = generator.iterateText();
        const children = generator.iterateChildren();
        let result = tokens.next();
        while (!result.done) {
            switch (result.value) {
                case Token.STRING:
                    const str = text.next().value;
                    if (this.needsIndent) {
                        this.write("    ".repeat(this.indent));
                        this.needsIndent = false;
                    }
                    this.write(str);
                    break;
                case Token.GOSUB:
                    const sub = children.next().value;
                    this.writeGenerator(sub);
                    break;
                case Token.INDENT:
                    this.indent++;
                    break;
                case Token.UNINDENT:
                    this.indent = this.indent > 0 ? this.indent - 1 : 0;
                    break;
                case Token.NEWLINE:
                    this.write("\n");
                    this.needsIndent = true;
                    break;
                default:
                    break;
            }
            result = tokens.next();
        }
    }
}
exports.CodeWriter = CodeWriter;
var Token;
(function (Token) {
    Token[Token["STRING"] = 0] = "STRING";
    Token[Token["NEWLINE"] = 1] = "NEWLINE";
    Token[Token["INDENT"] = 2] = "INDENT";
    Token[Token["UNINDENT"] = 3] = "UNINDENT";
    Token[Token["GOSUB"] = 4] = "GOSUB";
})(Token || (Token = {}));
class GenericCodeGenerator {
    constructor() {
        this.children = [];
        this.text = [];
        this.tokens = [];
        this.tags = {};
    }
    getTag(key) {
        return this.tags[key];
    }
    setTag(key, value) {
        this.tags[key] = value;
    }
    iterateTokens() {
        return this.tokens[Symbol.iterator]();
    }
    iterateText() {
        return this.text[Symbol.iterator]();
    }
    iterateChildren() {
        return this.children[Symbol.iterator]();
    }
    line(text) {
        this.tokens.push(Token.STRING, Token.NEWLINE);
        this.text.push(text);
    }
    comment(text) {
        this.line("// " + text);
    }
    call(name, params, returnVal = null) {
        if (returnVal)
            this.inline(`${returnVal.type} ${returnVal.name} = `);
        this.inline(name + "(");
        this.inline(params.join(", "));
        this.statement(")");
    }
    declare(name, type, isStatic = false, initValue = null) {
        if (isStatic)
            this.inline("static ");
        this.inline(type + " " + name);
        if (initValue)
            this.inline(" = " + initValue);
        this.statement("");
    }
    child(sub) {
        if (!sub)
            sub = this.createGenerator();
        this.tokens.push(Token.GOSUB);
        this.children.push(sub);
        return sub;
    }
    inline(str) {
        this.tokens.push(Token.STRING);
        this.text.push(str);
    }
    statement(str) {
        this.line(str + ";");
    }
    breakLine() {
        this.tokens.push(Token.NEWLINE);
    }
    indent() {
        this.tokens.push(Token.INDENT);
    }
    unindent() {
        this.tokens.push(Token.UNINDENT);
    }
    function(name, returnType, args, isStatic, func) {
        const sub = this.createGenerator();
        sub.setTag("_type", "function-body");
        sub.setTag("_name", name);
        sub.setTag("_isStatic", isStatic);
        sub.setTag("_returnType", returnType);
        if (isStatic)
            this.inline("static ");
        this.inline(returnType + " " + name + "(");
        this.inline(args.map(x => x.type + " " + x.name).join(", "));
        this.inline(") {");
        this.breakLine();
        this.indent();
        this.child(sub);
        this.unindent();
        this.line("}");
        this.breakLine();
        if (func)
            func(sub);
        return sub;
    }
    if(condition, funIf) {
        this.line("if(" + condition + ") {");
        this.indent();
        const sub = this.createGenerator();
        sub.setTag("_type", "if-body");
        sub.setTag("_condition", condition);
        this.child(sub);
        this.unindent();
        this.line("}");
        if (funIf)
            funIf(sub);
        return sub;
    }
    else(funElse) {
        this.line("else {");
        this.indent();
        const sub = this.createGenerator();
        sub.setTag("_type", "else-body");
        this.child(sub);
        this.unindent();
        this.line("}");
        if (funElse)
            funElse(sub);
        return sub;
    }
    returnExp(exp) {
        this.statement("return " + exp);
    }
    include(name) {
        this.line("#include <" + name + ">");
    }
    for(indexVar, lengthVar) {
        this.line(`for(int ${indexVar}; i < ${lengthVar}; i++){`);
        this.indent();
        const child = this.child();
        this.unindent();
        this.line("}");
        return child;
    }
    header(guard, fun) {
        this.line("#ifndef " + guard);
        this.line("#define " + guard);
        this.breakLine();
        const sub = this.child();
        sub.setTag("_type", "header-body");
        sub.setTag("_guardName", guard);
        this.line("#endif // " + guard);
        if (fun)
            fun(sub);
        return sub;
    }
    declareStruct(structName, varName, values, isStatic = false) {
        if (isStatic)
            this.inline("static ");
        this.statement(`${structName} ${varName} = { ${values.join(', ')} }`);
    }
    switch(switchVar) {
        this.line(`switch(${switchVar}) {`);
        this.indent();
        const body = this.child();
        this.unindent();
        this.line("}");
        return body;
    }
    case(value) {
        this.line(`case ${value}:`);
    }
    defaultBreak() {
        this.line("default:");
        this.line("{");
        this.indent();
        const body = this.child();
        this.statement("break");
        this.unindent();
        this.line("}");
        return body;
    }
    caseBreak(value) {
        this.case(value);
        this.line("{");
        this.indent();
        const body = this.child();
        this.statement("break");
        this.unindent();
        this.line("}");
        return body;
    }
}
exports.GenericCodeGenerator = GenericCodeGenerator;
class CodeGenerator extends GenericCodeGenerator {
    createGenerator() {
        return new CodeGenerator();
    }
}
exports.CodeGenerator = CodeGenerator;


/***/ }),

/***/ "./src/header-parser.ts":
/*!******************************!*\
  !*** ./src/header-parser.ts ***!
  \******************************/
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.HeaderParser = void 0;
class HeaderParser {
    parseEnums(input) {
        const matches = [...input.matchAll(/((?:\/\/.+\n)*)typedef enum {\n([^}]+)} ([^;]+)/gm)];
        return matches.map(groups => {
            return {
                description: this.parseComments(groups[1]),
                values: this.parseEnumValues(groups[2]),
                name: groups[3],
            };
        });
    }
    parseEnumValues(input) {
        let lastNumber = 0;
        return input.split('\n')
            .map(line => line.trim().match(/([^ ,]+)(?: = ([0-9]+))?,?(?: *)(?:\/\/ (.+))?/))
            .filter(x => x !== null && !x[0].startsWith("/"))
            .map(groups => {
            let val = lastNumber = groups[2] ? parseInt(groups[2]) : lastNumber;
            lastNumber++;
            return {
                name: groups[1],
                description: groups[3] || "",
                value: val
            };
        });
    }
    parseComments(input) {
        return input.split('\n').map(x => x.replace("// ", "")).join('\n').trim();
    }
    parseFunctionDefinitions(input) {
        const matches = [...input.matchAll(/^[A-Z]+ (.+?)(\w+)\(([^\)]+)\);(?: +\/\/ (.+))?$/gm)];
        return matches.map(groups => ({
            returnType: groups[1].trim(),
            name: groups[2],
            params: this.parseFunctionArgs(groups[3]),
            description: groups[4] || ""
        }));
    }
    parseFunctions(input, noPrefix = false) {
        const matches = noPrefix
            ? [...input.matchAll(/((?:\/\/.+\n)+)^(.+?)(\w+)\(([^\)]+)\)/gm)]
            : [...input.matchAll(/((?:\/\/.+\n)+)^[A-Z]+ (.+?)(\w+)\(([^\)]+)\)/gm)];
        return matches.map(groups => ({
            returnType: groups[2].trim(),
            name: groups[3],
            params: this.parseFunctionArgs(groups[4]),
            description: groups[1] ? this.parseComments(groups[1]) : ""
        }));
    }
    parseFunctionArgs(input) {
        return input.split(',').filter(x => x !== 'void').map(arg => {
            arg = arg.trim().replace(" *", "* ");
            const frags = arg.split(' ');
            const name = frags.pop();
            const type = frags.join(' ').replace("*", " *");
            return { name: name || "", type: type.trim() };
        });
    }
    parseStructs(input) {
        return [...input.matchAll(/((?:\/\/.+\n)+)typedef struct {([^}]+)} ([^;]+);/gm)].map(groups => ({
            name: groups[3],
            fields: this.parseStructFields(groups[2]),
            description: this.parseComments(groups[1])
        }));
    }
    parseStructFields(input) {
        return input.trim().split("\n").map(x => x.trim()).filter(x => !x.startsWith("/") && x.endsWith(";")).map(x => {
            const match = x.match(/([^ ]+(?: \*)?) ([^;]+);/);
            return {
                name: match[2],
                type: match[1],
                description: ""
            };
        });
    }
}
exports.HeaderParser = HeaderParser;


/***/ }),

/***/ "./src/quickjs.ts":
/*!************************!*\
  !*** ./src/quickjs.ts ***!
  \************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.QuickJsGenerator = exports.GenericQuickJsGenerator = exports.QuickJsHeader = void 0;
const fs_1 = __webpack_require__(/*! fs */ "fs");
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
class QuickJsHeader {
    constructor(name) {
        this.name = name;
        this.structLookup = {};
        const root = this.root = new QuickJsGenerator();
        const body = this.body = root.header("JS_" + this.name + "_GUARD");
        const includes = this.includes = body.child();
        includes.include("stdio.h");
        includes.include("stdlib.h");
        includes.include("string.h");
        includes.breakLine();
        includes.include("quickjs.h");
        body.breakLine();
        body.line("#ifndef countof");
        body.line("#define countof(x) (sizeof(x) / sizeof((x)[0]))");
        body.line("#endif");
        body.breakLine();
        this.definitions = body.child();
        body.breakLine();
        this.structs = body.child();
        this.functions = body.child();
        this.moduleFunctionList = body.jsFunctionList("js_" + name + "_funcs");
        const moduleInitFunc = body.function("js_" + this.name + "_init", "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true);
        const moduleInit = this.moduleInit = moduleInitFunc.child();
        moduleInit.statement(`JS_SetModuleExportList(ctx, m,${this.moduleFunctionList.getTag("_name")},countof(${this.moduleFunctionList.getTag("_name")}))`);
        moduleInitFunc.returnExp("0");
        const moduleEntryFunc = body.function("js_init_module_" + this.name, "JSModuleDef *", [{ type: "JSContext *", name: "ctx" }, { type: "const char *", name: "module_name" }], false);
        const moduleEntry = this.moduleEntry = moduleEntryFunc.child();
        moduleEntry.statement("JSModuleDef *m");
        moduleEntry.statement(`m = JS_NewCModule(ctx, module_name, ${moduleInitFunc.getTag("_name")})`);
        moduleEntry.statement("if(!m) return NULL");
        moduleEntry.statement(`JS_AddModuleExportList(ctx, m, ${this.moduleFunctionList.getTag("_name")}, countof(${this.moduleFunctionList.getTag("_name")}))`);
        moduleEntryFunc.statement("return m");
    }
    registerStruct(struct, classId) {
        this.structLookup[struct] = classId;
    }
    writeTo(filename) {
        const writer = new generation_1.CodeWriter();
        writer.writeGenerator(this.root);
        (0, fs_1.writeFileSync)(filename, writer.toString());
    }
}
exports.QuickJsHeader = QuickJsHeader;
class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator {
    jsBindingFunction(jsName) {
        const args = [
            { type: "JSContext *", name: "ctx" },
            { type: "JSValueConst", name: "this_val" },
            { type: "int", name: "argc" },
            { type: "JSValueConst *", name: "argv" },
        ];
        const sub = this.function("js_" + jsName, "JSValue", args, true);
        return sub;
    }
    jsToC(type, name, src, classIds = {}, supressDeclaration = false, typeAlias) {
        switch (typeAlias ?? type) {
            // Array Buffer
            case "const void *":
            case "void *":
            case "float *":
            case "unsigned short *":
            case "unsigned char *":
            case "const unsigned char *":
                this.declare(name + "_size", "size_t");
                this.declare(name + "_js", "void *", false, `(void *)JS_GetArrayBuffer(ctx, &${name}_size, ${src})`);
                this.if(name + "_js == NULL").returnExp("JS_EXCEPTION");
                this.declare(name, type, false, "malloc(" + name + "_size)");
                this.call("memcpy", ["(void *)" + name, "(const void *)" + name + "_js", name + "_size"]);
                break;
            // String
            case "const char *":
                //case "char *":
                if (!supressDeclaration)
                    this.statement(`${type} ${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`);
                else
                    this.statement(`${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`);
                break;
            case "double":
                if (!supressDeclaration)
                    this.statement(`${type} ${name}`);
                this.statement(`JS_ToFloat64(ctx, &${name}, ${src})`);
                break;
            case "float":
                this.statement("double _double_" + name);
                this.statement(`JS_ToFloat64(ctx, &_double_${name}, ${src})`);
                if (!supressDeclaration)
                    this.statement(`${type} ${name} = (${type})_double_${name}`);
                else
                    this.statement(`${name} = (${type})_double_${name}`);
                break;
            case "int":
                if (!supressDeclaration)
                    this.statement(`${type} ${name}`);
                this.statement(`JS_ToInt32(ctx, &${name}, ${src})`);
                break;
            case "unsigned int":
                if (!supressDeclaration)
                    this.statement(`${type} ${name}`);
                this.statement(`JS_ToUint32(ctx, &${name}, ${src})`);
                break;
            case "unsigned char":
                this.statement("unsigned int _int_" + name);
                this.statement(`JS_ToUint32(ctx, &_int_${name}, ${src})`);
                if (!supressDeclaration)
                    this.statement(`${type} ${name} = (${type})_int_${name}`);
                else
                    this.statement(`${name} = (${type})_int_${name}`);
                break;
            case "bool":
                if (!supressDeclaration)
                    this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`);
                else
                    this.statement(`${name} = JS_ToBool(ctx, ${src})`);
                break;
            default:
                const isConst = type.startsWith('const');
                const isPointer = type.endsWith(' *');
                const classId = classIds[type.replace("const ", "").replace(" *", "")];
                if (!classId)
                    throw new Error("Cannot convert into parameter type: " + type);
                const suffix = isPointer ? "" : "_ptr";
                this.jsOpqToStructPtr(type.replace(" *", ""), name + suffix, src, classId);
                this.statement(`if(${name + suffix} == NULL) return JS_EXCEPTION`);
                if (!isPointer)
                    this.declare(name, type, false, `*${name}_ptr`);
        }
    }
    jsToJs(type, name, src, classIds = {}) {
        switch (type) {
            case "int":
            case "long":
                this.declare(name, 'JSValue', false, `JS_NewInt32(ctx, ${src})`);
                break;
            case "long":
                this.declare(name, 'JSValue', false, `JS_NewInt64(ctx, ${src})`);
                break;
            case "unsigned int":
            case "unsigned char":
                this.declare(name, 'JSValue', false, `JS_NewUint32(ctx, ${src})`);
                break;
            case "bool":
                this.declare(name, 'JSValue', false, `JS_NewBool(ctx, ${src})`);
                break;
            case "float":
            case "double":
                this.declare(name, 'JSValue', false, `JS_NewFloat64(ctx, ${src})`);
                break;
            case "const char *":
            case "char *":
                this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`);
                break;
            // case "unsigned char *":
            //     this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`)
            //     break;
            default:
                const classId = classIds[type];
                if (!classId)
                    throw new Error("Cannot convert parameter type to Javascript: " + type);
                this.jsStructToOpq(type, name, src, classId);
        }
    }
    jsStructToOpq(structType, jsVar, srcVar, classId) {
        this.declare(jsVar + "_ptr", structType + "*", false, `(${structType}*)js_malloc(ctx, sizeof(${structType}))`);
        this.statement("*" + jsVar + "_ptr = " + srcVar);
        this.declare(jsVar, "JSValue", false, `JS_NewObjectClass(ctx, ${classId})`);
        this.call("JS_SetOpaque", [jsVar, jsVar + "_ptr"]);
    }
    jsCleanUpParameter(type, name) {
        switch (type) {
            case "char *":
            case "const char *":
                this.statement(`JS_FreeCString(ctx, ${name})`);
                break;
            case "const void *":
            case "void *":
            case "float *":
            case "unsigned short *":
            case "unsigned char *":
            case "const unsigned char *":
                this.statement(`free((void *)${name})`);
                break;
            default:
                break;
        }
    }
    jsFunctionList(name) {
        this.line("static const JSCFunctionListEntry " + name + "[] = {");
        this.indent();
        const sub = this.createGenerator();
        sub.setTag("_type", "js-function-list");
        sub.setTag("_name", name);
        this.child(sub);
        this.unindent();
        this.statement("}");
        this.breakLine();
        return sub;
    }
    jsFuncDef(jsName, numArgs, cName) {
        this.line(`JS_CFUNC_DEF("${jsName}",${numArgs},${cName}),`);
    }
    jsClassId(id) {
        this.declare(id, "JSClassID", true);
        return id;
    }
    jsPropStringDef(key, value) {
        this.line(`JS_PROP_STRING_DEF("${key}","${value}", JS_PROP_CONFIGURABLE),`);
    }
    jsGetSetDef(key, getFunc, setFunc) {
        this.line(`JS_CGETSET_DEF("${key}",${getFunc || "NULL"},${setFunc || "NULL"}),`);
    }
    jsStructFinalizer(classId, structName, onFinalize) {
        const args = [{ type: "JSRuntime *", name: "rt" }, { type: "JSValue", name: "val" }];
        const body = this.function(`js_${structName}_finalizer`, "void", args, true);
        body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`);
        body.if("ptr", cond => {
            //cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName} %p"`,"ptr"])
            if (onFinalize)
                onFinalize(cond, "ptr");
            cond.call("js_free_rt", ["rt", "ptr"]);
        });
        return body;
    }
    jsClassDeclaration(structName, classId, finalizerName, funcListName) {
        const body = this.function("js_declare_" + structName, "int", [{ type: "JSContext *", name: "ctx" }, { type: "JSModuleDef *", name: "m" }], true);
        body.call("JS_NewClassID", ["&" + classId]);
        const classDefName = `js_${structName}_def`;
        body.declare(classDefName, "JSClassDef", false, `{ .class_name = "${structName}", .finalizer = ${finalizerName} }`);
        body.call("JS_NewClass", ["JS_GetRuntime(ctx)", classId, "&" + classDefName]);
        body.declare("proto", "JSValue", false, "JS_NewObject(ctx)");
        body.call("JS_SetPropertyFunctionList", ["ctx", "proto", funcListName, `countof(${funcListName})`]);
        body.call("JS_SetClassProto", ["ctx", classId, "proto"]);
        body.statement("return 0");
        return body;
    }
    jsStructGetter(structName, classId, field, type, classIds, overrideRead) {
        const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }];
        const fun = this.function(`js_${structName}_get_${field}`, "JSValue", args, true);
        fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
        if (overrideRead) {
            overrideRead(fun);
        }
        else {
            fun.declare(field, type, false, "ptr->" + field);
        }
        fun.jsToJs(type, "ret", field, classIds);
        fun.returnExp("ret");
        return fun;
    }
    jsStructSetter(structName, classId, field, type, classIds, overrideWrite) {
        const args = [{ type: "JSContext*", name: "ctx" }, { type: "JSValueConst", name: "this_val" }, { type: "JSValueConst", name: "v" }];
        const fun = this.function(`js_${structName}_set_${field}`, "JSValue", args, true);
        fun.declare("ptr", structName + "*", false, `JS_GetOpaque2(ctx, this_val, ${classId})`);
        fun.jsToC(type, "value", "v", classIds);
        if (overrideWrite) {
            overrideWrite(fun);
        }
        else {
            fun.statement("ptr->" + field + " = value");
        }
        fun.returnExp("JS_UNDEFINED");
        return fun;
    }
    jsOpqToStructPtr(structType, structVar, srcVar, classId) {
        this.declare(structVar, structType + "*", false, `(${structType}*)JS_GetOpaque2(ctx, ${srcVar}, ${classId})`);
    }
    jsStructConstructor(structName, fields, classId, classIds) {
        const body = this.jsBindingFunction(structName + "_constructor");
        for (let i = 0; i < fields.length; i++) {
            const para = fields[i];
            body.jsToC(para.type, para.name, "argv[" + i + "]", classIds);
        }
        body.declareStruct(structName, "_struct", fields.map(x => x.name));
        body.jsStructToOpq(structName, "_return", "_struct", classId);
        body.returnExp("_return");
        return body;
    }
}
exports.GenericQuickJsGenerator = GenericQuickJsGenerator;
class QuickJsGenerator extends GenericQuickJsGenerator {
    createGenerator() {
        return new QuickJsGenerator();
    }
}
exports.QuickJsGenerator = QuickJsGenerator;


/***/ }),

/***/ "./src/raylib-header.ts":
/*!******************************!*\
  !*** ./src/raylib-header.ts ***!
  \******************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.RayLibHeader = void 0;
const quickjs_1 = __webpack_require__(/*! ./quickjs */ "./src/quickjs.ts");
const typescript_1 = __webpack_require__(/*! ./typescript */ "./src/typescript.ts");
class RayLibHeader extends quickjs_1.QuickJsHeader {
    constructor(name) {
        super(name);
        this.typings = new typescript_1.TypeScriptDeclaration();
        this.includes.include("raylib.h");
        //this.includes.line("#define RAYMATH_IMPLEMENTATION")
    }
    addApiFunction(api) {
        const options = api.binding || {};
        if (options.ignore)
            return;
        const jName = options.jsName || api.name.charAt(0).toLowerCase() + api.name.slice(1);
        console.log("Binding function " + api.name);
        const fun = this.functions.jsBindingFunction(jName);
        if (options.body) {
            options.body(fun);
        }
        else {
            if (options.before)
                options.before(fun);
            // read parameters
            api.params = api.params || [];
            const activeParams = api.params.filter(x => !x.binding?.ignore);
            for (let i = 0; i < activeParams.length; i++) {
                const para = activeParams[i];
                if (para.binding?.customConverter)
                    para.binding.customConverter(fun, "argv[" + i + "]");
                else
                    fun.jsToC(para.type, para.name, "argv[" + i + "]", this.structLookup, false, para.binding?.typeAlias);
            }
            // call c function
            if (options.customizeCall)
                fun.line(options.customizeCall);
            else
                fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" });
            // clean up parameters
            for (let i = 0; i < activeParams.length; i++) {
                const param = activeParams[i];
                if (param.binding?.customCleanup)
                    param.binding.customCleanup(fun, "argv[" + i + "]");
                else
                    fun.jsCleanUpParameter(param.type, param.name);
            }
            // return result
            if (api.returnType === "void") {
                if (options.after)
                    options.after(fun);
                fun.statement("return JS_UNDEFINED");
            }
            else {
                fun.jsToJs(api.returnType, "ret", "returnVal", this.structLookup);
                if (options.after)
                    options.after(fun);
                fun.returnExp("ret");
            }
        }
        // add binding to function declaration
        this.moduleFunctionList.jsFuncDef(jName, (api.params || []).filter(x => !x.binding?.ignore).length, fun.getTag("_name"));
        this.typings.addFunction(jName, api);
    }
    addEnum(renum) {
        console.log("Binding enum " + renum.name);
        renum.values.forEach(x => this.exportGlobalInt(x.name, x.description));
    }
    addApiStruct(struct) {
        const options = struct.binding || {};
        console.log("Binding struct " + struct.name);
        const classId = this.definitions.jsClassId(`js_${struct.name}_class_id`);
        this.registerStruct(struct.name, classId);
        options.aliases?.forEach(x => this.registerStruct(x, classId));
        const finalizer = this.structs.jsStructFinalizer(classId, struct.name, (gen, ptr) => options.destructor && gen.call(options.destructor.name, ["*" + ptr]));
        const propDeclarations = this.structs.createGenerator();
        if (options && options.properties) {
            for (const field of Object.keys(options.properties)) {
                const type = struct.fields.find(x => x.name === field)?.type;
                if (!type)
                    throw new Error(`Struct ${struct.name} does not contain field ${field}`);
                const el = options.properties[field];
                let _get = undefined;
                let _set = undefined;
                if (el.get)
                    _get = this.structs.jsStructGetter(struct.name, classId, field, type, /*Be carefull when allocating memory in a getter*/ this.structLookup, el.overrideRead);
                if (el.set)
                    _set = this.structs.jsStructSetter(struct.name, classId, field, type, this.structLookup, el.overrideWrite);
                propDeclarations.jsGetSetDef(field, _get?.getTag("_name"), _set?.getTag("_name"));
            }
        }
        const classFuncList = this.structs.jsFunctionList(`js_${struct.name}_proto_funcs`);
        classFuncList.child(propDeclarations);
        classFuncList.jsPropStringDef("[Symbol.toStringTag]", struct.name);
        const classDecl = this.structs.jsClassDeclaration(struct.name, classId, finalizer.getTag("_name"), classFuncList.getTag("_name"));
        this.moduleInit.call(classDecl.getTag("_name"), ["ctx", "m"]);
        if (options?.createConstructor || options?.createEmptyConstructor) {
            const body = this.functions.jsStructConstructor(struct.name, options?.createEmptyConstructor ? [] : struct.fields, classId, this.structLookup);
            this.moduleInit.statement(`JSValue ${struct.name}_constr = JS_NewCFunction2(ctx, ${body.getTag("_name")},"${struct.name})", ${struct.fields.length}, JS_CFUNC_constructor_or_func, 0)`);
            this.moduleInit.call("JS_SetModuleExport", ["ctx", "m", `"${struct.name}"`, struct.name + "_constr"]);
            this.moduleEntry.call("JS_AddModuleExport", ["ctx", "m", '"' + struct.name + '"']);
        }
        this.typings.addStruct(struct);
    }
    exportGlobalStruct(structName, exportName, values, description) {
        this.moduleInit.declareStruct(structName, exportName + "_struct", values);
        const classId = this.structLookup[structName];
        if (!classId)
            throw new Error("Struct " + structName + " not found in register");
        this.moduleInit.jsStructToOpq(structName, exportName + "_js", exportName + "_struct", classId);
        this.moduleInit.call("JS_SetModuleExport", ["ctx", "m", `"${exportName}"`, exportName + "_js"]);
        this.moduleEntry.call("JS_AddModuleExport", ["ctx", "m", `"${exportName}"`]);
        this.typings.constants.tsDeclareConstant(exportName, structName, description);
    }
    exportGlobalInt(name, description) {
        this.moduleInit.statement(`JS_SetModuleExport(ctx, m, "${name}", JS_NewInt32(ctx, ${name}))`);
        this.moduleEntry.statement(`JS_AddModuleExport(ctx, m, "${name}")`);
        this.typings.constants.tsDeclareConstant(name, "number", description);
    }
    exportGlobalDouble(name, description) {
        this.moduleInit.statement(`JS_SetModuleExport(ctx, m, "${name}", JS_NewFloat64(ctx, ${name}))`);
        this.moduleEntry.statement(`JS_AddModuleExport(ctx, m, "${name}")`);
        this.typings.constants.tsDeclareConstant(name, "number", description);
    }
}
exports.RayLibHeader = RayLibHeader;


/***/ }),

/***/ "./src/typescript.ts":
/*!***************************!*\
  !*** ./src/typescript.ts ***!
  \***************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.TypescriptGenerator = exports.GenericTypescriptGenerator = exports.TypeScriptDeclaration = void 0;
const generation_1 = __webpack_require__(/*! ./generation */ "./src/generation.ts");
const fs_1 = __webpack_require__(/*! fs */ "fs");
class TypeScriptDeclaration {
    constructor() {
        this.root = new TypescriptGenerator();
        this.structs = this.root.child();
        this.functions = this.root.child();
        this.constants = this.root.child();
    }
    addFunction(name, api) {
        const options = api.binding || {};
        const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: x.binding?.jsType ?? this.toJsType(x.type) }));
        const returnType = options.jsReturns ?? this.toJsType(api.returnType);
        this.functions.tsDeclareFunction(name, para, returnType, api.description);
    }
    addStruct(api) {
        const options = api.binding || {};
        var fields = api.fields.filter(x => !!(options.properties || {})[x.name]).map(x => ({ name: x.name, description: x.description, type: this.toJsType(x.type) }));
        this.structs.tsDeclareInterface(api.name, fields);
        this.structs.tsDeclareType(api.name, !!(options.createConstructor || options.createEmptyConstructor), options.createEmptyConstructor ? [] : fields);
    }
    toJsType(type) {
        switch (type) {
            case "int":
            case "long":
            case "unsigned int":
            case "unsigned char":
            case "float":
            case "double":
                return "number";
            case "const unsigned char *":
            case "unsigned char *":
            case "unsigned short *":
            case "float *":
                return "ArrayBuffer";
            case "bool":
                return "boolean";
            case "const char *":
            case "char *":
                return "string | undefined | null";
            case "void *":
            case "const void *":
                return "any";
            case "Camera":
            case "Camera *":
                return "Camera3D";
            case "Texture2D":
            case "Texture2D *":
            case "TextureCubemap":
                return "Texture";
            case "RenderTexture2D":
            case "RenderTexture2D *":
                return "RenderTexture";
            case "Quaternion":
                return "Vector4";
            default:
                return type.replace(" *", "").replace("const ", "");
        }
    }
    writeTo(filename) {
        const writer = new generation_1.CodeWriter();
        writer.writeGenerator(this.root);
        (0, fs_1.writeFileSync)(filename, writer.toString());
    }
}
exports.TypeScriptDeclaration = TypeScriptDeclaration;
class GenericTypescriptGenerator extends generation_1.GenericCodeGenerator {
    tsDeclareFunction(name, parameters, returnType, description) {
        this.tsDocComment(description);
        this.statement(`declare function ${name}(${parameters.map(x => x.name + ': ' + x.type).join(', ')}): ${returnType}`);
    }
    tsDeclareConstant(name, type, description) {
        this.tsDocComment(description);
        this.statement(`declare var ${name}: ${type}`);
    }
    tsDeclareType(name, hasConstructor, parameters) {
        this.line(`declare var ${name}: {`);
        this.indent();
        this.statement("prototype: " + name);
        if (hasConstructor)
            this.statement(`new(${parameters.map(x => x.name + ": " + x.type).join(', ')}): ${name}`);
        this.unindent();
        this.line("}");
    }
    tsDeclareInterface(name, fields) {
        this.line(`interface ${name} {`);
        this.indent();
        for (const field of fields) {
            if (field.description)
                this.tsDocComment(field.description);
            this.line(field.name + ": " + field.type + ",");
        }
        this.unindent();
        this.line("}");
    }
    tsDocComment(comment) {
        this.line(`/** ${comment} */`);
    }
}
exports.GenericTypescriptGenerator = GenericTypescriptGenerator;
class TypescriptGenerator extends GenericTypescriptGenerator {
    createGenerator() {
        return new TypescriptGenerator();
    }
}
exports.TypescriptGenerator = TypescriptGenerator;


/***/ }),

/***/ "fs":
/*!*********************!*\
  !*** external "fs" ***!
  \*********************/
/***/ ((module) => {

module.exports = require("fs");

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
var exports = __webpack_exports__;
/*!**********************!*\
  !*** ./src/index.ts ***!
  \**********************/

Object.defineProperty(exports, "__esModule", ({ value: true }));
const fs_1 = __webpack_require__(/*! fs */ "fs");
const raylib_header_1 = __webpack_require__(/*! ./raylib-header */ "./src/raylib-header.ts");
const header_parser_1 = __webpack_require__(/*! ./header-parser */ "./src/header-parser.ts");
let api;
function getFunction(funList, name) {
    return funList.find(x => x.name === name);
}
function getStruct(strList, name) {
    return strList.find(x => x.name === name);
}
function getAliases(aliasList, name) {
    return aliasList.filter(x => x.type === name).map(x => x.name);
}
function ignore(name) {
    getFunction(api.functions, name).binding = { ignore: true };
}
function main() {
    // Load the pre-generated raylib api
    api = JSON.parse((0, fs_1.readFileSync)("thirdparty/raylib/parser/output/raylib_api.json", 'utf8'));
    const parser = new header_parser_1.HeaderParser();
    const rmathHeader = (0, fs_1.readFileSync)("thirdparty/raylib/src/raymath.h", "utf8");
    const mathApi = parser.parseFunctions(rmathHeader);
    mathApi.forEach(x => api.functions.push(x));
    const rcameraHeader = (0, fs_1.readFileSync)("thirdparty/raylib/src/rcamera.h", "utf8");
    const cameraApi = parser.parseFunctionDefinitions(rcameraHeader);
    cameraApi.forEach(x => api.functions.push(x));
    const rguiHeader = (0, fs_1.readFileSync)("thirdparty/raygui/src/raygui.h", "utf8");
    const rguiFunctions = parser.parseFunctionDefinitions(rguiHeader);
    const rguiEnums = parser.parseEnums(rguiHeader);
    //rguiApi.forEach(x => console.log(`core.addApiFunctionByName("${x.name}")`))
    rguiFunctions.forEach(x => api.functions.push(x));
    rguiEnums.forEach(x => api.enums.push(x));
    const rlightsHeader = (0, fs_1.readFileSync)("include/rlights.h", "utf8");
    const rlightsFunctions = parser.parseFunctions(rlightsHeader, true);
    api.functions.push(rlightsFunctions[0]);
    api.functions.push(rlightsFunctions[1]);
    const rlightsEnums = parser.parseEnums(rlightsHeader);
    rlightsEnums.forEach(x => api.enums.push(x));
    const rlightsStructs = parser.parseStructs(rlightsHeader);
    rlightsStructs[0].binding = {
        properties: {
            type: { get: true, set: true },
            enabled: { get: true, set: true },
            position: { get: true, set: true },
            target: { get: true, set: true },
            color: { get: true, set: true },
            attenuation: { get: true, set: true },
        },
    };
    api.structs.push(rlightsStructs[0]);
    const reasingsHeader = (0, fs_1.readFileSync)("include/reasings.h", "utf8");
    const reasingsFunctions = parser.parseFunctions(reasingsHeader);
    reasingsFunctions.forEach(x => api.functions.push(x));
    const rlightmapperHeader = (0, fs_1.readFileSync)("src/rlightmapper.h", "utf8");
    const rlightmapperFunctions = parser.parseFunctionDefinitions(rlightmapperHeader);
    const rlightmapperStructs = parser.parseStructs(rlightmapperHeader);
    rlightmapperFunctions.forEach(x => api.functions.push(x));
    rlightmapperStructs.forEach(x => api.structs.push(x));
    rlightmapperStructs[0].binding = {
        properties: {
            w: { get: true },
            h: { get: true },
            progress: { get: true }
        }
    };
    rlightmapperStructs[1].binding = {
        properties: {
            hemisphereSize: { get: true, set: true },
            zNear: { get: true, set: true },
            zFar: { get: true, set: true },
            backgroundColor: { get: true, set: true },
            interpolationPasses: { get: true, set: true },
            interpolationThreshold: { get: true, set: true },
            cameraToSurfaceDistanceModifier: { get: true, set: true },
        }
    };
    const rextensionsHeader = (0, fs_1.readFileSync)("src/rextensions.h", "utf8");
    const rextensionsFunctions = parser.parseFunctionDefinitions(rextensionsHeader);
    console.log(rextensionsFunctions);
    rextensionsFunctions.forEach(x => api.functions.push(x));
    // Define a new header
    const core = new raylib_header_1.RayLibHeader("raylib_core");
    core.includes.include("raymath.h");
    core.includes.include("rcamera.h");
    core.includes.line("#define RAYGUI_IMPLEMENTATION");
    core.includes.include("raygui.h");
    core.includes.line("#define RLIGHTS_IMPLEMENTATION");
    core.includes.include("rlights.h");
    core.includes.include("reasings.h");
    core.includes.line("#define RLIGHTMAPPER_IMPLEMENTATION");
    core.includes.include("rlightmapper.h");
    getStruct(api.structs, "Color").binding = {
        properties: {
            r: { get: true, set: true },
            g: { get: true, set: true },
            b: { get: true, set: true },
            a: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Rectangle").binding = {
        properties: {
            x: { get: true, set: true },
            y: { get: true, set: true },
            width: { get: true, set: true },
            height: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Vector2").binding = {
        properties: {
            x: { get: true, set: true },
            y: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Vector3").binding = {
        properties: {
            x: { get: true, set: true },
            y: { get: true, set: true },
            z: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Vector4").binding = {
        properties: {
            x: { get: true, set: true },
            y: { get: true, set: true },
            z: { get: true, set: true },
            w: { get: true, set: true },
        },
        createConstructor: true,
        aliases: getAliases(api.aliases, "Vector4")
    };
    getStruct(api.structs, "Ray").binding = {
        properties: {
            position: { get: false, set: true },
            direction: { get: false, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "RayCollision").binding = {
        properties: {
            hit: { get: true, set: false },
            distance: { get: true, set: false },
            point: { get: true, set: false },
            normal: { get: true, set: false },
        },
        createConstructor: false
    };
    getStruct(api.structs, "Camera2D").binding = {
        properties: {
            offset: { get: true, set: true },
            target: { get: true, set: true },
            rotation: { get: true, set: true },
            zoom: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Camera3D").binding = {
        properties: {
            position: { get: true, set: true },
            target: { get: true, set: true },
            up: { get: false, set: true },
            fovy: { get: true, set: true },
            projection: { get: true, set: true },
        },
        createConstructor: true,
        aliases: getAliases(api.aliases, "Camera3D")
    };
    getStruct(api.structs, "BoundingBox").binding = {
        properties: {
            min: { get: true, set: true },
            max: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Matrix").binding = {
        properties: {},
        createConstructor: false
    };
    getStruct(api.structs, "NPatchInfo").binding = {
        properties: {
            source: { get: true, set: true },
            left: { get: true, set: true },
            top: { get: true, set: true },
            right: { get: true, set: true },
            bottom: { get: true, set: true },
            layout: { get: true, set: true },
        },
        createConstructor: true
    };
    getStruct(api.structs, "Image").binding = {
        properties: {
            data: { set: true },
            width: { get: true, set: true },
            height: { get: true, set: true },
            mipmaps: { get: true, set: true },
            format: { get: true, set: true }
        },
        createEmptyConstructor: true
        //destructor: "UnloadImage"
    };
    getStruct(api.structs, "Wave").binding = {
        properties: {
            frameCount: { get: true },
            sampleRate: { get: true },
            sampleSize: { get: true },
            channels: { get: true }
        },
        //destructor: "UnloadWave"
    };
    getStruct(api.structs, "Sound").binding = {
        properties: {
            frameCount: { get: true }
        },
        //destructor: "UnloadSound"
    };
    getStruct(api.structs, "Music").binding = {
        properties: {
            frameCount: { get: true },
            looping: { get: true, set: true },
            ctxType: { get: true },
        },
        //destructor: "UnloadMusicStream"
    };
    getStruct(api.structs, "Model").binding = {
        properties: {
            transform: { get: true, set: true },
            meshCount: { get: true },
            materialCount: { get: true },
            boneCount: { get: true },
        },
        //destructor: "UnloadModel"
    };
    getStruct(api.structs, "Mesh").binding = {
        properties: {
            vertexCount: { get: true, set: true },
            triangleCount: { get: true, set: true },
            // TODO: Free previous pointers before overwriting
            vertices: { set: true },
            texcoords: { set: true },
            texcoords2: { set: true },
            normals: { set: true },
            tangents: { set: true },
            colors: { set: true },
            indices: { set: true },
            animVertices: { set: true },
            animNormals: { set: true },
            boneIds: { set: true },
            boneWeights: { set: true },
        },
        createEmptyConstructor: true
        //destructor: "UnloadMesh"
    };
    getStruct(api.structs, "Shader").binding = {
        properties: {
            id: { get: true }
        },
        //destructor: "UnloadShader"
    };
    getStruct(api.structs, "Texture").binding = {
        properties: {
            width: { get: true },
            height: { get: true },
            mipmaps: { get: true },
            format: { get: true },
        },
        aliases: getAliases(api.aliases, "Texture")
        //destructor: "UnloadTexture"
    };
    getStruct(api.structs, "Font").binding = {
        properties: {
            baseSize: { get: true },
            glyphCount: { get: true },
            glyphPadding: { get: true },
        },
        //destructor: "UnloadFont"
    };
    getStruct(api.structs, "RenderTexture").binding = {
        properties: {
            id: { get: true },
            texture: { get: true },
            depth: { get: true },
        },
        aliases: getAliases(api.aliases, "RenderTexture")
        //destructor: "UnloadRenderTexture"
    };
    getStruct(api.structs, "MaterialMap").binding = {
        properties: {
            texture: { set: true },
            color: { set: true, get: true },
            value: { get: true, set: true }
        },
        //destructor: "UnloadMaterialMap"
    };
    getStruct(api.structs, "Material").binding = {
        properties: {
            shader: { get: true, set: true }
        },
        //destructor: "UnloadMaterial"
    };
    const structDI = getStruct(api.structs, "VrDeviceInfo");
    structDI.fields.filter(x => x.name === "lensDistortionValues")[0].type = "Vector4";
    structDI.binding = {
        createEmptyConstructor: true,
        properties: {
            hResolution: { set: true, get: true },
            vResolution: { set: true, get: true },
            hScreenSize: { set: true, get: true },
            vScreenSize: { set: true, get: true },
            vScreenCenter: { set: true, get: true },
            eyeToScreenDistance: { set: true, get: true },
            lensSeparationDistance: { set: true, get: true },
            interpupillaryDistance: { set: true, get: true },
            // lensDistortionValues: { 
            //     set: true, 
            //     get: true, 
            //     overrideRead(fn) {
            //         fn.line("// TODO")
            //     },
            //     overrideWrite(fn) {
            //         fn.line("// TODO")
            //     },
            // },
        }
    };
    getFunction(api.functions, "EndDrawing").binding = { after: gen => gen.call("app_update_quickjs", []) };
    ignore("SetWindowIcons");
    ignore("GetWindowHandle");
    // Custom frame control functions
    // NOT SUPPORTED BECAUSE NEEDS COMPILER FLAG
    ignore("SwapScreenBuffer");
    ignore("PollInputEvents");
    ignore("WaitTime");
    //ignore("BeginVrStereoMode")
    //ignore("EndVrStereoMode")
    //ignore("LoadVrStereoConfig")
    //ignore("UnloadVrStereoConfig")
    getFunction(api.functions, "SetShaderValue").binding = { body: (gen) => {
            gen.jsToC("Shader", "shader", "argv[0]", core.structLookup);
            gen.jsToC("int", "locIndex", "argv[1]", core.structLookup);
            gen.declare("value", "void *", false, "NULL");
            gen.declare("valueFloat", "float");
            gen.declare("valueInt", "int");
            gen.jsToC("int", "uniformType", "argv[3]", core.structLookup);
            const sw = gen.switch("uniformType");
            let b = sw.caseBreak("SHADER_UNIFORM_FLOAT");
            b.jsToC("float", "valueFloat", "argv[2]", core.structLookup, true);
            b.statement("value = (void *)&valueFloat");
            b = sw.caseBreak("SHADER_UNIFORM_VEC2");
            b.jsToC("Vector2 *", "valueV2", "argv[2]", core.structLookup);
            b.statement("value = (void*)valueV2");
            b = sw.caseBreak("SHADER_UNIFORM_VEC3");
            b.jsToC("Vector3 *", "valueV3", "argv[2]", core.structLookup);
            b.statement("value = (void*)valueV3");
            b = sw.caseBreak("SHADER_UNIFORM_VEC4");
            b.jsToC("Vector4 *", "valueV4", "argv[2]", core.structLookup);
            b.statement("value = (void*)valueV4");
            b = sw.caseBreak("SHADER_UNIFORM_INT");
            b.jsToC("int", "valueInt", "argv[2]", core.structLookup, true);
            b.statement("value = (void*)&valueInt");
            b = sw.defaultBreak();
            b.returnExp("JS_EXCEPTION");
            gen.call("SetShaderValue", ["shader", "locIndex", "value", "uniformType"]);
            gen.returnExp("JS_UNDEFINED");
        } };
    ignore("SetShaderValueV");
    const traceLog = getFunction(api.functions, "TraceLog");
    traceLog.params?.pop();
    // Memory functions not supported on JS, just use ArrayBuffer
    ignore("MemAlloc");
    ignore("MemRealloc");
    ignore("MemFree");
    // Callbacks not supported on JS
    ignore("SetTraceLogCallback");
    ignore("SetLoadFileDataCallback");
    ignore("SetSaveFileDataCallback");
    ignore("SetLoadFileTextCallback");
    ignore("SetSaveFileTextCallback");
    // Files management functions
    const lfd = getFunction(api.functions, "LoadFileData");
    lfd.params[lfd.params.length - 1].binding = { ignore: true };
    lfd.binding = {
        body: gen => {
            gen.jsToC("const char *", "fileName", "argv[0]");
            gen.declare("bytesRead", "unsigned int");
            gen.call("LoadFileData", ["fileName", "&bytesRead"], { type: "unsigned char *", name: "retVal" });
            gen.statement("JSValue buffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*)retVal, bytesRead)");
            gen.call("UnloadFileData", ["retVal"]);
            gen.jsCleanUpParameter("const char*", "fileName");
            gen.returnExp("buffer");
        }
    };
    ignore("UnloadFileData");
    // TODO: SaveFileData works but unnecessary makes copy of memory
    getFunction(api.functions, "SaveFileData").binding = {};
    ignore("ExportDataAsCode");
    getFunction(api.functions, "LoadFileText").binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) };
    getFunction(api.functions, "SaveFileText").params[1].binding = { typeAlias: "const char *" };
    ignore("UnloadFileText");
    const createFileList = (gen, loadName, unloadName, args) => {
        gen.call(loadName, args, { type: "FilePathList", name: "files" });
        gen.call("JS_NewArray", ["ctx"], { type: "JSValue", name: "ret" });
        const f = gen.for("i", "files.count");
        f.call("JS_SetPropertyUint32", ["ctx", "ret", "i", "JS_NewString(ctx,files.paths[i])"]);
        gen.call(unloadName, ["files"]);
    };
    getFunction(api.functions, "LoadDirectoryFiles").binding = {
        jsReturns: "string[]",
        body: gen => {
            gen.jsToC("const char *", "dirPath", "argv[0]");
            createFileList(gen, "LoadDirectoryFiles", "UnloadDirectoryFiles", ["dirPath"]);
            gen.jsCleanUpParameter("const char *", "dirPath");
            gen.returnExp("ret");
        }
    };
    getFunction(api.functions, "LoadDirectoryFilesEx").binding = {
        jsReturns: "string[]",
        body: gen => {
            gen.jsToC("const char *", "basePath", "argv[0]");
            gen.jsToC("const char *", "filter", "argv[1]");
            gen.jsToC("bool", "scanSubdirs", "argv[2]");
            createFileList(gen, "LoadDirectoryFilesEx", "UnloadDirectoryFiles", ["basePath", "filter", "scanSubdirs"]);
            gen.jsCleanUpParameter("const char *", "basePath");
            gen.jsCleanUpParameter("const char *", "filter");
            gen.returnExp("ret");
        }
    };
    ignore("UnloadDirectoryFiles");
    getFunction(api.functions, "LoadDroppedFiles").binding = {
        jsReturns: "string[]",
        body: gen => {
            createFileList(gen, "LoadDroppedFiles", "UnloadDroppedFiles", []);
            gen.returnExp("ret");
        }
    };
    ignore("UnloadDroppedFiles");
    // Compression/encoding functionality
    ignore("CompressData");
    ignore("DecompressData");
    ignore("EncodeDataBase64");
    ignore("DecodeDataBase64");
    ignore("DrawLineStrip");
    ignore("DrawTriangleFan");
    ignore("DrawTriangleStrip");
    ignore("CheckCollisionPointPoly");
    ignore("CheckCollisionLines");
    ignore("LoadImageAnim");
    ignore("ExportImageAsCode");
    getFunction(api.functions, "LoadImageColors").binding = {
        jsReturns: "ArrayBuffer",
        body: gen => {
            gen.jsToC("Image", "image", "argv[0]", core.structLookup);
            gen.call("LoadImageColors", ["image"], { name: "colors", type: "Color *" });
            gen.statement("JSValue retVal = JS_NewArrayBufferCopy(ctx, (const uint8_t*)colors, image.width*image.height*sizeof(Color))");
            gen.call("UnloadImageColors", ["colors"]);
            gen.returnExp("retVal");
        }
    };
    ignore("LoadImagePalette");
    ignore("UnloadImageColors");
    ignore("UnloadImagePalette");
    ignore("GetPixelColor");
    ignore("SetPixelColor");
    const lfx = getFunction(api.functions, "LoadFontEx");
    lfx.params[2].binding = { ignore: true };
    lfx.params[3].binding = { ignore: true };
    lfx.binding = { customizeCall: "Font returnVal = LoadFontEx(fileName, fontSize, NULL, 0);" };
    ignore("LoadFontFromMemory");
    ignore("LoadFontData");
    ignore("GenImageFontAtlas");
    ignore("UnloadFontData");
    ignore("ExportFontAsCode");
    ignore("DrawTextCodepoints");
    ignore("GetGlyphInfo");
    ignore("LoadUTF8");
    ignore("UnloadUTF8");
    ignore("LoadCodepoints");
    ignore("UnloadCodepoints");
    ignore("GetCodepointCount");
    ignore("GetCodepoint");
    ignore("GetCodepointNext");
    ignore("GetCodepointPrevious");
    ignore("CodepointToUTF8");
    // Not supported, use JS Stdlib instead
    api.functions.filter(x => x.name.startsWith("Text")).forEach(x => ignore(x.name));
    ignore("DrawTriangleStrip3D");
    ignore("LoadMaterials");
    ignore("LoadModelAnimations");
    ignore("UpdateModelAnimation");
    ignore("UnloadModelAnimation");
    ignore("UnloadModelAnimations");
    ignore("IsModelAnimationValid");
    ignore("ExportWaveAsCode");
    // Wave/Sound management functions
    ignore("LoadWaveSamples");
    ignore("UnloadWaveSamples");
    ignore("LoadMusicStreamFromMemory");
    ignore("LoadAudioStream");
    ignore("IsAudioStreamReady");
    ignore("UnloadAudioStream");
    ignore("UpdateAudioStream");
    ignore("IsAudioStreamProcessed");
    ignore("PlayAudioStream");
    ignore("PauseAudioStream");
    ignore("ResumeAudioStream");
    ignore("IsAudioStreamPlaying");
    ignore("StopAudioStream");
    ignore("SetAudioStreamVolume");
    ignore("SetAudioStreamPitch");
    ignore("SetAudioStreamPan");
    ignore("SetAudioStreamBufferSizeDefault");
    ignore("SetAudioStreamCallback");
    ignore("AttachAudioStreamProcessor");
    ignore("DetachAudioStreamProcessor");
    ignore("AttachAudioMixedProcessor");
    ignore("DetachAudioMixedProcessor");
    ignore("Vector3OrthoNormalize");
    ignore("Vector3ToFloatV");
    ignore("MatrixToFloatV");
    ignore("QuaternionToAxisAngle");
    core.exportGlobalDouble("DEG2RAD", "(PI/180.0)");
    core.exportGlobalDouble("RAD2DEG", "(180.0/PI)");
    const setOutParam = (fun, index) => {
        const param = fun.params[index];
        param.binding = {
            jsType: `{ ${param.name}: number }`,
            customConverter: (gen, src) => {
                gen.declare(param.name, param.type, false, "NULL");
                gen.declare(param.name + "_out", param.type.replace(" *", ""));
                const body = gen.if("!JS_IsNull(" + src + ")");
                body.statement(param.name + " = &" + param.name + "_out");
                body.call("JS_GetPropertyStr", ["ctx", src, '"' + param.name + '"'], { name: param.name + "_js", type: "JSValue" });
                body.call("JS_ToInt32", ["ctx", param.name, param.name + "_js"]);
            },
            customCleanup: (gen, src) => {
                const body = gen.if("!JS_IsNull(" + src + ")");
                body.call("JS_SetPropertyStr", ["ctx", src, `"${param.name}"`, "JS_NewInt32(ctx," + param.name + "_out)"]);
            }
        };
    };
    const setOutParamString = (fun, index, indexLen) => {
        const lenParam = fun.params[indexLen];
        lenParam.binding = { ignore: true };
        const param = fun.params[index];
        param.binding = {
            jsType: `{ ${param.name}: string }`,
            customConverter: (gen, src) => {
                gen.call("JS_GetPropertyStr", ["ctx", src, '"' + param.name + '"'], { name: param.name + "_js", type: "JSValue" });
                gen.declare(param.name + "_len", "size_t");
                gen.call("JS_ToCStringLen", ["ctx", "&" + param.name + "_len", param.name + "_js"], { name: param.name + "_val", type: "const char *" });
                gen.call("memcpy", ["(void *)textbuffer", param.name + "_val", param.name + "_len"]);
                gen.statement("textbuffer[" + param.name + "_len] = 0");
                gen.declare(param.name, param.type, false, "textbuffer");
                gen.declare(lenParam.name, lenParam.type, false, "4096");
            },
            customCleanup: (gen, src) => {
                gen.jsCleanUpParameter("const char *", param.name + "_val");
                gen.call("JS_SetPropertyStr", ["ctx", src, `"${param.name}"`, "JS_NewString(ctx," + param.name + ")"]);
            }
        };
    };
    core.definitions.declare("textbuffer[4096]", "char", true);
    setOutParam(getFunction(api.functions, "GuiDropdownBox"), 2);
    setOutParam(getFunction(api.functions, "GuiSpinner"), 2);
    setOutParam(getFunction(api.functions, "GuiValueBox"), 2);
    setOutParam(getFunction(api.functions, "GuiListView"), 2);
    // const setStringListParam = (fun: RayLibFunction, index: number, indexLen: number) => {
    //     const lenParam = fun!.params![indexLen]
    //     lenParam.binding = { ignore: true }
    //     const param = fun!.params![index]
    //     fun.binding = { customizeCall: "int returnVal = GuiListViewEx(bounds, text, count, focus, scrollIndex, active);" }
    //     param.binding = { 
    //         jsType: `{ ${param.name}: string[] }`,
    //         customConverter: (gen,src) => {
    //             gen.line("// TODO: Read string values")
    //         },
    //         customCleanup: (gen, src) => {
    //             gen.line("// TODO: Dispose strings")
    //         }             
    //     }
    // }
    //const glve = getFunction(api.functions, "GuiListViewEx")!
    //setStringListParam(glve, 1,2)
    //setOutParam(glve, 3)
    //setOutParam(glve, 4)
    ignore("GuiListViewEx");
    setOutParamString(getFunction(api.functions, "GuiTextBox"), 1, 2);
    const gtib = getFunction(api.functions, "GuiTextInputBox");
    setOutParamString(gtib, 4, 5);
    setOutParam(gtib, 6);
    // needs string array
    ignore("GuiTabBar");
    ignore("GuiGetIcons");
    ignore("GuiLoadIcons");
    api.structs.forEach(x => core.addApiStruct(x));
    api.functions.forEach(x => core.addApiFunction(x));
    api.defines.filter(x => x.type === "COLOR").map(x => ({ name: x.name, description: x.description, values: (x.value.match(/\{([^}]+)\}/) || "")[1].split(',').map(x => x.trim()) })).forEach(x => {
        core.exportGlobalStruct("Color", x.name, x.values, x.description);
    });
    api.enums.forEach(x => core.addEnum(x));
    core.exportGlobalInt("MATERIAL_MAP_DIFFUSE", "Albedo material (same as: MATERIAL_MAP_DIFFUSE");
    core.exportGlobalInt("MATERIAL_MAP_SPECULAR", "Metalness material (same as: MATERIAL_MAP_SPECULAR)");
    core.writeTo("src/bindings/js_raylib_core.h");
    core.typings.writeTo("examples/lib.raylib.d.ts");
    const ignored = api.functions.filter(x => x.binding?.ignore).length;
    console.log(`Converted ${api.functions.length - ignored} function. ${ignored} ignored`);
    console.log("Success!");
    // TODO: Expose PLatform defines
}
main();

})();

/******/ })()
;