mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-27 07:59:03 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Inspect the il2cpp string-literal table of the 20230831 client.
|
||||
|
||||
global-metadata.dat ships lightly obfuscated (header magic is not 0xFAB11BAF),
|
||||
but the interop copy BepInEx writes (global-metadata.interop.dat) is plain v27,
|
||||
which is the file the plugin itself reads. String literals live at header
|
||||
offsets 8..24:
|
||||
|
||||
int32 stringLiteralOffset // table of (length, dataOffset) pairs
|
||||
int32 stringLiteralCount // BYTES in that table (8 per literal)
|
||||
int32 stringLiteralDataOffset // concatenated utf8 bytes
|
||||
int32 stringLiteralDataCount // BYTES of literal data
|
||||
|
||||
Usage:
|
||||
python tools/sep-literals.py find <needle> [-i] [--limit N]
|
||||
python tools/sep-literals.py around <index> [radius]
|
||||
python tools/sep-literals.py token <index>
|
||||
|
||||
Environment:
|
||||
RADICADE_METADATA override the metadata path
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
|
||||
DEFAULT_METADATA = (
|
||||
r"C:\RecRoom\2023-09-06T06-53-47Z\RecRoom_Data\il2cpp_data"
|
||||
r"\Metadata\global-metadata.interop.dat"
|
||||
)
|
||||
|
||||
|
||||
def load(path):
|
||||
with open(path, "rb") as handle:
|
||||
blob = handle.read()
|
||||
magic, version = struct.unpack_from("<Ii", blob, 0)
|
||||
if magic != 0xFAB11BAF:
|
||||
raise SystemExit(
|
||||
f"not a plain il2cpp metadata file (magic {magic:#x}); "
|
||||
"point RADICADE_METADATA at global-metadata.interop.dat"
|
||||
)
|
||||
table, table_bytes, data, data_bytes = struct.unpack_from("<iiii", blob, 8)
|
||||
count = table_bytes // 8
|
||||
if table_bytes % 8 or table < 24 or table + table_bytes > len(blob):
|
||||
raise SystemExit(f"implausible literal table ({table}, {table_bytes})")
|
||||
literals = []
|
||||
for index in range(count):
|
||||
length, offset = struct.unpack_from("<ii", blob, table + 8 * index)
|
||||
if length < 0 or offset < 0 or offset + length > data_bytes:
|
||||
raise SystemExit(f"literal #{index} escapes the data segment")
|
||||
literals.append(blob[data + offset : data + offset + length].decode("utf-8", "replace"))
|
||||
return version, literals
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) < 2:
|
||||
raise SystemExit(__doc__)
|
||||
path = os.environ.get("RADICADE_METADATA", DEFAULT_METADATA)
|
||||
version, literals = load(path)
|
||||
command, rest = argv[1], argv[2:]
|
||||
|
||||
if command == "find":
|
||||
if not rest:
|
||||
raise SystemExit("find needs a needle")
|
||||
ignore_case = "-i" in rest
|
||||
limit = None
|
||||
positional = []
|
||||
index = 0
|
||||
while index < len(rest):
|
||||
if rest[index] == "--limit":
|
||||
limit = int(rest[index + 1])
|
||||
index += 2
|
||||
continue
|
||||
if rest[index] != "-i":
|
||||
positional.append(rest[index])
|
||||
index += 1
|
||||
if not positional:
|
||||
raise SystemExit("find needs a needle")
|
||||
needle = positional[0]
|
||||
target = needle.lower() if ignore_case else needle
|
||||
hits = 0
|
||||
for index, text in enumerate(literals):
|
||||
haystack = text.lower() if ignore_case else text
|
||||
if target not in haystack:
|
||||
continue
|
||||
print(f"#{index}\t{text!r}")
|
||||
hits += 1
|
||||
if limit and hits >= limit:
|
||||
break
|
||||
print(f"-- {hits} match(es); {len(literals)} literals; metadata v{version}")
|
||||
|
||||
elif command == "around":
|
||||
index = int(rest[0])
|
||||
radius = int(rest[1]) if len(rest) > 1 else 25
|
||||
for other in range(max(0, index - radius), min(len(literals), index + radius + 1)):
|
||||
mark = ">>" if other == index else " "
|
||||
print(f"{mark} #{other}\t{literals[other]!r}")
|
||||
|
||||
elif command == "token":
|
||||
for raw in rest:
|
||||
index = int(raw)
|
||||
print(f"#{index}\t0x{(5 << 29) | (index << 1):X}\t{literals[index]!r}")
|
||||
|
||||
else:
|
||||
raise SystemExit(__doc__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
Reference in New Issue
Block a user