Files
rea/src/il2cpp/Il2cppMetadataAdmission.ts
T
JANG BYUNGKUNandgensokyo-dev 21e0c580e7 feat(il2cpp): read member names, string literals and metadata versions 24 and 27 (#5326)
* feat(il2cpp): read member names, string literals and metadata versions 24 and 27

Adds field, property, event and parameter names with their owners, managed string literals, and the version 24 and 27 header layouts to inspect_il2cpp_metadata, with filtered pages for the new collections. Member decoding yields between row batches like the other row readers.

* fix(il2cpp): bound metadata expansion and query parameter owners

* test(il2cpp): cover nonempty property and event tables

---------

Co-authored-by: gensokyo-dev <gensokyo-dev@users.noreply.github.com>
2026-10-20 19:21:42 +08:00

67 lines
2.1 KiB
TypeScript

import { jsonStringByteLength } from "../domain/jsonStringByteLength.js";
import type {
Il2cppMetadataReadOptions,
Il2cppMetadataRowCounts,
} from "./Il2cppMetadataFile.js";
// Minimum serialized row shapes; actual integer widths and the report envelope
// are checked exactly after decoding. This lower bound never refuses a fitting report.
const emptyRows = {
images: { name: "", type_start: 0, type_count: 0 },
types: {
name: "",
namespace: "",
method_start: 0,
method_count: 0,
nested_type_start: 0,
nested_type_count: 0,
declaring_type: 0,
},
methods: { name: "", declaring_type: 0 },
fields: { name: "", declaring_type: 0 },
properties: { name: "", declaring_type: 0 },
events: { name: "", declaring_type: 0 },
parameters: { name: "", method: 0 },
string_literals: { value: "", value_base64: null },
};
/** Per-snapshot admission, before text expansion and each retained fact. */
export const createIl2cppMetadataAdmission = (
limit: number,
exceed: () => never,
): Required<Il2cppMetadataReadOptions> => {
let used = 0;
// Keys/decoded names/literal spans are immutable within this one decoder run.
const lengths = new Map<string, number>();
const text = (value: string): number => {
const cached = lengths.get(value);
if (cached !== undefined) return cached;
const length = jsonStringByteLength(value, limit - used);
lengths.set(value, length);
return length;
};
return {
beforeRows(counts: Il2cppMetadataRowCounts) {
let minimum = 0;
for (const key of Object.keys(emptyRows) as (keyof typeof emptyRows)[])
minimum += counts[key] * JSON.stringify(emptyRows[key]).length;
if (minimum > limit) exceed();
},
beforeText(bytes) {
if (bytes > limit - used) exceed();
},
onRow(row) {
let bytes = 2,
first = true;
for (const [key, value] of Object.entries(row)) {
bytes += (first ? 0 : 1) + text(key) + 1;
first = false;
bytes +=
typeof value === "string" ? text(value) : value === null ? 4 : 1;
if (used + bytes > limit) exceed();
}
used += bytes;
},
};
};