diff --git a/PKHeX.Core/PKM/HOME/GameDataPK8.cs b/PKHeX.Core/PKM/HOME/GameDataPK8.cs index 757b962e6..e77793efb 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPK8.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPK8.cs @@ -113,10 +113,10 @@ public void CopyFrom(PK7 pk, PKH pkh) Ability = (ushort)pk.Ability; pkh.MarkingValue &= 0b1111_1111_1111; - StringConverter8.NormalizeHalfWidth(pkh.OriginalTrainerTrash); + StringConverter8.TransferGlyphs78(pkh.OriginalTrainerTrash); if (pk.IsNicknamed) { - StringConverter8.NormalizeHalfWidth(pkh.NicknameTrash); + StringConverter8.TransferGlyphs78(pkh.NicknameTrash); } else { diff --git a/PKHeX.Core/PKM/Strings/StringConverter8.cs b/PKHeX.Core/PKM/Strings/StringConverter8.cs index 56e15d06b..86ffb0d44 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter8.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter8.cs @@ -141,29 +141,97 @@ private static bool IsEqualsEncoded(ReadOnlySpan relevantSection, ReadOnly /// /// Used when importing a 3DS string into HOME. /// - public static void NormalizeHalfWidth(Span str) + public static void TransferGlyphs78(Span str) { - if (BitConverter.IsLittleEndian) + bool modified = false; + var u16 = MemoryMarshal.Cast(str); + foreach (ref var c in u16) { - var u16 = MemoryMarshal.Cast(str); - foreach (ref var c in u16) - { - if (c == TerminatorNull) - return; - c = NormalizeHalfWidth(c); - } + var x = c; + if (x == TerminatorNull) + break; + if (!BitConverter.IsLittleEndian) + x = (char)ReverseEndianness(x); + + var t = TransferGlyphs78(x); + if (t == x) + continue; + + if (!BitConverter.IsLittleEndian) + t = (char)ReverseEndianness(t); + c = t; + modified = true; } - // Slower path for Big-Endian runtimes. - for (int i = 0; i < str.Length; i += 2) - { - var data = str[i..]; - var c = ReadUInt16LittleEndian(data); - if (c == TerminatorNull) - return; - WriteUInt16LittleEndian(data, NormalizeHalfWidth((char)c)); - } + if (modified) + TrimHalfSpaces(u16); } - private static char NormalizeHalfWidth(char str) => StringConverter.NormalizeGenderSymbol(str); + private static void TrimHalfSpaces(Span u16) + { + // If a replacement is made, any leading or trailing halfwidth spaces are trimmed. + // This allows nicknames/OT names that are the empty string or consist entirely of fullwidth spaces. + var region = u16[..u16.IndexOf((char)TerminatorNull)]; + char seek = ' '; + if (BitConverter.IsLittleEndian) + seek = (char)ReverseEndianness(' '); + + var trim = region.Trim(seek); + if (region.Length == trim.Length) + return; + + trim.CopyTo(u16); + u16[trim.Length..].Clear(); + } + + private static ReadOnlySpan Glyphs78 => + [ + ' ', // '\uE081' -> '\u3000' + ' ', // '\uE082' -> '\u3000' + ' ', // '\uE083' -> '\u3000' + ' ', // '\uE084' -> '\u3000' + ' ', // '\uE085' -> '\u3000' + ' ', // '\uE086' -> '\u3000' + ' ', // '\uE087' -> '\u3000' + '', // '\uE088' -> '\uE088' + '', // '\uE089' -> '\uE089' + '', // '\uE08A' -> '\uE08A' + '', // '\uE08B' -> '\uE08B' + '', // '\uE08C' -> '\uE08C' + '…', // '\uE08D' -> '\u2026' + '♂', // '\uE08E' -> '\u2642' + '♀', // '\uE08F' -> '\u2640' + '♠', // '\uE090' -> '\u2660' + '♣', // '\uE091' -> '\u2663' + '♥', // '\uE092' -> '\u2665' + '♦', // '\uE093' -> '\u2666' + '★', // '\uE094' -> '\u2605' + '◎', // '\uE095' -> '\u25CE' + '○', // '\uE096' -> '\u25CB' + '□', // '\uE097' -> '\u25A1' + '△', // '\uE098' -> '\u25B3' + '◇', // '\uE099' -> '\u25C7' + '♪', // '\uE09A' -> '\u266A' + '☀', // '\uE09B' -> '\u2600' + '☁', // '\uE09C' -> '\u2601' + '☂', // '\uE09D' -> '\u2602' + '☃', // '\uE09E' -> '\u2603' + ' ', // '\uE09F' -> '\u0020' + ' ', // '\uE0A0' -> '\u0020' + ' ', // '\uE0A1' -> '\u0020' + ' ', // '\uE0A2' -> '\u0020' + ' ', // '\uE0A3' -> '\u0020' + ' ', // '\uE0A4' -> '\u0020' + ' ', // '\uE0A5' -> '\u0020' + ]; + + private const int Glyphs78Start = 0xE081; + + private static char TransferGlyphs78(char chr) + { + int index = chr - Glyphs78Start; + if ((uint)index >= Glyphs78.Length) + return chr; + return Glyphs78[index]; + } }