diff --git a/scripts/check_sysabi_aerolib.py b/scripts/check_sysabi_aerolib.py new file mode 100644 index 0000000..0f5127b --- /dev/null +++ b/scripts/check_sysabi_aerolib.py @@ -0,0 +1,172 @@ +# Copyright (C) 2026 SharpEmu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +#!/usr/bin/env python3 + +"""Offline check: SysAbiExport ExportName must hash to its Nid (name2nid). + +NIDs absent from aerolib.bin are skipped (unknown/unresolved symbols). +Known historic mislabels may be allowlisted with a one-line reason. + +Run from the repository root: + python scripts/check_sysabi_aerolib.py + python scripts/check_sysabi_aerolib.py --strict +""" + +from __future__ import annotations + +import argparse +import hashlib +import re +import struct +import sys +from base64 import b64encode as base64enc +from binascii import unhexlify as uhx +from pathlib import Path + +SRC_ROOT = Path("src") +AEROLIB_BIN = Path("src/SharpEmu.HLE/Aerolib/aerolib.bin") +SYSABI_EXPORT_RE = re.compile(r"\[SysAbiExport\((.*?)\)\]", re.DOTALL) +NID_RE = re.compile(r'Nid\s*=\s*"([^"]+)"') +EXPORT_NAME_RE = re.compile(r'ExportName\s*=\s*"([^"]+)"') + +# NID -> reason. Keep minimal; fix ExportName when safe instead of growing this list. +ALLOWLISTED_NIDS: dict[str, str] = { + "KMcEa+rHsIo": "Historic kernel MapMemory stub bound to sceAvPlayerAddSource NID; API rewrite deferred.", + "WV1GwM32NgY": "Historic WebApi2 init alias for PushEventCreateHandle NID; ABI rewrite deferred.", +} + + +def name2nid(name: str) -> str: + symbol = hashlib.sha1(name.encode() + uhx("518D64A635DED8C1E6B039B1C3E55230")).digest() + id_val = struct.unpack(" Path: + cwd = Path.cwd() + if (cwd / SRC_ROOT).is_dir() and (cwd / "scripts").is_dir(): + return cwd + script_root = Path(__file__).resolve().parent.parent + if (script_root / SRC_ROOT).is_dir(): + return script_root + raise SystemExit("Run from the repository root (src/ and scripts/ expected).") + + +def load_aerolib_nids(aerolib_path: Path) -> set[str]: + data = aerolib_path.read_bytes() + if len(data) < 4: + raise SystemExit(f"Aerolib binary too small: {aerolib_path}") + + count = struct.unpack_from("= len(data): + raise SystemExit(f"Truncated aerolib.bin while reading NIDs: {aerolib_path}") + nid_len = data[offset] + offset += 1 + nid = data[offset : offset + nid_len].decode("utf-8") + offset += nid_len + if offset + 2 > len(data): + raise SystemExit(f"Truncated aerolib.bin name length: {aerolib_path}") + name_len = struct.unpack_from(" int: + parser = argparse.ArgumentParser( + description="Check that SysAbiExport ExportName values hash to their Nid via name2nid." + ) + parser.add_argument( + "--strict", + action="store_true", + help="Exit 1 when any non-skipped/non-allowlisted ExportName does not hash to its Nid.", + ) + parser.add_argument( + "--quiet", + action="store_true", + help="Print only the summary line.", + ) + args = parser.parse_args() + + repo_root = find_repo_root() + aerolib_path = repo_root / AEROLIB_BIN + if not aerolib_path.is_file(): + raise SystemExit(f"Missing Aerolib catalog: {aerolib_path.as_posix()}") + + catalog_nids = load_aerolib_nids(aerolib_path) + checked, mismatches, skipped_no_catalog, allowlisted = scan( + repo_root / SRC_ROOT, catalog_nids + ) + ok = checked - len(mismatches) - skipped_no_catalog - allowlisted + + if not args.quiet: + for path, line, nid, export_name, computed in mismatches: + rel = path.relative_to(repo_root).as_posix() + print( + f"{rel}:{line}: NID={nid} ExportName={export_name!r} " + f"computed={computed}" + ) + + print( + f"checked={checked} ok={ok} fail={len(mismatches)} " + f"skipped_no_catalog={skipped_no_catalog} allowlisted={allowlisted} " + f"allowlist_size={len(ALLOWLISTED_NIDS)}" + ) + + if args.strict and mismatches: + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/SharpEmu.Libs/Np/NpManagerExports.cs b/src/SharpEmu.Libs/Np/NpManagerExports.cs index a86d7c8..184de01 100644 --- a/src/SharpEmu.Libs/Np/NpManagerExports.cs +++ b/src/SharpEmu.Libs/Np/NpManagerExports.cs @@ -59,7 +59,7 @@ public static class NpManagerExports [SysAbiExport( Nid = "rbknaUjpqWo", - ExportName = "sceNpGetOnlineIdA", + ExportName = "sceNpGetAccountIdA", Target = Generation.Gen5, LibraryName = "libSceNpManager")] public static int NpGetOnlineIdA(CpuContext ctx)