UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

98 lines (97 loc) • 3.72 kB
import IgirException from "../../exceptions/igirException.js"; import IOFile from "../../models/files/ioFile.js"; import Patch from "./patch.js"; const APSN64PatchType = { SIMPLE: 0, N64: 1 }; class APSN64Patch extends Patch { static FILE_SIGNATURE = Buffer.from("APS10"); patchType; constructor(patchType, file, crcBefore, sizeAfter) { super(file, crcBefore, void 0, sizeAfter); this.patchType = patchType; } /** * Parse an N64-format .aps patch file and return an {@link APSN64Patch}. */ static async patchFrom(file) { let patchType = APSN64PatchType.SIMPLE; const crcBefore = super.getCrcFromPath(file.getExtractedFilePath()); let targetSize = 0; await file.extractToTempIOFile("r", async (patchFile) => { patchFile.seek(this.FILE_SIGNATURE.length); patchType = (await patchFile.readNext(1)).readUInt8(); patchFile.skipNext(1); patchFile.skipNext(50); if (patchType === APSN64PatchType.SIMPLE) { targetSize = (await patchFile.readNext(4)).readUInt32LE(); } else if (patchType === APSN64PatchType.N64) { patchFile.skipNext(1); patchFile.skipNext(2); patchFile.skipNext(1); patchFile.skipNext(8); patchFile.skipNext(5); targetSize = (await patchFile.readNext(4)).readUInt32LE(); } else { throw new IgirException( `APS (N64) patch type ${patchType} isn't supported: ${patchFile.getPathLike().toString()}` ); } }); return new APSN64Patch(patchType, file, crcBefore, targetSize); } /** * Apply this patch to the input ROM file and write the patched result to the output path. */ async createPatchedFile(inputRomFile, outputRomPath, callback) { await this.getFile().extractToTempIOFile("r", async (patchFile) => { const header = await patchFile.readNext(APSN64Patch.FILE_SIGNATURE.length); if (!header.equals(APSN64Patch.FILE_SIGNATURE)) { throw new IgirException(`APS (N64) patch header is invalid: ${this.getFile().toString()}`); } if (this.patchType === APSN64PatchType.SIMPLE) { patchFile.seek(61); } else if (this.patchType === APSN64PatchType.N64) { patchFile.seek(78); } else { throw new IgirException( `APS (N64) patch type ${this.patchType} isn't supported: ${patchFile.getPathLike().toString()}` ); } await APSN64Patch.writeOutputFile(inputRomFile, outputRomPath, patchFile, callback); }); } static async writeOutputFile(inputRomFile, outputRomPath, patchFile, callback) { await inputRomFile.extractToFile(outputRomPath); const targetFile = await IOFile.fileFrom(outputRomPath, "r+"); try { await this.applyPatch(patchFile, targetFile, callback); } finally { await targetFile.close(); } } static async applyPatch(patchFile, targetFile, callback) { while (patchFile.getPosition() < patchFile.getSize()) { const offset = (await patchFile.readNext(4)).readUInt32LE(); const size = (await patchFile.readNext(1)).readUInt8(); let data; if (size === 0) { const byte = await patchFile.readNext(1); const rleSize = (await patchFile.readNext(1)).readUInt8(); data = Buffer.from(byte.toString("hex").repeat(rleSize), "hex"); } else { data = await patchFile.readNext(size); } await targetFile.writeAt(data, offset); if (callback !== void 0) { const progressPercentage = patchFile.getPosition() / patchFile.getSize(); callback(Math.floor(progressPercentage * targetFile.getSize())); } } } } export { APSN64Patch as default }; //# sourceMappingURL=apsN64Patch.js.map