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.

108 lines (107 loc) • 4.12 kB
import ArrayPoly from '../../polyfill/arrayPoly.js'; import APSPatch from './apsPatch.js'; import BPSPatch from './bpsPatch.js'; import DPSPatch from './dpsPatch.js'; import IPSPatch from './ipsPatch.js'; import NinjaPatch from './ninjaPatch.js'; import PPFPatch from './ppfPatch.js'; import UPSPatch from './upsPatch.js'; import VcdiffPatch from './vcdiffPatch.js'; /** * NOTE(cemmer): this file exists to prevent circular dependencies between Patch and its children. */ export default class PatchFactory { static PATCH_PARSERS = [ { extensions: APSPatch.SUPPORTED_EXTENSIONS, fileSignatures: [APSPatch.FILE_SIGNATURE], factory: APSPatch.patchFrom, }, { extensions: BPSPatch.SUPPORTED_EXTENSIONS, fileSignatures: [BPSPatch.FILE_SIGNATURE], factory: BPSPatch.patchFrom, }, { extensions: DPSPatch.SUPPORTED_EXTENSIONS, fileSignatures: [], factory: DPSPatch.patchFrom, }, { extensions: IPSPatch.SUPPORTED_EXTENSIONS, fileSignatures: IPSPatch.FILE_SIGNATURES, factory: IPSPatch.patchFrom, }, { extensions: NinjaPatch.SUPPORTED_EXTENSIONS, fileSignatures: [NinjaPatch.FILE_SIGNATURE], factory: NinjaPatch.patchFrom, }, { extensions: PPFPatch.SUPPORTED_EXTENSIONS, fileSignatures: [PPFPatch.FILE_SIGNATURE], factory: PPFPatch.patchFrom, }, { extensions: UPSPatch.SUPPORTED_EXTENSIONS, fileSignatures: [UPSPatch.FILE_SIGNATURE], factory: UPSPatch.patchFrom, }, { extensions: VcdiffPatch.SUPPORTED_EXTENSIONS, fileSignatures: [VcdiffPatch.FILE_SIGNATURE], factory: VcdiffPatch.patchFrom, }, ]; static MAX_HEADER_LENGTH_BYTES = Object.values(PatchFactory.PATCH_PARSERS) .flatMap((parser) => parser.fileSignatures) .reduce((max, fileSignature) => Math.max(max, fileSignature.length), 0); static getSupportedExtensions() { return Object.values(PatchFactory.PATCH_PARSERS) .flatMap((parser) => parser.extensions) .reduce(ArrayPoly.reduceUnique(), []) .sort(); } static async patchFromFilename(file) { const filePath = file.getExtractedFilePath(); const parsers = Object.values(this.PATCH_PARSERS); for (const parser of parsers) { if (parser.extensions.some((ext) => filePath.toLowerCase().endsWith(ext))) { return parser.factory(file); } } return undefined; } static async readHeaderHex(stream, length) { return new Promise((resolve, reject) => { stream.resume(); const chunks = []; const resolveHeader = () => { const header = Buffer.concat(chunks).subarray(0, length).toString('hex').toLowerCase(); resolve(header); }; stream.on('data', (chunk) => { if (chunk.length > 0) { chunks.push(chunk); } // Stop reading when we get enough data, trigger a 'close' event if (chunks.reduce((sum, buff) => sum + buff.length, 0) >= length) { resolveHeader(); stream.destroy(); } }); stream.on('end', resolveHeader); stream.on('error', reject); }); } static async patchFromFileContents(file) { const fileHeader = await file.createReadStream(async (readable) => PatchFactory.readHeaderHex(readable, this.MAX_HEADER_LENGTH_BYTES)); const parsers = Object.values(this.PATCH_PARSERS); for (const parser of parsers) { if (parser.fileSignatures.some((fileSignature) => fileHeader.startsWith(fileSignature.toString('hex')))) { return parser.factory(file); } } return undefined; } }