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.

115 lines (114 loc) • 4.24 kB
import IgirException from "../../exceptions/igirException.js"; import IOFile from "../../models/files/ioFile.js"; import FsUtil from "../../utils/fsUtil.js"; import Patch from "./patch.js"; class PPFHeader { static FILE_SIGNATURE = Buffer.from("PPF"); version; undoDataAvailable; constructor(version, isUndoDataAvailable) { this.version = version; this.undoDataAvailable = isUndoDataAvailable; } /** * Read and validate the PPF header from the patch file, advancing the read position past it. */ static async fromIOFile(inputRomFile, patchFile) { const header = (await patchFile.readNext(5)).toString(); if (!header.startsWith(this.FILE_SIGNATURE.toString())) { throw new IgirException(`PPF patch header is invalid: ${patchFile.getPathLike().toString()}`); } const encoding = (await patchFile.readNext(1)).readUInt8(); const version = encoding + 1; if (!header.endsWith(`${version}0`)) { throw new IgirException( `PPF patch header has an invalid version: ${patchFile.getPathLike().toString()}` ); } patchFile.skipNext(50); let isBlockCheckEnabled; let isUndoDataAvailable = false; if (version === 2) { const sourceSize = (await patchFile.readNext(4)).readUInt32LE(); if (inputRomFile.getSize() !== sourceSize) { throw new IgirException( `PPF patch expected ROM size of ${FsUtil.sizeReadable(sourceSize)}: ${patchFile.getPathLike().toString()}` ); } isBlockCheckEnabled = true; } else if (version === 3) { patchFile.skipNext(1); isBlockCheckEnabled = (await patchFile.readNext(1)).readUInt8() === 1; isUndoDataAvailable = (await patchFile.readNext(1)).readUInt8() === 1; patchFile.skipNext(1); } else { throw new IgirException( `PPF v${version} isn't supported: ${patchFile.getPathLike().toString()}` ); } if (isBlockCheckEnabled) { patchFile.skipNext(1024); } return new PPFHeader(version, isUndoDataAvailable); } } class PPFPatch extends Patch { static SUPPORTED_EXTENSIONS = [".ppf"]; static FILE_SIGNATURE = PPFHeader.FILE_SIGNATURE; /** * Parse a .ppf patch file and return a {@link PPFPatch}. */ static patchFrom(file) { const crcBefore = super.getCrcFromPath(file.getExtractedFilePath()); return new PPFPatch(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 PPFHeader.fromIOFile(inputRomFile, patchFile); await PPFPatch.writeOutputFile(inputRomFile, outputRomPath, patchFile, header, callback); }); } static async writeOutputFile(inputRomFile, outputRomPath, patchFile, header, callback) { await inputRomFile.extractToFile(outputRomPath); const targetFile = await IOFile.fileFrom(outputRomPath, "r+"); try { while (!patchFile.isEOF()) { await this.applyPatchBlock(patchFile, targetFile, header); if (callback !== void 0) { const progressPercentage = patchFile.getPosition() / patchFile.getSize(); callback(Math.floor(progressPercentage * targetFile.getSize())); } } } finally { await targetFile.close(); } } static async applyPatchBlock(patchFile, targetFile, header) { const peek = (await patchFile.peekNext(18)).toString(); if (!peek) { return; } if (peek === "@BEGIN_FILE_ID.DIZ") { return; } let offset = 0; if (header.version === 2) { offset = (await patchFile.readNext(4)).readUInt32LE(); } else if (header.version === 3) { offset = (await patchFile.readNext(4)).readUInt32LE() + (await patchFile.readNext(4)).readUInt32LE() * 4294967296; } const bytesToChange = (await patchFile.readNext(1)).readUInt8(); const data = await patchFile.readNext(bytesToChange); if (header.undoDataAvailable) { patchFile.skipNext(bytesToChange); } await targetFile.writeAt(data, offset); } } export { PPFPatch as default }; //# sourceMappingURL=ppfPatch.js.map