chip-gameV3/Assets/Scripts/Common/BoardUtility.cs
sega 8a6d23b77d Реорг Scripts
Разделено на 4 модуля
- Menu
- Gameplay
- Settings
- Common
2026-07-15 10:49:55 +03:00

230 lines
8.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static class BoardUtility
{
// Константы из Python скрипта
private const uint POLY_A = 0x54505353;
private const uint POLY_B = 0x504F5450;
private const uint POLY_C = 0x53534F54;
private const uint POLY_SEED = 0x5450534F;
private const byte XOR_KEY = 0xD7;
private const uint SERIAL_LENGTH = 9;
private const uint ENCRYPTED_LENGTH = 38;
private const uint GARBAGE_LENGTH = 12;
private const string CHARSET = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
private const string ENCODE_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+$";
/// <summary>
/// Расшифровка обфусцированного серийника (38 символов)
/// </summary>
public static string DecryptSerialObfuscated(string obfuscatedData)
{
if (string.IsNullOrEmpty(obfuscatedData))
throw new ArgumentException("Input data cannot be null or empty");
if (obfuscatedData.Length != ENCRYPTED_LENGTH)
throw new ArgumentException($"Invalid length: expected {ENCRYPTED_LENGTH}, got {obfuscatedData.Length}");
try
{
// Извлекаем закодированные данные, пропуская мусор
StringBuilder encoded = new StringBuilder();
// Пропускаем первые 4 символа мусора (0-3)
// Берем символы 4-12 (9 символов данных)
encoded.Append(obfuscatedData.Substring(4, 9));
// Пропускаем символы 13-16 (4 символа мусора)
// Берем символы 17-25 (9 символов данных)
encoded.Append(obfuscatedData.Substring(17, 9));
// Декодируем (18 символов -> 9 байт)
byte[] encrypted = new byte[9];
string encodedStr = encoded.ToString();
for (int i = 0; i < 18; i += 2)
{
byte decodedByte = DecodeFromBase64Like(encodedStr[i], encodedStr[i + 1]);
encrypted[i / 2] = decodedByte;
}
// Расшифровываем XOR
StringBuilder decrypted = new StringBuilder();
byte prev = 0x3C;
for (int i = 0; i < encrypted.Length; i++)
{
byte decryptedChar = (byte)(encrypted[i] ^ XOR_KEY ^ (i * 17) ^ prev);
decrypted.Append((char)decryptedChar);
prev = encrypted[i];
}
return decrypted.ToString();
}
catch (Exception ex)
{
throw new Exception($"Obfuscated decryption error: {ex.Message}", ex);
}
}
/// <summary>
/// Расшифровка HEX серийника (старый формат)
/// </summary>
public static string DecryptSerialHex(string encryptedHex)
{
if (string.IsNullOrEmpty(encryptedHex))
throw new ArgumentException("Input data cannot be null or empty");
try
{
byte[] encryptedBytes = HexStringToByteArray(encryptedHex);
StringBuilder decrypted = new StringBuilder();
byte prev = 0x3C;
for (int i = 0; i < encryptedBytes.Length; i++)
{
byte decryptedChar = (byte)(encryptedBytes[i] ^ XOR_KEY ^ (i * 17) ^ prev);
decrypted.Append((char)decryptedChar);
prev = encryptedBytes[i];
}
return decrypted.ToString();
}
catch (Exception ex)
{
throw new Exception($"HEX decryption error: {ex.Message}", ex);
}
}
/// <summary>
/// Универсальный метод расшифровки - автоматически определяет формат
/// </summary>
public static string DecryptSerial(string encryptedData)
{
if (string.IsNullOrEmpty(encryptedData))
throw new ArgumentException("Input data cannot be null or empty");
// Определяем формат по длине
if (encryptedData.Length == ENCRYPTED_LENGTH) // Обфусцированный формат
{
return DecryptSerialObfuscated(encryptedData);
}
else if (encryptedData.Length == SERIAL_LENGTH * 2) // HEX формат
{
return DecryptSerialHex(encryptedData);
}
else // Предполагаем, что это уже открытый серийник
{
return encryptedData;
}
}
/// <summary>
/// Декодирование из base64-подобного формата
/// </summary>
private static byte DecodeFromBase64Like(char c1, char c2)
{
int val1 = ENCODE_TABLE.IndexOf(c1);
int val2 = ENCODE_TABLE.IndexOf(c2);
if (val1 == -1) val1 = 0;
if (val2 == -1) val2 = 0;
return (byte)(((val1 << 2) & 0xFC) | ((val2 >> 4) & 0x03));
}
/// <summary>
/// Преобразование HEX строки в массив байт
/// </summary>
private static byte[] HexStringToByteArray(string hex)
{
if (hex.Length % 2 != 0)
throw new ArgumentException("Hex string must have even length");
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
string byteValue = hex.Substring(i * 2, 2);
bytes[i] = Convert.ToByte(byteValue, 16);
}
return bytes;
}
/// <summary>
/// Генерация ожидаемого серийника из MAC адреса (для проверки)
/// </summary>
public static string GenerateSerialFromMac(string macAddress)
{
if (string.IsNullOrEmpty(macAddress))
throw new ArgumentException("MAC address cannot be null or empty");
// Преобразуем MAC в число
string cleanMac = macAddress.Replace(":", "").Replace("-", "");
ulong macNumber = ulong.Parse(cleanMac, System.Globalization.NumberStyles.HexNumber);
ulong macLow = macNumber & 0xFFFFFFFF;
ulong macHigh = (macNumber >> 32) & 0xFFFF;
ulong x = macLow ^ macHigh;
// Полином с обрезкой до 64 бит
ulong result = ((ulong)POLY_A * x * x * x + (ulong)POLY_B * x * x + (ulong)POLY_C * x + (ulong)POLY_SEED) & 0xFFFFFFFFFFFFFFFF;
char[] serial = new char[SERIAL_LENGTH];
for (int i = 0; i < SERIAL_LENGTH; i++)
{
int index = (int)((result >> (i * 5)) % (ulong)CHARSET.Length);
serial[i] = CHARSET[index];
result = ((result * (ulong)POLY_A + (ulong)POLY_B) ^ (result >> 13)) & 0xFFFFFFFFFFFFFFFF;
}
return new string(serial);
}
/// <summary>
/// Проверка валидности серийника для MAC адреса
/// </summary>
public static bool VerifySerial(string macAddress, string serialOrEncrypted)
{
try
{
string decryptedSerial = DecryptSerial(serialOrEncrypted);
string expectedSerial = GenerateSerialFromMac(macAddress);
return decryptedSerial == expectedSerial;
}
catch
{
return false;
}
}
/// <summary>
/// Проверка валидности серийника для MAC адреса с возвратом подробной информации
/// </summary>
public static (bool IsValid, string Message) VerifySerialDetailed(string macAddress, string serialOrEncrypted)
{
try
{
string decryptedSerial = DecryptSerial(serialOrEncrypted);
string expectedSerial = GenerateSerialFromMac(macAddress);
if (decryptedSerial == expectedSerial)
{
return (true, "Valid serial");
}
else
{
return (false, $"Invalid! Expected: {expectedSerial}, Got: {decryptedSerial}");
}
}
catch (Exception ex)
{
return (false, $"Verification error: {ex.Message}");
}
}
}