"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OverlayPayload = exports.find_init = exports.OvlPayloadResult = void 0;
const PayloadType_1 = require("modloader64_api/PayloadType");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const smart_buffer_1 = require("smart-buffer");
const GameAliases_1 = require("./Common/types/GameAliases");
class OvlPayloadResult {
    slot;
    core;
    size;
    pointer;
    constructor(core, slot, pointer, size) {
        this.slot = slot;
        this.core = core;
        this.pointer = pointer;
        this.size = size;
    }
    spawnActorRXYZ(params, rotX, rotY, rotZ, pos, address) {
        return this.core.commandBuffer.spawnActorRXYZ(this.slot, params, rotX, rotY, rotZ, pos, address);
    }
    spawnActorRXY_Z(params, rotXY, rotZ, pos, address) {
        return this.core.commandBuffer.spawnActorRXY_Z(this.slot, params, rotXY, rotZ, pos, address);
    }
    spawn(params, rot, pos, address) {
        return this.core.commandBuffer.spawnActor(this.slot, params, rot, pos, address);
    }
}
exports.OvlPayloadResult = OvlPayloadResult;
class find_init {
    constructor() { }
    find(buf, locate) {
        let loc = Buffer.from(locate, 'hex');
        if (buf.indexOf(loc) > -1) {
            return buf.indexOf(loc);
        }
        return -1;
    }
}
exports.find_init = find_init;
class OverlayPayload extends PayloadType_1.PayloadType {
    ModLoader;
    core;
    constructor(ext, ModLoader, core) {
        super(ext);
        this.ModLoader = ModLoader;
        this.core = core;
    }
    parse(file, buf, dest) {
        this.ModLoader.logger.debug('Trying to allocate actor...');
        let overlay_start = GameAliases_1.Z64_OVERLAY_TABLE;
        let size = 0x01d6;
        let empty_slots = new Array();
        for (let i = 0; i < size; i++) {
            let entry_start = overlay_start + i * 0x20;
            let _i = dest.rdramRead32(entry_start + 0x14);
            let total = 0;
            total += _i;
            if (total === 0) {
                empty_slots.push(i);
            }
        }
        this.ModLoader.logger.debug(empty_slots.length + ' empty actor slots found.');
        let finder = new find_init();
        let meta = JSON.parse(fs_1.default
            .readFileSync(path_1.default.join(path_1.default.parse(file).dir, path_1.default.parse(file).name + '.json'))
            .toString());
        let offset = finder.find(buf, meta.init);
        if (offset === -1) {
            this.ModLoader.logger.debug('Failed to find spawn parameters for actor ' +
                path_1.default.parse(file).base +
                '.');
            return -1;
        }
        let slot = empty_slots.shift();
        if (meta.forceSlot !== undefined) {
            slot = parseInt(meta.forceSlot);
        }
        this.ModLoader.logger.debug('Assigning ' + path_1.default.parse(file).base + ' to slot ' + slot + '.');
        // Clean anything in here out first.
        for (let i = 0; i < 0x20; i++) {
            dest.rdramWrite8(slot * 0x20 + overlay_start + i, 0);
        }
        let final = this.core.ModLoader.gfx_heap.malloc(buf.byteLength + 0x10);
        dest.rdramWrite32(slot * 0x20 + overlay_start + 0x14, final + offset);
        buf.writeUInt16BE(slot, offset);
        let alloc = new smart_buffer_1.SmartBuffer();
        alloc.writeBuffer(buf);
        alloc.writeUInt32BE(final);
        alloc.writeUInt32BE(final + (buf.byteLength - buf.readUInt32BE(buf.byteLength - 0x4)));
        alloc.writeUInt32BE(0x80800000);
        alloc.writeUInt32BE(buf.byteLength);
        dest.rdramWriteBuffer(final, alloc.toBuffer());
        let hash = this.ModLoader.utils.hashBuffer(buf);
        this.ModLoader.utils.setTimeoutFrames(() => {
            this.core.commandBuffer.relocateOverlay(final, final + (buf.byteLength - buf.readUInt32BE(buf.byteLength - 0x4)), 0x80800000).then(() => {
                let hash2 = this.ModLoader.utils.hashBuffer(this.ModLoader.emulator.rdramReadBuffer(final, buf.byteLength));
                if (hash !== hash2) {
                    this.ModLoader.logger.info(`${path_1.default.parse(file).base} relocated.`);
                }
                else {
                    this.ModLoader.logger.error(`${path_1.default.parse(file).base} failed`);
                }
            });
        }, 20);
        return new OvlPayloadResult(this.core, slot, final, buf.byteLength);
    }
}
exports.OverlayPayload = OverlayPayload;
//# sourceMappingURL=OverlayPayload.js.map