"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.trimBuffer = exports.Z64RomTools = void 0;
const PatchManager_1 = require("modloader64_api/Patchers/PatchManager");
const Z64LibSupportedGames_1 = require("./Z64LibSupportedGames");
const romheap_1 = require("modloader64_api/romheap");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
let VROM_END = 0x04000000;
const mb = Buffer.alloc(1 * 1024 * 1024);
let HAS_MOVED_CODE_FILE = false;
let heap;
class Z64RomTools {
    ModLoader;
    DMA_Offset;
    Particle_Offset;
    Code_DMA;
    DMA_DMA;
    Actor_Offset;
    Object_Offset;
    constructor(ModLoader, game) {
        this.ModLoader = ModLoader;
        switch (game) {
            case Z64LibSupportedGames_1.Z64LibSupportedGames.OCARINA_OF_TIME:
                this.DMA_Offset = 0x7430;
                this.Particle_Offset = 0xD6BA0;
                this.Code_DMA = 27;
                this.DMA_DMA = 2;
                this.Actor_Offset = 0xD7490;
                this.Object_Offset = 0xE7F58;
                break;
            case Z64LibSupportedGames_1.Z64LibSupportedGames.MAJORAS_MASK:
                this.DMA_Offset = 0x1A500;
                this.Particle_Offset = 0x1089E0;
                this.Code_DMA = 0x1F;
                this.DMA_DMA = 2;
                this.Actor_Offset = 0x109510;
                this.Object_Offset = 0x11CC80;
                break;
            case Z64LibSupportedGames_1.Z64LibSupportedGames.DEBUG_OF_TIME:
                this.DMA_Offset = 0x12F70;
                this.Particle_Offset = 0xF8B50;
                this.Code_DMA = 27;
                this.DMA_DMA = 2;
                this.Actor_Offset = 0xF9440;
                this.Object_Offset = 0x10A6C0;
                break;
        }
    }
    static getRomHeap() {
        return heap;
    }
    getCodeFile(rom) {
        if (!HAS_MOVED_CODE_FILE) {
            let code = this.decompressDMAFileFromRom(rom, this.Code_DMA);
            this.relocateFileToExtendedRom(rom, this.Code_DMA, code, code.byteLength, true);
            HAS_MOVED_CODE_FILE = true;
        }
        return this.decompressDMAFileFromRom(rom, this.Code_DMA);
    }
    isFileCompressed(rom, index) {
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size = rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        return isFileCompressed;
    }
    injectActorIntoSlot(rom, actorID, file, initvars) {
        let dma_id = this.findDMAIndexOfActor(rom, actorID);
        // Replace file and DMA entry first.
        if (this.recompressDMAFileIntoRom(rom, dma_id, file)) {
            this.relocateFileToExtendedRom(rom, dma_id, file);
        }
        // Check for init variables.
        let init = Buffer.from(initvars, 'hex');
        let init_start = file.indexOf(init);
        if (init_start === -1) {
            return false;
        }
        // Wipe entry in overlay table.
        let code = this.decompressDMAFileFromRom(rom, this.Code_DMA);
        for (let i = 0; i < (8 * 4); i++) {
            code.writeUInt8(0x0D7490 + (actorID * 0x20) + i, 0);
        }
        // Create new overlay table entry.
        let dma_data = this.getStartEndOfDMAEntry(rom, dma_id);
        code.writeUInt32BE(dma_data.vrom_start, 0x0D7490 + (actorID * 0x20) + 0x0);
        code.writeUInt32BE(dma_data.vrom_end, 0x0D7490 + (actorID * 0x20) + 0x4);
        code.writeUInt32BE(0x80800000, 0x0D7490 + (actorID * 0x20) + 0x8);
        code.writeUInt32BE(0x80800000 + file.byteLength, 0x0D7490 + (actorID * 0x20) + 0xC);
        code.writeUInt32BE(0, 0x0D7490 + (actorID * 0x20) + 0x10);
        code.writeUInt32BE(0x80800000 + init_start, 0x0D7490 + (actorID * 0x20) + 0x14);
        code.writeUInt32BE(0, 0x0D7490 + (actorID * 0x20) + 0x18);
        code.writeUInt32BE(0, 0x0D7490 + (actorID * 0x20) + 0x1C);
        return true;
    }
    getStartEndOfDMAEntry(rom, index) {
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        let vrom_start = rom.readUInt32BE(dma + offset + 0x0);
        let vrom_end = rom.readUInt32BE(dma + offset + 0x4);
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        return { vrom_start, vrom_end, start, end };
    }
    decompressDMAFileFromRom(rom, index) {
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        if (start === 0xFFFFFFFF) {
            return Buffer.alloc(0xFF);
        }
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size = rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        if (start === 0) {
            return Buffer.alloc(1);
        }
        let buf = Buffer.alloc(size);
        rom.copy(buf, 0, start, end);
        if (isFileCompressed) {
            if (buf.readUInt32BE(0) !== 0x59617A30) {
                return buf;
            }
            buf = this.ModLoader.utils.yaz0Decode(buf);
        }
        return buf;
    }
    getRawDMAFileFromRom(rom, index) {
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size =
                rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        let buf = Buffer.alloc(size);
        rom.copy(buf, 0, start, end);
        return buf;
    }
    recompressDMAFileIntoRom(rom, index, file) {
        let original = this.getRawDMAFileFromRom(rom, index);
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size = rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        if (isFileCompressed) {
            let buf = this.ModLoader.utils.yaz0Encode(file);
            if (buf.byteLength > original.byteLength) {
                return false;
            }
            buf.copy(rom, start);
        }
        else {
            if (file.byteLength > original.byteLength) {
                return false;
            }
            file.copy(rom, start);
        }
        return true;
    }
    findDMAIndexOfParticle(rom, index) {
        let code = this.decompressDMAFileFromRom(rom, this.Code_DMA);
        let dma = this.decompressDMAFileFromRom(rom, this.DMA_DMA);
        let offset = this.Particle_Offset + (index * 0x1C);
        let search = code.slice(offset, offset + 0x8);
        let dma_index = dma.indexOf(search) / 0x10;
        return dma_index;
    }
    decompressParticleFileFromRom(rom, index) {
        let dma_index = this.findDMAIndexOfParticle(rom, index);
        return this.decompressDMAFileFromRom(rom, dma_index);
    }
    recompressParticleFileIntoRom(rom, index, file) {
        let dma_index = this.findDMAIndexOfParticle(rom, index);
        return this.recompressDMAFileIntoRom(rom, dma_index, file);
    }
    findDMAIndexOfActor(rom, index) {
        let size = 0x20;
        let code = this.decompressDMAFileFromRom(rom, this.Code_DMA);
        let dma = this.decompressDMAFileFromRom(rom, this.DMA_DMA);
        let offset = this.Actor_Offset + (index * size);
        let search = code.slice(offset, offset + 0x8);
        let dma_index = dma.indexOf(search) / 0x10;
        return dma_index;
    }
    decompressActorFileFromRom(rom, index) {
        let dma_index = this.findDMAIndexOfActor(rom, index);
        return this.decompressDMAFileFromRom(rom, dma_index);
    }
    recompressActorFileIntoRom(rom, index, file) {
        let dma_index = this.findDMAIndexOfActor(rom, index);
        return this.recompressDMAFileIntoRom(rom, dma_index, file);
    }
    findDMAIndexOfObject(rom, index) {
        let size = 0x8;
        let code = this.decompressDMAFileFromRom(rom, this.Code_DMA);
        let dma = this.decompressDMAFileFromRom(rom, this.DMA_DMA);
        let offset = this.Object_Offset + (index * size);
        let search = code.slice(offset, offset + 0x8);
        let dma_index = dma.indexOf(search) / 0x10;
        return dma_index;
    }
    decompressObjectFileFromRom(rom, index) {
        let dma_index = this.findDMAIndexOfObject(rom, index);
        return this.decompressDMAFileFromRom(rom, dma_index);
    }
    recompressObjectFileIntoRom(rom, index, file) {
        let dma_index = this.findDMAIndexOfObject(rom, index);
        return this.recompressDMAFileIntoRom(rom, dma_index, file);
    }
    relocateFileToExtendedRom(rom, index, file, sizeOverride = 0, nocompress = false) {
        if (heap === undefined) {
            let f = rom.indexOf(mb, 0x2000000);
            heap = new romheap_1.RomHeap(rom, this.ModLoader.rom, f, (0x100 * 1024 * 1024) - f);
        }
        let r = 0;
        let buf = file;
        if (!nocompress) {
            buf = this.ModLoader.utils.yaz0Encode(file);
        }
        let alloc = heap.malloc(buf.byteLength);
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        if (sizeOverride > 0) {
            if (!nocompress) {
                let vram_start = alloc;
                let vram_end = vram_start + sizeOverride;
                rom.writeUInt32BE(vram_start, dma + offset);
                rom.writeUInt32BE(vram_end, dma + offset + 0x4);
            }
            let temp = Buffer.alloc(sizeOverride);
            file.copy(temp);
            file = temp;
        }
        if (nocompress) {
            rom.writeUInt32BE(alloc, dma + offset + 0x8);
            rom.writeUInt32BE(0x0, dma + offset + 0xC);
        }
        else {
            rom.writeUInt32BE(alloc, dma + offset + 0x8);
            rom.writeUInt32BE(alloc + buf.byteLength, dma + offset + 0xC);
        }
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size = rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        if (isFileCompressed && !nocompress) {
            buf.copy(rom, start);
            r = buf.byteLength;
        }
        else {
            file.copy(rom, start);
            r = file.byteLength;
        }
        let loc = alloc;
        return loc;
    }
    injectNewFile(rom, index, file, nocompress = false) {
        if (heap === undefined) {
            let f = rom.indexOf(mb, 0x2000000);
            heap = new romheap_1.RomHeap(rom, this.ModLoader.rom, f, (0x100 * 1024 * 1024) - f);
        }
        let r = 0;
        let buf = file;
        if (!nocompress) {
            buf = this.ModLoader.utils.yaz0Encode(file);
        }
        let alloc = heap.malloc(buf.byteLength);
        let dma = this.DMA_Offset;
        let offset = index * 0x10;
        rom.writeUInt32BE(VROM_END, dma + offset + 0x0);
        rom.writeUInt32BE(VROM_END + file.byteLength, dma + offset + 0x4);
        VROM_END += file.byteLength;
        rom.writeUInt32BE(alloc, dma + offset + 0x8);
        if (nocompress) {
            rom.writeUInt32BE(0x0, dma + offset + 0xC);
        }
        else {
            rom.writeUInt32BE(alloc + buf.byteLength, dma + offset + 0xC);
        }
        let start = rom.readUInt32BE(dma + offset + 0x8);
        let end = rom.readUInt32BE(dma + offset + 0xc);
        let size = end - start;
        let isFileCompressed = true;
        if (end === 0) {
            isFileCompressed = false;
            size = rom.readUInt32BE(dma + offset + 0x4) - rom.readUInt32BE(dma + offset);
            end = start + size;
        }
        if (isFileCompressed) {
            buf.copy(rom, start);
            r = buf.byteLength;
        }
        else {
            file.copy(rom, start);
            r = file.byteLength;
        }
        return r;
    }
    fixLinkObjectTableEntry(rom, code, game) {
        let offset = 0x11CD08;
        let dma = this.decompressDMAFileFromRom(rom, this.DMA_DMA);
        let vram_start = dma.readUInt32BE((654 * 0x10));
        let vram_end = dma.readUInt32BE((654 * 0x10) + 0x4);
        code.writeUInt32BE(vram_start, offset);
        code.writeUInt32BE(vram_end, offset + 0x4);
        return code;
    }
    noCRC(rom) {
        rom = PatchManager_1.PatchTypes.get(".txt").patch(rom, fs_1.default.readFileSync(path_1.default.join(__dirname, "no_crc.txt")));
    }
}
exports.Z64RomTools = Z64RomTools;
function trimBuffer(buffer) {
    var pos = 0;
    for (var i = buffer.length - 1; i >= 0; i--) {
        if (buffer[i] !== 0x00) {
            pos = i;
            break;
        }
    }
    pos++;
    while (pos % 0x10 !== 0) {
        pos++;
    }
    return buffer.slice(0, pos);
}
exports.trimBuffer = trimBuffer;
//# sourceMappingURL=Z64RomTools.js.map