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