Update how symbols are handled for Bank -> HOME transfers (#4276)

This maps the remaining (legal) symbols in the private use area that are modified on transfer from Bank -> HOME. If any of these replacements are made, any leading or trailing halfwidth spaces are trimmed. This can result in nicknames/OT names that are the empty string or consist entirely of fullwidth spaces, even though these can't normally entered.
This commit is contained in:
abcboy101 2024-05-17 12:56:55 -04:00 committed by GitHub
parent 4d6ce53bce
commit 417231a67c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 89 additions and 21 deletions

View file

@ -113,10 +113,10 @@ public sealed class GameDataPK8 : HomeOptional1, IGameDataSide<PK8>, IGigantamax
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
{

View file

@ -141,29 +141,97 @@ public static class StringConverter8
/// <summary>
/// Used when importing a 3DS string into HOME.
/// </summary>
public static void NormalizeHalfWidth(Span<byte> str)
public static void TransferGlyphs78(Span<byte> str)
{
if (BitConverter.IsLittleEndian)
bool modified = false;
var u16 = MemoryMarshal.Cast<byte, char>(str);
foreach (ref var c in u16)
{
var u16 = MemoryMarshal.Cast<byte, char>(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<char> 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<char> 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];
}
}