diff --git a/OpenKh.Bbs/Messages/Internals/InternationalCtdEncoder.cs b/OpenKh.Bbs/Messages/Internals/InternationalCtdEncoder.cs index 36b15e55f..15c32d234 100644 --- a/OpenKh.Bbs/Messages/Internals/InternationalCtdEncoder.cs +++ b/OpenKh.Bbs/Messages/Internals/InternationalCtdEncoder.cs @@ -9,11 +9,36 @@ internal class InternationalCtdEncoder : ICtdMessageEncoder { private static readonly string _mapping0 = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[¥]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + private static readonly string _mapping81 = + "$??%#&*@"; private static readonly string _mapping99 = - "ÀÁÂÄÆÇÈÉÊËÌÍÎÏÑÒÓÔÕÖŒÙÚÛÜßàáâäæçèéêëìíîïñòóôõöùúûüœ¿¡"; + "ÀÁÂÄÆÇÈÉÊËÌÍÎÏÑÒÓÔÕÖŒÙÚÛÜßàáâäæçèéêëìíîïñòóôõöùúûüœ¿¡‚„—°«»≤≥❤¹²³⁴⁵£€§·¢¨‘’©®™‾ₐ"; + private static readonly Dictionary _mappingF1 = new Dictionary + { + [0xae] = "button-triangle", + [0xaf] = "button-circle", + [0xb0] = "button-square", + [0xb1] = "button-cross", + [0xb2] = "button-analog", + [0xb3] = "button-r", + [0xb4] = "button-l", + [0xc3] = "button-dpad", + [0xc4] = "button-dpad-h", + [0xc5] = "button-dpad-v", + }; + private static readonly Dictionary _mappingF9 = new Dictionary + { + [0x41] = "default", + [0x58] = "white", + [0x59] = "yellow", + }; private static readonly Dictionary _inverseMapping = _mapping0 .Select((x, i) => new { Ch = x, Data = i + 0x20 }) + .Concat(_mapping81.Select((x, i) => new { Ch = x, Data = 0x8190 + i })) + .Concat(_mapping99.Select((x, i) => new { Ch = x, Data = 0x9980 + i })) + .GroupBy(x => x.Ch) + .Select(x => x.First()) .ToDictionary(x => x.Ch, x => x.Data); public string Decode(byte[] data) @@ -33,32 +58,61 @@ public byte[] Encode(string text) foreach (var ch in text) { var data = _inverseMapping[ch]; - + if (data < 0x100) encoded.Add((byte)data); + else + { + encoded.Add((byte)(data >> 8)); + encoded.Add((byte)(data & 0xFF)); + } } return encoded.ToArray(); } - private static char GetCharacter(byte[] data, ref int index) + private static string GetCharacter(byte[] data, ref int index) { var ch = data[index++]; if (ch >= 0x00 && ch < 0x20) - return (char)ch; + return $"{(char)ch}"; if (ch >= 0x20 && ch < 0x80) - return _mapping0[ch - 0x20]; + return $"{_mapping0[ch - 0x20]}"; - var ch2 = data[index++]; + var param = data[index++]; switch (ch) { + case 0x81: + return $"{_mapping81[param - 0x90]}"; case 0x99: - return _mapping99[ch2 - 0x80]; + return $"{_mapping99[param - 0x80]}"; + default: + return GetCommand(ch, param); + } + } + + private static string GetCommand(byte command, byte param) + { + string name; + string value; + + switch (command) + { + case 0xf1: + name = "icon"; + value = _mappingF1[param]; + break; + case 0xf9: + name = "color"; + value = _mappingF9[param]; + break; + default: + throw new Exception($"Data \"{command:X02} {param:X02}\" cannot be decoded."); } - throw new Exception($"Data {ch:X02} cannot be decoded."); + return $"{{:{name} {value}}}"; } } } diff --git a/OpenKh.Tests/Bbs/CtdEncodingTests.cs b/OpenKh.Tests/Bbs/CtdEncodingTests.cs index 6fb0e4dba..7157de367 100644 --- a/OpenKh.Tests/Bbs/CtdEncodingTests.cs +++ b/OpenKh.Tests/Bbs/CtdEncodingTests.cs @@ -43,10 +43,29 @@ public void InternationalDecodingSimpleTest(char expected, byte ch) [InlineData('Ò', 0x99, 0x8f)] [InlineData('ç', 0x99, 0x9f)] [InlineData('ú', 0x99, 0xaf)] + [InlineData('$', 0x81, 0x90)] + [InlineData('%', 0x81, 0x93)] + [InlineData('#', 0x81, 0x94)] + [InlineData('&', 0x81, 0x95)] + [InlineData('*', 0x81, 0x96)] + [InlineData('@', 0x81, 0x97)] public void InternationalDecodingExtTest(char expected, byte ch, byte ch2) { string actual = euDec.Decode(new byte[] { ch, ch2 }); Assert.Equal(expected, actual[0]); } + + [Theory] + [InlineData(0xf1, 0xae, "{:icon button-triangle}")] + [InlineData(0xf1, 0xb4, "{:icon button-l}")] + [InlineData(0xf1, 0xc5, "{:icon button-dpad-v}")] + [InlineData(0xf9, 0x41, "{:color default}")] + [InlineData(0xf9, 0x58, "{:color white}")] + [InlineData(0xf9, 0x59, "{:color yellow}")] + public void InternationalDecodingCommandTest(byte command, byte parameter, string expected) + { + string actual = euDec.Decode(new byte[] { command, parameter }); + Assert.Equal(expected, actual); + } } }