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
JavaScript
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