using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; namespace Ra3.BattleNet.Database.Utils; public static class ChineseEncoding { private const int EncodedChineseTotalLength = 120; private const int EncodedChineseChecksumLength = 3; private const int EncodedChineseContentLength = EncodedChineseTotalLength - EncodedChineseChecksumLength; private const int ChineseCodeSize = 13; private const int ChineseCodeMask = 0b1111111111111; private const int AsciiSectionSize = 0x80; private const int Gb2312EucOffset = 0xA0; private const int Gb2312RowWidth = 94; private const int Gb2312LastRow = 87; private const int Gb2312FirstUnassignedSectionBegin = 10; private const int Gb2312FirstUnassignedSectionSize = 6; private const int Gb2312SecondUnassignedSectionBegin = 88; private const int Base64CodeSize = 6; private const int Base64CodeMask = 0b111111; private const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; public static string DecodeChineseFromBase64(string original) { BigInteger bits = 0; int index = 0; foreach (var c in original) { BigInteger value = Base64Table.IndexOf(c); if (value == -1) { throw new Exception("Invalid base64 string"); } bits = bits | (value << index); index += Base64CodeSize; } BigInteger checksum = 0; var result = new List(); for (var bitIndex = 0; bitIndex < EncodedChineseContentLength; bitIndex += ChineseCodeSize) { int value = (int)((bits >> bitIndex) & ChineseCodeMask); checksum += value; if (value == 0) { break; } if (value < AsciiSectionSize) { if (value < 0x20 || value == 0x7F) { throw new Exception("Invalid ASCII"); } result.Add((byte)value); } else { value -= AsciiSectionSize; var index1 = value / Gb2312RowWidth + 1; var index2 = value % Gb2312RowWidth + 1; if (index1 >= Gb2312FirstUnassignedSectionBegin) { index1 += Gb2312FirstUnassignedSectionSize; } if (index1 >= Gb2312SecondUnassignedSectionBegin) { throw new Exception("Invalid first byte"); } if (index2 > Gb2312RowWidth) { throw new Exception("Invalid second byte"); } index1 += Gb2312EucOffset; index2 += Gb2312EucOffset; result.Add((byte)index1); result.Add((byte)index2); } } if ((bits >> EncodedChineseContentLength) != checksum % 8) { throw new Exception("Invalid checksum"); } Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); return Encoding.GetEncoding(936).GetString(result.ToArray()); } public static string EncodeChineseToBase64(string original) { BigInteger bits = 0; int index = 0; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var bytes = Encoding.GetEncoding(936).GetBytes(original); BigInteger checksum = 0; for (var i = 0; i < bytes.Length;) { BigInteger c = bytes[i]; if (c < AsciiSectionSize) { if (c < 0x20 || c == 0x7F) { throw new Exception("Invalid ASCII"); } bits = bits | (c << index); index += ChineseCodeSize; checksum += c; i++; } else { if (c <= Gb2312EucOffset || c > (Gb2312EucOffset + Gb2312LastRow)) { throw new Exception("Invalid first byte"); } BigInteger c2 = bytes[i + 1]; if (c2 <= Gb2312EucOffset || c2 > (Gb2312EucOffset + Gb2312RowWidth)) { throw new Exception("Invalid second byte"); } var index1 = c - Gb2312EucOffset; if (index1 >= Gb2312FirstUnassignedSectionBegin) { var offset = index1 - Gb2312FirstUnassignedSectionBegin; if (offset < Gb2312FirstUnassignedSectionSize) { throw new Exception("AA-AF user defined zone not supported"); } index1 -= Gb2312FirstUnassignedSectionSize; } var index2 = c2 - Gb2312EucOffset; if (index2 > Gb2312RowWidth) { throw new Exception("Invalid second byte"); } var value = (index1 - 1) * Gb2312RowWidth + (index2 - 1); value = AsciiSectionSize + value; bits = bits | (value << index); index += ChineseCodeSize; checksum += value; i += 2; } } bits = bits | ((checksum % 8) << EncodedChineseContentLength); var result = ""; var bitsIndex = 0; while (bitsIndex < EncodedChineseTotalLength) { int v = (int)((bits >> bitsIndex) & Base64CodeMask); result += Base64Table[v]; bitsIndex += Base64CodeSize; } return result; } public static string GetPrettyName(string name) { if (name.Length != 20) { return name; } foreach (var c in name) { if (!Base64Table.Contains(c)) { return name; } } try { return DecodeChineseFromBase64(name); } catch { return name; } } }