// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later using System.Security.Cryptography; using System.Text; namespace SharpEmu.SourceGenerators; /// /// The PS NID derivation: base64 (with the '+','-' alphabet, no padding) of the /// byte-reversed first eight bytes of SHA1(symbolName + fixed suffix). The same /// algorithm scripts/generate_aerolib_binary.py uses to build the runtime catalog, /// in C# so the analyzer and generator can validate and derive NIDs at compile time. /// public static class Ps5Nid { private static readonly byte[] Suffix = [ 0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1, 0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30, ]; public static string Compute(string symbolName) { using var sha1 = SHA1.Create(); return Compute(symbolName, sha1); } /// /// Bulk-callable overload: the aerolib build task hashes every catalog name /// (~150k), so the caller owns one SHA1 instance instead of churning one per name. /// public static string Compute(string symbolName, SHA1 sha1) { var nameBytes = Encoding.UTF8.GetBytes(symbolName); var input = new byte[nameBytes.Length + Suffix.Length]; nameBytes.CopyTo(input, 0); Suffix.CopyTo(input, nameBytes.Length); var hash = sha1.ComputeHash(input); // The script reads the first eight bytes as a little-endian integer and // formats it big-endian before encoding — a byte reversal. var reversed = new byte[8]; for (var index = 0; index < 8; index++) { reversed[index] = hash[7 - index]; } return Convert.ToBase64String(reversed) .TrimEnd('=') .Replace('/', '-'); } /// Eleven characters of the '+','-' base64 alphabet. public static bool IsValidFormat(string nid) { if (nid.Length != 11) { return false; } foreach (var character in nid) { var valid = character is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z') or (>= '0' and <= '9') or '+' or '-'; if (!valid) { return false; } } return true; } }