87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
/** Stable machine-readable reason for each rejected input. */
|
|
export const IL2CPP_METADATA_FAILURE_REASONS = {
|
|
unsupported: ["unrecognized-magic", "unsupported-version"],
|
|
corrupt: [
|
|
"truncated-header",
|
|
"unexpected-header-size",
|
|
"section-out-of-bounds",
|
|
"section-overlap",
|
|
"record-size-mismatch",
|
|
"type-index-width",
|
|
"index-out-of-range",
|
|
"nested-type-conflict",
|
|
"declaring-type-mismatch",
|
|
"image-range-overlap",
|
|
"invalid-string",
|
|
],
|
|
} as const;
|
|
export type Il2cppMetadataFailureKind =
|
|
keyof typeof IL2CPP_METADATA_FAILURE_REASONS;
|
|
export type Il2cppMetadataFailureReason =
|
|
(typeof IL2CPP_METADATA_FAILURE_REASONS)[Il2cppMetadataFailureKind][number];
|
|
|
|
/** Distinguish inputs outside decoder coverage from damaged metadata. */
|
|
export class Il2cppMetadataFormatFailure extends Error {
|
|
readonly kind: Il2cppMetadataFailureKind;
|
|
constructor(
|
|
readonly reason: Il2cppMetadataFailureReason,
|
|
message: string,
|
|
options?: ErrorOptions,
|
|
) {
|
|
super(`${reason}: ${message}`, options);
|
|
this.name = "Il2cppMetadataFormatFailure";
|
|
this.kind = (
|
|
IL2CPP_METADATA_FAILURE_REASONS.unsupported as readonly string[]
|
|
).includes(reason)
|
|
? "unsupported"
|
|
: "corrupt";
|
|
}
|
|
}
|
|
|
|
/** Validate a section against a known serialized record width once. */
|
|
export const checkedSectionCount = (
|
|
section: Il2cppMetadataSection,
|
|
recordBytes: number,
|
|
): number => {
|
|
if (section.size % recordBytes !== 0)
|
|
throw new Il2cppMetadataFormatFailure(
|
|
"record-size-mismatch",
|
|
`section ${section.name} size ${section.size} is not a whole multiple of its ${recordBytes}-byte records`,
|
|
);
|
|
const count = section.size / recordBytes;
|
|
if (section.count !== null && count !== section.count)
|
|
throw new Il2cppMetadataFormatFailure(
|
|
"record-size-mismatch",
|
|
`section ${section.name} size ${section.size} does not hold ${section.count} ${recordBytes}-byte records`,
|
|
);
|
|
return count;
|
|
};
|
|
|
|
export const checkRange = (
|
|
label: string,
|
|
start: number,
|
|
length: number,
|
|
total: number,
|
|
) => {
|
|
if (length === 0 && start >= -1 && start <= total) return;
|
|
if (start < 0 || start + length > total)
|
|
throw new Il2cppMetadataFormatFailure(
|
|
"index-out-of-range",
|
|
`${label} range [${start}, ${start + length}) is outside the ${total} available entries`,
|
|
);
|
|
};
|
|
|
|
/** Read a serialized index; the all-ones value of narrow widths means -1. */
|
|
export const readIndex = (bytes: Buffer, offset: number, width: 1 | 2 | 4) => {
|
|
if (width === 1) {
|
|
const value = bytes.readUInt8(offset);
|
|
return value === 0xff ? -1 : value;
|
|
}
|
|
if (width === 2) {
|
|
const value = bytes.readUInt16LE(offset);
|
|
return value === 0xffff ? -1 : value;
|
|
}
|
|
return bytes.readInt32LE(offset);
|
|
};
|
|
import type { Il2cppMetadataSection } from "../domain/il2cpp/il2cppMetadata.js";
|