"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandBuffer_Factory = exports.CommandBuffer = exports.JRRA = exports.COMMANDBUFFER_SIZEOF = exports.COMMAND_EVENT_OFFSET = exports.COMMAND_EVENT_SIZEOF = exports.COMMAND_RETURN_OFFSET = exports.COMMAND_RETURN_SIZEOF = exports.COMMAND_RETURN_DATA_SIZEOF = exports.COMMAND_OFFSET = exports.COMMAND_SIZEOF = exports.COMMAND_PARAM_SIZEOF = exports.COMMANDEVENT_MAX = exports.COMMAND_MAX = exports.CommandBuffer_CommandEventType = exports.CommandBuffer_CommandType = void 0;
const OpcodeBullshit_1 = require("./OpcodeBullshit");
const Deprecated_1 = require("modloader64_api/Deprecated");
const Actor_1 = require("../Actor");
const AssemblyList_1 = require("@Z64Lib/API/Common/AssemblyList");
const GameAliases_1 = require("../types/GameAliases");
const smart_buffer_1 = require("smart-buffer");
const OotAliases_1 = require("../types/OotAliases");
const MMAliases_1 = require("../types/MMAliases");
const MipsAssembler_1 = __importDefault(require("@Z64Lib/API/Utilities/MipsAssembler"));
const ModHook_1 = __importDefault(require("./ModHook"));
var CommandBuffer_CommandType;
(function (CommandBuffer_CommandType) {
    CommandBuffer_CommandType[CommandBuffer_CommandType["NONE"] = 0] = "NONE";
    CommandBuffer_CommandType[CommandBuffer_CommandType["ACTORSPAWN"] = 1] = "ACTORSPAWN";
    CommandBuffer_CommandType[CommandBuffer_CommandType["ACTORADDREMCAT"] = 2] = "ACTORADDREMCAT";
    CommandBuffer_CommandType[CommandBuffer_CommandType["RELOCATE"] = 3] = "RELOCATE";
    CommandBuffer_CommandType[CommandBuffer_CommandType["UPDATEBUTTON"] = 4] = "UPDATEBUTTON";
    CommandBuffer_CommandType[CommandBuffer_CommandType["PLAYSOUND"] = 5] = "PLAYSOUND";
    CommandBuffer_CommandType[CommandBuffer_CommandType["WARP"] = 6] = "WARP";
    CommandBuffer_CommandType[CommandBuffer_CommandType["MOVEPLAYERTOADDRESS"] = 7] = "MOVEPLAYERTOADDRESS";
    CommandBuffer_CommandType[CommandBuffer_CommandType["ARBITRARYFUNCTIONCALL"] = 8] = "ARBITRARYFUNCTIONCALL";
    CommandBuffer_CommandType[CommandBuffer_CommandType["PVPDAMAGE"] = 9] = "PVPDAMAGE";
    CommandBuffer_CommandType[CommandBuffer_CommandType["MALLOCFREE"] = 10] = "MALLOCFREE";
    CommandBuffer_CommandType[CommandBuffer_CommandType["OBJECTLOAD"] = 11] = "OBJECTLOAD";
    CommandBuffer_CommandType[CommandBuffer_CommandType["SPAWN_NO_EVENT"] = 12] = "SPAWN_NO_EVENT";
})(CommandBuffer_CommandType = exports.CommandBuffer_CommandType || (exports.CommandBuffer_CommandType = {}));
var CommandBuffer_CommandEventType;
(function (CommandBuffer_CommandEventType) {
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["NONE"] = 0] = "NONE";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["INIT"] = 1] = "INIT";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["SPAWN"] = 2] = "SPAWN";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["SPAWNENTRY"] = 3] = "SPAWNENTRY";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["SPAWNTRANSITION"] = 4] = "SPAWNTRANSITION";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["ADDCAT"] = 5] = "ADDCAT";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["REMCAT"] = 6] = "REMCAT";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["DESTROY"] = 7] = "DESTROY";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["UPDATE"] = 8] = "UPDATE";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["OBJECTSPAWN"] = 9] = "OBJECTSPAWN";
    CommandBuffer_CommandEventType[CommandBuffer_CommandEventType["ERROR_FILLED"] = 10] = "ERROR_FILLED";
})(CommandBuffer_CommandEventType = exports.CommandBuffer_CommandEventType || (exports.CommandBuffer_CommandEventType = {}));
exports.COMMAND_MAX = 256;
exports.COMMANDEVENT_MAX = 1024;
exports.COMMAND_PARAM_SIZEOF = 0x20;
exports.COMMAND_SIZEOF = exports.COMMAND_PARAM_SIZEOF + 8;
exports.COMMAND_OFFSET = 4;
exports.COMMAND_RETURN_DATA_SIZEOF = 0x04;
exports.COMMAND_RETURN_SIZEOF = exports.COMMAND_RETURN_DATA_SIZEOF + 8;
exports.COMMAND_RETURN_OFFSET = exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * exports.COMMAND_MAX;
exports.COMMAND_EVENT_SIZEOF = 0xC;
exports.COMMAND_EVENT_OFFSET = exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * exports.COMMAND_MAX;
exports.COMMANDBUFFER_SIZEOF = 0x6404;
exports.JRRA = MipsAssembler_1.default.assemble(`
    NOP
    JR RA
    NOP
`);
class CommandBufferBootstrap {
    ModLoader;
    core;
    hax;
    payloadPointer = -1;
    bootstrapPointer = -1;
    data;
    revision;
    commands = -1;
    constructor(ModLoader, core, hax, data, revision) {
        this.ModLoader = ModLoader;
        this.core = core;
        this.hax = hax;
        this.data = data;
        this.revision = revision;
    }
    inject() {
        if (this.payloadPointer > 0)
            return;
        /**
         * Injecting the command buffer is a two step process.
         * Start of Step 1
         */
        this.ModLoader.logger.debug("Starting command buffer bootstrap sequence...");
        /**
         * Malloc the space for the buffer itself.
         */
        this.commands = this.ModLoader.heap.malloc(exports.COMMANDBUFFER_SIZEOF);
        let wipe = Buffer.alloc(exports.COMMANDBUFFER_SIZEOF);
        this.ModLoader.emulator.rdramWriteBuffer(this.commands, wipe);
        /**
         * Malloc and write the command buffer overlay
         */
        this.payloadPointer = this.ModLoader.heap.malloc(this.hax.byteLength);
        this.ModLoader.emulator.rdramWriteBuffer(this.payloadPointer, this.hax); // initialize memory to 0
        /**
         * Find the dummy pointer inside the overlay and replace it.
         */
        let badfood = this.hax.indexOf(Buffer.from("0BADF00D", 'hex'));
        this.ModLoader.emulator.rdramWrite32(this.payloadPointer + badfood, this.commands);
        /**
         * This is the bootstrap assembly. This runs for a single frame and runs the Overlay_Reallocate function...
         * ... on the command buffer overlay.
         */
        let variableNameOfAllocatedVRamAddress = this.payloadPointer;
        let variableNameOfOverlayInfo = this.payloadPointer + (this.hax.byteLength - this.hax.readUInt32BE(this.hax.byteLength - 0x4));
        let variableNameOfVRamAddress = 0x80800000;
        let addressOfThisGamesOverlay_Relocate = this.data.VERSIONS.get(this.revision).get("Overlay_Relocate");
        let bootstrapasm = `
        .definelabel allocatedVRamAddress,0x${variableNameOfAllocatedVRamAddress.toString(16)}
        .definelabel overlayInfo,0x${variableNameOfOverlayInfo.toString(16)}
        .definelabel vRamAddress,0x${variableNameOfVRamAddress.toString(16)}
        .definelabel Overlay_Relocate,0x${addressOfThisGamesOverlay_Relocate.toString(16)}
        
        bootstrap:
        ADDIU   SP SP -0x08 ; stack header, 4 bytes of padding, 4 to store the return address
        SW      RA 0x04(SP) ; store return address
        LUI     A0 hi(allocatedVRamAddress) ; arg0 is allocatedVRamAddress
        ADDIU   A0 A0 lo(allocatedVRamAddress)
        LUI     A1 hi(overlayInfo) ; arg1 is overlayInfo
        ADDIU   A1 A1 lo(overlayInfo)
        LUI     A2 hi(vRamAddress) ; arg2 is vRamAddress
        JAL     Overlay_Relocate
        ADDIU   A2 A2 lo(vRamAddress) ; delay slot
        LW      RA 0x04(SP) ; restore return address
        JR      RA  ; abort mission, mission successful?
        ADDIU   SP SP 0x08 ; stack footer (delay slot)
        `;
        let bootstrap = MipsAssembler_1.default.assemble(bootstrapasm);
        this.bootstrapPointer = this.ModLoader.heap.malloc(bootstrap.byteLength);
        this.ModLoader.emulator.rdramWriteBuffer(this.bootstrapPointer, bootstrap);
        /**
         * Write a JAL to our entry point. We will overwrite this later with the actual main loop of our overlay.
         * This delays the game starting by one frame as the game's main overlay isn't processed.
         */
        let sb = new smart_buffer_1.SmartBuffer();
        sb.writeUInt32BE((0, OpcodeBullshit_1.JAL_ENCODE)(this.bootstrapPointer));
        this.ModLoader.emulator.rdramWriteBuffer(this.data.VERSIONS.get(this.revision).get("CommandBuffer_Update"), sb.toBuffer());
        sb.clear();
        this.ModLoader.emulator.invalidateCachedCode();
        /**
         * Start of Step 2
         * This needs to happen in 1 frame so we put it in a timeout.
         */
        this.ModLoader.utils.setTimeoutFrames(() => {
            this.ModLoader.logger.debug("Command buffer bootstrap step 2...");
            /**
             * Find our struct inside the overlay that contains all our function pointers.
             */
            let embedStart = this.hax.indexOf(Buffer.from("DEADBEEF", 'hex'));
            let instance = this.payloadPointer + embedStart;
            /**
             * Overwrite the JAL we wrote earlier with a new one to our main loop function inside the overlay.
             * This also resumes the game's main overlay.
             */
            sb.writeUInt32BE((0, OpcodeBullshit_1.JAL_ENCODE)(this.ModLoader.emulator.rdramRead32(instance + 0x4)));
            this.ModLoader.emulator.rdramWriteBuffer(this.data.VERSIONS.get(this.revision).get("CommandBuffer_Update"), sb.toBuffer());
            sb.clear();
            /**
             * Local helper function. Generates a J and a NOP immediately following.
             * @param pointer Base pointer to get the address to J to (double pointer)
             * @param offset Offset from pointer
             * @param tag Name of the target. See IInjectedAssembly.
             * @returns void
             */
            let JNOP = (pointer, offset, tag) => {
                if (!this.data.VERSIONS.get(this.revision).has(tag))
                    return;
                if (this.data.VERSIONS.get(this.revision).get(tag) === 0xDEADBEEF)
                    return;
                sb.writeUInt32BE((0, OpcodeBullshit_1.J_ENCODE)(this.ModLoader.emulator.rdramRead32(pointer + offset)));
                sb.writeBuffer(exports.JRRA);
                this.ModLoader.emulator.rdramWriteBuffer(this.data.VERSIONS.get(this.revision).get(tag), sb.toBuffer());
                sb.clear();
            };
            /**
             * Local helper function. Generates a JAL.
             * @param pointer Base pointer to get the address to J to (double pointer)
             * @param offset Offset from pointer
             * @param tag Name of the target. See IInjectedAssembly.
             * @returns void
             */
            let JAL = (pointer, offset, tag) => {
                if (!this.data.VERSIONS.get(this.revision).has(tag))
                    return;
                if (this.data.VERSIONS.get(this.revision).get(tag) === 0xDEADBEEF)
                    return;
                sb.writeUInt32BE((0, OpcodeBullshit_1.JAL_ENCODE)(this.ModLoader.emulator.rdramRead32(pointer + offset)));
                this.ModLoader.emulator.rdramWriteBuffer(this.data.VERSIONS.get(this.revision).get(tag), sb.toBuffer());
                sb.clear();
            };
            /**
             * This section hooks several game functions in order to feed data...
             * ... to the event system.
             */
            JNOP(instance, 0x08, "Actor_SpawnCave");
            //JNOP(instance, 0x2C, "Actor_AddToCategory");
            //JNOP(instance, 0x30, "Actor_RemoveFromCategory");
            //JAL(instance, 0x18, "Actor_UpdateCave");
            if (GameAliases_1.Z64_GAME === OotAliases_1.OOT_GAME) {
                //JAL(instance, 0x1C, "Actor_SpawnTransitionActorCave");
                JAL(instance, 0x0C, "Actor_DestroyCave");
                //JAL(instance, 0x10, "Actor_SpawnEntryCave");
            }
            else if (GameAliases_1.Z64_GAME === MMAliases_1.MM_GAME) {
                JNOP(instance, 0x1C, "Actor_SpawnTransitionActorCave");
                JNOP(instance, 0x10, "Actor_SpawnEntryCave");
                JNOP(instance, 0x20, "Actor_SpawnWithParentAndCutscene");
                if (GameAliases_1.Z64_IS_RANDOMIZER) {
                    if (this.data.VERSIONS.get(this.revision).has("Actor_DestroyCave")) {
                        if (this.data.VERSIONS.get(this.revision).get("Actor_DestroyCave") === 0xDEADBEEF) {
                            let Actor_DestroyCave_Addr = this.data.VERSIONS.get(this.revision).get("Actor_DestroyCave");
                            if (Actor_DestroyCave_Addr !== undefined) {
                                let Actor_AfterDtor_Hook = (0, OpcodeBullshit_1.JAL_DECODE)(this.ModLoader.emulator.rdramReadBuffer(Actor_DestroyCave_Addr + 4, 4));
                                this.ModLoader.emulator.rdramWrite32(Actor_AfterDtor_Hook + 8, (0, OpcodeBullshit_1.JAL_ENCODE)(this.ModLoader.emulator.rdramRead32(instance + 0x0C)));
                            }
                        }
                    }
                }
                else {
                    JAL(instance, 0x0C, "Actor_DestroyCave");
                }
            }
            // Object spawn would go here but its broke. 0x24
            /**
             * Set this for other possible hooks.
             */
            //setSpawnWithAddrPointer(this.ModLoader.emulator.rdramRead32(instance + 0x34));
            /**
             *
             */
            ModHook_1.default.MODHOOK_ADDR = instance + 0x38;
            this.ModLoader.emulator.invalidateCachedCode();
        }, 1);
        this.ModLoader.logger.debug(`Command Buffer: ${this.commands.toString(16)}`);
        return this.commands;
    }
}
class CommandBuffer {
    ModLoader;
    core;
    cmdbuf;
    uuid = 0;
    constructor(ModLoader, revision, game, core) {
        this.ModLoader = ModLoader;
        this.cmdbuf = CommandBuffer_Factory.Inject(this.ModLoader, core, this, this.ModLoader.heap, revision, AssemblyList_1.AssemblyList.getAssemblyForGame(game));
        this.core = core;
    }
    runCommand(command, param, callback) { }
    spawnActor(actorId, params, rot, pos, address = 0) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.ACTORSPAWN);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite16(offset + 8, actorId);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 2, params);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 4, Math.floor(rot.x) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 6, Math.floor(rot.y) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 8, Math.floor(rot.z) % 32768);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0xC, pos.x);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x10, pos.y);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x14, pos.z);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 0x18, address);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === CommandBuffer_CommandType.ACTORSPAWN && uuid === myUUID) {
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        if (this.ModLoader.emulator.rdramRead32(offset + 8) === 0) {
                            reject("Actor pointer was zero.");
                        }
                        else {
                            accept(new Actor_1.ActorBase(this.ModLoader, this.core.global.scene, this.ModLoader.emulator.rdramRead32(offset + 8)));
                        }
                    }
                }
                reject("Failed to find return value.");
            }, 1);
        });
    }
    spawnActorQuietly(actorId, params, rot, pos, address = 0) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.SPAWN_NO_EVENT);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite16(offset + 8, actorId);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 2, params);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 4, Math.floor(rot.x) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 6, Math.floor(rot.y) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 8, Math.floor(rot.z) % 32768);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0xC, pos.x);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x10, pos.y);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x14, pos.z);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 0x18, address);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === CommandBuffer_CommandType.SPAWN_NO_EVENT && uuid === myUUID) {
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        if (this.ModLoader.emulator.rdramRead32(offset + 8) === 0) {
                            reject("Actor pointer was zero.");
                        }
                        else {
                            accept(new Actor_1.ActorBase(this.ModLoader, this.core.global.scene, this.ModLoader.emulator.rdramRead32(offset + 8)));
                        }
                    }
                }
                reject("Failed to find return value.");
            }, 1);
        });
    }
    spawnActorRXYZ(actorId, params, rotX, rotY, rotZ, pos, address = 0) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.ACTORSPAWN);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite16(offset + 8, actorId);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 2, params);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 4, Math.floor(rotX) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 6, Math.floor(rotY) % 32768);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 8, Math.floor(rotZ) % 32768);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0xc, pos.x);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x10, pos.y);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x14, pos.z);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 0x18, address);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === CommandBuffer_CommandType.ACTORSPAWN && uuid === myUUID) {
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        if (this.ModLoader.emulator.rdramRead32(offset + 8) === 0) {
                            reject("Actor pointer was zero.");
                        }
                        else {
                            accept(new Actor_1.ActorBase(this.ModLoader, this.core.global.scene, this.ModLoader.emulator.rdramRead32(offset + 8)));
                        }
                    }
                }
                reject("Failed to find return value.");
            }, 1);
        });
    }
    spawnActorRXY_Z(actorId, params, rotXY, rotZ, pos, address = 0) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.ACTORSPAWN);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite16(offset + 8, actorId);
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 2, params);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 4, Math.floor(rotXY));
        this.ModLoader.emulator.rdramWrite16(offset + 8 + 8, Math.floor(rotZ) % 32768);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0xc, pos.x);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x10, pos.y);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x14, pos.z);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 0x18, address);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    if (type === 0)
                        continue;
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === CommandBuffer_CommandType.ACTORSPAWN && uuid === myUUID) {
                        let addr = this.ModLoader.emulator.rdramRead32(offset + 8);
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        if (addr === 0) {
                            reject("Actor pointer was zero.");
                        }
                        else {
                            accept(new Actor_1.ActorBase(this.ModLoader, this.core.global.scene, addr));
                        }
                    }
                }
                reject("Failed to find return value.");
            }, 1);
        });
    }
    relocateOverlay(allocatedVRamAddress, overlayInfoPointer, vRamAddress) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.RELOCATE);
        this.ModLoader.emulator.rdramWrite32(offset + 8, allocatedVRamAddress);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 4, overlayInfoPointer);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 8, vRamAddress);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                accept();
            }, 1);
        });
    }
    updateButton(button) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.UPDATEBUTTON);
        this.ModLoader.emulator.rdramWrite16(offset + 8, button);
    }
    playSound(sfxId, a1, a2, a3, a4, a5) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.PLAYSOUND);
        this.ModLoader.emulator.rdramWrite16(offset + 8, sfxId);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x4, a1.x);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x8, a1.y);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0xc, a1.z);
        this.ModLoader.emulator.rdramWrite8(offset + 8 + 0x10, a2);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x14, a3);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x18, a4);
        this.ModLoader.emulator.rdramWriteF32(offset + 8 + 0x1c, a5);
    }
    runWarp(entranceIndex, cutsceneIndex, age, transition) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.WARP);
        this.ModLoader.emulator.rdramWrite32(offset + 8, entranceIndex);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 4, cutsceneIndex);
        if (age === undefined) {
            this.ModLoader.emulator.rdramWrite32(offset + 8 + 8, 0xFFFFFFFF);
        }
        else {
            this.ModLoader.emulator.rdramWrite32(offset + 8 + 8, age);
        }
        if (transition === undefined) {
            this.ModLoader.emulator.rdramWrite32(offset + 8 + 0xC, 0xFFFFFFFF);
        }
        else {
            this.ModLoader.emulator.rdramWrite32(offset + 8 + 0xC, transition);
        }
        return new Promise((accept, reject) => {
            accept(true);
        });
    }
    movePlayerActorToAddress(address) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.MOVEPLAYERTOADDRESS);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite32(offset + 8, address);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === 0)
                        continue;
                    if (type === CommandBuffer_CommandType.MOVEPLAYERTOADDRESS && uuid === myUUID) {
                        let addr = this.ModLoader.emulator.rdramRead32(offset + 8);
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        if (addr === 0) {
                            reject("Player pointer was zero.");
                        }
                        else {
                            accept(true);
                        }
                    }
                }
                reject("Failed to find return value.");
            }, 1);
        });
    }
    // supports up to 8 args (exclusing args that go in FP regs); returns function return value (v0)
    arbitraryFunctionCall(functionAddress, argsPointer, argsCount) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.ARBITRARYFUNCTIONCALL);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite32(offset + 8, functionAddress);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 4, argsPointer);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 8, argsCount);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    if (type === 0)
                        continue;
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === CommandBuffer_CommandType.ARBITRARYFUNCTIONCALL && uuid === myUUID) {
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        accept(this.ModLoader.emulator.rdramReadBuffer(offset + 8, 4));
                    }
                }
                reject("Failed to find return value!");
            }, 1);
        });
    }
    PvpDamage(context, invokingActorPointer) {
        console.warn("Not functional when we are using gamestate cave! Bug Drahsid to transition to the new system (low priority)");
    }
    // param malloc == 0: free, param malloc == 1: malloc, param malloc == 2 mallocR, data = address on free, otherwise size of malloc
    Zelda_MallocFree(malloc, data) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.MALLOCFREE);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite32(offset + 8, malloc);
        this.ModLoader.emulator.rdramWrite32(offset + 8 + 4, data);
        return new Promise((accept, reject) => {
            if (malloc) {
                this.ModLoader.utils.setTimeoutFrames(() => {
                    for (let index = 0; index < exports.COMMAND_MAX; index++) {
                        let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                        let type = this.ModLoader.emulator.rdramRead32(offset);
                        let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                        if (type === 0)
                            continue;
                        if (type === CommandBuffer_CommandType.MALLOCFREE && uuid === myUUID) {
                            let addr = this.ModLoader.emulator.rdramRead32(offset + 8);
                            this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                            if (addr === 0) {
                                reject(-1);
                            }
                            else {
                                accept(addr);
                            }
                        }
                    }
                }, 1);
            }
            else {
                accept(1);
            }
        });
    }
    ObjectLoad(objectId) {
        let count = this.ModLoader.emulator.rdramRead16(this.cmdbuf);
        let offset = this.cmdbuf + exports.COMMAND_OFFSET + exports.COMMAND_SIZEOF * count;
        let myUUID = this.uuid++;
        this.ModLoader.emulator.rdramWrite16(this.cmdbuf, count + 1);
        this.ModLoader.emulator.rdramWrite32(offset, CommandBuffer_CommandType.OBJECTLOAD);
        this.ModLoader.emulator.rdramWrite32(offset + 4, myUUID);
        this.ModLoader.emulator.rdramWrite16(offset + 8, objectId);
        return new Promise((accept, reject) => {
            this.ModLoader.utils.setTimeoutFrames(() => {
                for (let index = 0; index < exports.COMMAND_MAX; index++) {
                    let offset = this.cmdbuf + exports.COMMAND_RETURN_OFFSET + exports.COMMAND_RETURN_SIZEOF * index;
                    let type = this.ModLoader.emulator.rdramRead32(offset);
                    let uuid = this.ModLoader.emulator.rdramRead32(offset + 4);
                    if (type === 0)
                        continue;
                    if (type === CommandBuffer_CommandType.OBJECTLOAD && uuid === myUUID) {
                        let addr = this.ModLoader.emulator.rdramRead32(offset + 8);
                        this.ModLoader.emulator.rdramWrite32(offset, 0); // free return slot
                        let obj_list = global.ModLoader["obj_context"];
                        obj_list += 0xC;
                        let _offset = addr * 0x44;
                        obj_list += _offset;
                        obj_list += 0x4;
                        accept(this.ModLoader.emulator.rdramRead32(obj_list));
                    }
                }
                reject(-1);
            }, 1);
        });
    }
    onTick() {
    }
}
__decorate([
    (0, Deprecated_1.Deprecated)("CommandBuffer.runCommand is deprecated.")
], CommandBuffer.prototype, "runCommand", null);
exports.CommandBuffer = CommandBuffer;
class CommandBuffer_Factory {
    static cmdbuf = 0;
    static Inject(ModLoader, core, parent, heap, revision, inject) {
        let bootstrap = new CommandBufferBootstrap(ModLoader, core, inject.commandBuffer, inject, revision);
        this.cmdbuf = bootstrap.inject();
        return this.cmdbuf;
    }
}
exports.CommandBuffer_Factory = CommandBuffer_Factory;
//# sourceMappingURL=CommandBuffer.js.map