From 41ef6acad3c18727299b1e93ce56ffbedf14922d Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Tue, 13 Oct 2026 23:19:42 +0530 Subject: [PATCH 1/2] feat(tools): add --name option to nid_names to compute symbol NIDs Add --name / --symbol CLI option to tools/nid_names.py to compute the Sony SHA-1 NID for given symbol names from the command line without requiring a full database download. Support positional chaining and --json output format. Add unit test coverage in tools/tests/test_nid_names.py. --- tools/nid_names.py | 18 ++++++++++++++++-- tools/tests/test_nid_names.py | 28 +++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/tools/nid_names.py b/tools/nid_names.py index 2cb0d756e4..48b3d0295b 100644 --- a/tools/nid_names.py +++ b/tools/nid_names.py @@ -131,22 +131,25 @@ def option_name(argument, options): def nid_arguments(arguments, options): normalized = [] collecting = False + collecting_option = "--nid" for index, argument in enumerate(arguments): if argument == "--": normalized.extend(arguments[index:]) break name = option_name(argument, options) - if name == "--nid" and "=" not in argument: + if name in ("--nid", "--name", "--symbol") and "=" not in argument: collecting = True + collecting_option = "--name" if name in ("--name", "--symbol") else "--nid" continue if collecting and name is None: - normalized.append("--nid=" + argument) + normalized.append(collecting_option + "=" + argument) continue collecting = False normalized.append(argument) return normalized + def main(argv=None): parser = argparse.ArgumentParser( description="Suggest real names for Unknown NID stubs") @@ -154,11 +157,22 @@ def main(argv=None): help="NID database CSV (downloaded to a cache dir by default)") parser.add_argument("--nid", action="append", default=None, help="resolve only these NIDs instead of scanning the tree") + parser.add_argument("--name", "--symbol", dest="name", action="append", default=None, + help="compute NIDs for these symbol names") parser.add_argument("--json", action="store_true", help="emit machine-readable JSON") options = [option for action in parser._actions for option in action.option_strings] arguments = sys.argv[1:] if argv is None else argv args = parser.parse_args(nid_arguments(arguments, options)) + if args.name: + rows = [{"name": name, "nid": compute_nid(name)} for name in args.name] + if args.json: + print(json.dumps({"symbols": rows}, indent=1)) + else: + print("%-40s %s" % ("NAME", "NID")) + for row in rows: + print("%-40s %s" % (row["name"][:40], row["nid"])) + return 0 requested_nids = any(option_name(argument, options) == "--nid" for argument in arguments) for nid in args.nid or []: if re.fullmatch(r"[A-Za-z0-9+\-]{11}", nid) is None: diff --git a/tools/tests/test_nid_names.py b/tools/tests/test_nid_names.py index 41a8f5118d..994908eb5c 100644 --- a/tools/tests/test_nid_names.py +++ b/tools/tests/test_nid_names.py @@ -240,9 +240,31 @@ int APS5_VABI sceReal() { return 0; } ''') self.assertEqual(nid_names.collect_real_names(), {"sceReal"}) - def test_spliced_line_comment_and_macro_tokens(self): - self.write_source('// continued \\\nAPS5_EXPORT("AAAAAAAAAAA", exampleUnknown00);\nAPS5_EX\\\nPORT("BBBBBBBBBBB", exampleUnknown01);\n') - self.assertEqual(nid_names.collect_unknowns(), {"BBBBBBBBBBB": "core/libs/prx/libSceExample/Export.cpp:exampleUnknown01"}) + def test_name_option_computes_symbol_nid(self): + output = io.StringIO() + with patch.object(sys, "argv", ["nid_names.py", "--name", "ceilf", "floorf"]), contextlib.redirect_stdout(output): + self.assertEqual(nid_names.main(), 0) + text = output.getvalue() + self.assertIn("ceilf", text) + self.assertIn("GAUuLKGhsCw", text) + self.assertIn("floorf", text) + self.assertIn("mKhVDmYciWA", text) + + def test_name_option_with_json_emits_symbols_list(self): + output = io.StringIO() + with patch.object(sys, "argv", ["nid_names.py", "--name", "ceilf", "--json"]), contextlib.redirect_stdout(output): + self.assertEqual(nid_names.main(), 0) + data = json.loads(output.getvalue()) + self.assertEqual(data, {"symbols": [{"name": "ceilf", "nid": "GAUuLKGhsCw"}]}) + + def test_symbol_alias_does_not_load_database(self): + output = io.StringIO() + with patch.object(sys, "argv", ["nid_names.py", "--symbol", "truncf"]), \ + patch.object(nid_names, "load_db", side_effect=AssertionError("unexpected db load")), \ + contextlib.redirect_stdout(output): + self.assertEqual(nid_names.main(), 0) + self.assertIn("Vo8rvWtZw3g", output.getvalue()) + if __name__ == "__main__": From ecce9a3a162619417098b5ae05fbd90dd140ed44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 14 Oct 2026 13:36:11 +0200 Subject: [PATCH 2/2] fix(tools): keep the spliced-comment test, reject --name with --nid, no name truncation Co-Authored-By: Claude --- tools/nid_names.py | 5 +++-- tools/tests/test_nid_names.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/nid_names.py b/tools/nid_names.py index 48b3d0295b..0df8c3f147 100644 --- a/tools/nid_names.py +++ b/tools/nid_names.py @@ -149,7 +149,6 @@ def nid_arguments(arguments, options): return normalized - def main(argv=None): parser = argparse.ArgumentParser( description="Suggest real names for Unknown NID stubs") @@ -165,13 +164,15 @@ def main(argv=None): arguments = sys.argv[1:] if argv is None else argv args = parser.parse_args(nid_arguments(arguments, options)) if args.name: + if args.nid: + parser.error("--name/--symbol cannot be combined with --nid") rows = [{"name": name, "nid": compute_nid(name)} for name in args.name] if args.json: print(json.dumps({"symbols": rows}, indent=1)) else: print("%-40s %s" % ("NAME", "NID")) for row in rows: - print("%-40s %s" % (row["name"][:40], row["nid"])) + print("%-40s %s" % (row["name"], row["nid"])) return 0 requested_nids = any(option_name(argument, options) == "--nid" for argument in arguments) for nid in args.nid or []: diff --git a/tools/tests/test_nid_names.py b/tools/tests/test_nid_names.py index 994908eb5c..6dd4b20804 100644 --- a/tools/tests/test_nid_names.py +++ b/tools/tests/test_nid_names.py @@ -265,6 +265,21 @@ int APS5_VABI sceReal() { return 0; } self.assertEqual(nid_names.main(), 0) self.assertIn("Vo8rvWtZw3g", output.getvalue()) + def test_name_option_rejects_nid_and_keeps_long_names(self): + with patch.object(sys, "argv", ["nid_names.py", "--name", "ceilf", "--nid", "AAAAAAAAAAA"]), \ + contextlib.redirect_stderr(io.StringIO()): + with self.assertRaises(SystemExit): + nid_names.main() + long_name = "a" * 50 + output = io.StringIO() + with patch.object(sys, "argv", ["nid_names.py", "--name", long_name]), contextlib.redirect_stdout(output): + self.assertEqual(nid_names.main(), 0) + self.assertIn(long_name, output.getvalue()) + + def test_spliced_line_comment_and_macro_tokens(self): + self.write_source('// continued \\\nAPS5_EXPORT("AAAAAAAAAAA", exampleUnknown00);\nAPS5_EX\\\nPORT("BBBBBBBBBBB", exampleUnknown01);\n') + self.assertEqual(nid_names.collect_unknowns(), {"BBBBBBBBBBB": "core/libs/prx/libSceExample/Export.cpp:exampleUnknown01"}) + if __name__ == "__main__":