superx-goes-arch/tools/onexconsole_asar_probe.py

109 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""
Kleine Read-only-Hilfe zum Mining von OneXConsole app.asar.
Zieht gezielt RGB-Routen, Pipe-/Helper-Hinweise und Zonennamen raus.
"""
from __future__ import annotations
import json
import re
import struct
from pathlib import Path
DEFAULT_ASAR = Path.home() / "SuperX-goes-Arch/.cache/onexconsole/resources/app.asar"
PATTERNS = {
"route_prefixes": [
"/programhandle/rgb/setPreset/",
"/programhandle/rgb/setColor/",
"/programhandle/rgb/assignSetColor/",
"/programhandle/rgb/setOpen2/",
"/programhandle/rgb/assignSetOpen/",
],
"helpers": [
r"CompatLayerCT",
r"\\\\\.\\pipe\\CompatLayerCT",
],
"zones": [
r'rgbPartition_left_stick:\"[^\"]+\"',
r'rgbPartition_right_stick:\"[^\"]+\"',
r'rgbPartition_kb:\"[^\"]+\"',
r'rgbPartition_back:\"[^\"]+\"',
r'rgbPartition_v:\"[^\"]+\"',
r'rgbPlace0304:\"[^\"]+\"',
r'rgbPlace0304Enable:\"[^\"]+\"',
],
}
def load_asar_text(path: Path) -> dict[str, str]:
with path.open("rb") as f:
_, _, _, header_len = struct.unpack("<4I", f.read(16))
header = json.loads(f.read(header_len))
payload = f.read()
files: dict[str, str] = {}
def walk(prefix: str, node: dict) -> None:
if "files" in node:
for name, child in node["files"].items():
walk(f"{prefix}/{name}" if prefix else name, child)
return
if "offset" not in node or "size" not in node:
return
off = int(node["offset"])
size = int(node["size"])
if prefix.endswith((".js", ".json", ".html", ".txt")):
files[prefix] = payload[off : off + size].decode("utf-8", "replace")
walk("", header)
return files
def main() -> int:
if not DEFAULT_ASAR.exists():
print(f"app.asar nicht gefunden: {DEFAULT_ASAR}")
return 1
texts = load_asar_text(DEFAULT_ASAR)
interesting = [
p for p in texts if p == "background.js" or p.startswith("js/") or p == "package.json"
]
print(f"asar: {DEFAULT_ASAR}")
print(f"dateien gescannt: {len(interesting)}")
for section, pats in PATTERNS.items():
print(f"\n## {section}")
seen: set[str] = set()
for path in interesting:
text = texts[path]
if section == "route_prefixes":
for prefix in pats:
start = 0
while True:
idx = text.find(prefix, start)
if idx == -1:
break
snippet = text[max(0, idx - 80) : min(len(text), idx + 220)]
snippet = snippet.replace("\n", " ")
if snippet not in seen:
seen.add(snippet)
print(f"[{path}] {snippet}")
start = idx + len(prefix)
continue
for pat in pats:
for m in re.finditer(pat, text, re.I):
s = m.group(0)
if s in seen:
continue
seen.add(s)
print(f"[{path}] {s}")
if not seen:
print("(nichts gefunden)")
return 0
if __name__ == "__main__":
raise SystemExit(main())