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.
98 lines (97 loc) • 3.72 kB
JavaScript
import IgirException from "../../exceptions/igirException.js";
import IOFile from "../../models/files/ioFile.js";
import Patch from "./patch.js";
const APSN64PatchType = {
SIMPLE: 0,
N64: 1
};
class APSN64Patch extends Patch {
static FILE_SIGNATURE = Buffer.from("APS10");
patchType;
constructor(patchType, file, crcBefore, sizeAfter) {
super(file, crcBefore, void 0, sizeAfter);
this.patchType = patchType;
}
/**
* Parse an N64-format .aps patch file and return an {@link APSN64Patch}.
*/
static async patchFrom(file) {
let patchType = APSN64PatchType.SIMPLE;
const crcBefore = super.getCrcFromPath(file.getExtractedFilePath());
let targetSize = 0;
await file.extractToTempIOFile("r", async (patchFile) => {
patchFile.seek(this.FILE_SIGNATURE.length);
patchType = (await patchFile.readNext(1)).readUInt8();
patchFile.skipNext(1);
patchFile.skipNext(50);
if (patchType === APSN64PatchType.SIMPLE) {
targetSize = (await patchFile.readNext(4)).readUInt32LE();
} else if (patchType === APSN64PatchType.N64) {
patchFile.skipNext(1);
patchFile.skipNext(2);
patchFile.skipNext(1);
patchFile.skipNext(8);
patchFile.skipNext(5);
targetSize = (await patchFile.readNext(4)).readUInt32LE();
} else {
throw new IgirException(
`APS (N64) patch type ${patchType} isn't supported: ${patchFile.getPathLike().toString()}`
);
}
});
return new APSN64Patch(patchType, file, crcBefore, targetSize);
}
/**
* 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(APSN64Patch.FILE_SIGNATURE.length);
if (!header.equals(APSN64Patch.FILE_SIGNATURE)) {
throw new IgirException(`APS (N64) patch header is invalid: ${this.getFile().toString()}`);
}
if (this.patchType === APSN64PatchType.SIMPLE) {
patchFile.seek(61);
} else if (this.patchType === APSN64PatchType.N64) {
patchFile.seek(78);
} else {
throw new IgirException(
`APS (N64) patch type ${this.patchType} isn't supported: ${patchFile.getPathLike().toString()}`
);
}
await APSN64Patch.writeOutputFile(inputRomFile, outputRomPath, patchFile, callback);
});
}
static async writeOutputFile(inputRomFile, outputRomPath, patchFile, callback) {
await inputRomFile.extractToFile(outputRomPath);
const targetFile = await IOFile.fileFrom(outputRomPath, "r+");
try {
await this.applyPatch(patchFile, targetFile, callback);
} finally {
await targetFile.close();
}
}
static async applyPatch(patchFile, targetFile, callback) {
while (patchFile.getPosition() < patchFile.getSize()) {
const offset = (await patchFile.readNext(4)).readUInt32LE();
const size = (await patchFile.readNext(1)).readUInt8();
let data;
if (size === 0) {
const byte = await patchFile.readNext(1);
const rleSize = (await patchFile.readNext(1)).readUInt8();
data = Buffer.from(byte.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 {
APSN64Patch as default
};
//# sourceMappingURL=apsN64Patch.js.map