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.

75 lines (74 loc) • 2.79 kB
import IgirException from "../../exceptions/igirException.js"; import IOFile from "../../models/files/ioFile.js"; import Patch from "./patch.js"; class IPSPatch extends Patch { static SUPPORTED_EXTENSIONS = [".ips", ".ips32", ".ebp"]; static FILE_SIGNATURES = [Buffer.from("PATCH"), Buffer.from("IPS32")]; /** * Parse an .ips/.ips32/.ebp patch file and return an {@link IPSPatch}. */ static patchFrom(file) { const crcBefore = super.getCrcFromPath(file.getExtractedFilePath()); return new IPSPatch(file, crcBefore); } /** * 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(5); if (IPSPatch.FILE_SIGNATURES.every((fileSignature) => !header.equals(fileSignature))) { throw new IgirException(`IPS patch header is invalid: ${this.getFile().toString()}`); } let offsetSize = 3; let eofString = "EOF"; if (header.toString() === "IPS32") { offsetSize = 4; eofString = "EEOF"; } await IPSPatch.writeOutputFile( inputRomFile, outputRomPath, patchFile, offsetSize, eofString, callback ); }); } static async writeOutputFile(inputRomFile, outputRomPath, patchFile, offsetSize, eofString, callback) { await inputRomFile.extractToFile(outputRomPath); const targetFile = await IOFile.fileFrom(outputRomPath, "r+"); try { await this.applyPatch(patchFile, targetFile, offsetSize, eofString, callback); } finally { await targetFile.close(); } } static async applyPatch(patchFile, targetFile, offsetSize, eofString, callback) { while (!patchFile.isEOF()) { const offsetPeek = await patchFile.peekNext(eofString.length); if (offsetPeek.length === 0 || offsetPeek.toString() === eofString) { break; } const offset = (await patchFile.readNext(offsetSize)).readUIntBE(0, offsetSize); const size = (await patchFile.readNext(2)).readUInt16BE(); let data; if (size === 0) { const rleSize = (await patchFile.readNext(2)).readUInt16BE(); data = Buffer.from((await patchFile.readNext(1)).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 { IPSPatch as default }; //# sourceMappingURL=ipsPatch.js.map