AnotherReplayReader/ChineseEncoding.cs

179 lines
6.1 KiB
C#

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<byte>();
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;
}
}
}