diff --git a/PKHeX.Core/Legality/LearnSource/MoveResult.cs b/PKHeX.Core/Legality/LearnSource/MoveResult.cs index 6b5187679..df106a6fb 100644 --- a/PKHeX.Core/Legality/LearnSource/MoveResult.cs +++ b/PKHeX.Core/Legality/LearnSource/MoveResult.cs @@ -69,6 +69,9 @@ public readonly record struct MoveResult(MoveLearnInfo Info, byte EvoStage = 0, public static MoveResult Unobtainable(ushort expect) => new(LearnMethod.UnobtainableExpect) { Expect = expect }; public static MoveResult Unobtainable() => new(LearnMethod.Unobtainable); + /// + /// Checks if all s in the span are . + /// public static bool AllValid(ReadOnlySpan span) { foreach (var result in span) @@ -79,6 +82,9 @@ public readonly record struct MoveResult(MoveLearnInfo Info, byte EvoStage = 0, return true; } + /// + /// Checks if all s in the span are . + /// public static bool AllParsed(ReadOnlySpan span) { foreach (var result in span) diff --git a/PKHeX.Core/Legality/RNG/CXD/LockFinder.cs b/PKHeX.Core/Legality/RNG/CXD/LockFinder.cs index e4bec3be6..34a763db7 100644 --- a/PKHeX.Core/Legality/RNG/CXD/LockFinder.cs +++ b/PKHeX.Core/Legality/RNG/CXD/LockFinder.cs @@ -52,10 +52,13 @@ public static class LockFinder /// True if the starter ID correlation is correct public static bool IsXDStarterValid(uint seed, uint TID16, uint SID16) { - // pidiv reversed 2x yields SID16, 3x yields TID16. shift by 7 if another PKM is generated prior - var SIDf = XDRNG.Prev2(seed); - var TIDf = XDRNG.Prev(SIDf); - return SIDf >> 16 == SID16 && TIDf >> 16 == TID16; + // pidiv is right before the IV calls; need to unroll 2x for fake calls, then unroll for the TID/SID. + // reversed 2x yields SID16, 3x yields TID16. + var TIDf = XDRNG.Prev3(seed); + if (TIDf >> 16 != TID16) + return false; + var SIDf = XDRNG.Next(TIDf); + return SIDf >> 16 == SID16; } /// diff --git a/PKHeX.Core/PKM/Strings/StringConverter12.cs b/PKHeX.Core/PKM/Strings/StringConverter12.cs index f7351ab32..5543468d1 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter12.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter12.cs @@ -101,6 +101,8 @@ public static class StringConverter12 /// Character count loaded. public static int LoadString(ReadOnlySpan data, Span result, bool jp) { + if (data.Length == 0) + return 0; if (data[0] == G1TradeOTCode) // In-game Trade { result[0] = G1TradeOT; @@ -139,7 +141,9 @@ public static class StringConverter12 else if (option is StringConverterOption.Clear7F) destBuffer.Fill(G1SpaceCode); - if (value.Length != 0 && value[0] == G1TradeOT) // Handle "[TRAINER]" + if (value.Length == 0) + return 0; + if (value[0] == G1TradeOT) // Handle "[TRAINER]" { destBuffer[0] = G1TradeOTCode; destBuffer[1] = G1TerminatorCode; @@ -149,7 +153,7 @@ public static class StringConverter12 if (value.Length > maxLength) value = value[..maxLength]; // Hard cap - var dict = jp ? TableEN : TableJP; + var dict = jp ? TableJP : TableEN; int i = 0; for (; i < value.Length; i++) { @@ -213,7 +217,7 @@ public static class StringConverter12 'チ', 'ツ', 'テ', 'ト', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'ヒ', 'フ', 'ホ', 'マ', 'ミ', 'ム', // 90-9F 'メ', 'モ', 'ヤ', 'ユ', 'ヨ', 'ラ', 'ル', 'レ', 'ロ', 'ワ', 'ヲ', 'ン', 'ッ', 'ャ', 'ュ', 'ョ', // A0-AF 'ィ', 'あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', // B0-BF - 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'ヘ', 'ほ', 'ま', // C0-CF + 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', // C0-CF 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'リ', 'る', 'れ', 'ろ', 'わ', 'を', 'ん', 'っ', // D0-DF 'ゃ', 'ゅ', 'ょ', 'ー', '゚', '゙', '?', '!', '。', 'ァ', 'ゥ', 'ェ', NUL, NUL, NUL, '♂', // E0-EF MNY, NUL, '.', '/', 'ォ', '♀', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // F0-FF diff --git a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs index ebbc8be16..6e279cba7 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs @@ -40,6 +40,8 @@ public static class StringConverter2KOR /// Character count loaded. public static int LoadString(ReadOnlySpan data, Span result) { + if (data.Length == 0) + return 0; if (data[0] == G1TradeOTCode) // In-game Trade { result[0] = G1TradeOT; @@ -91,7 +93,10 @@ public static class StringConverter2KOR else if (option is StringConverterOption.Clear50) destBuffer.Fill(G1TerminatorCode); - if (value.Length != 0 && value[0] == G1TradeOT) // Handle "[TRAINER]" + if (value.Length == 0) + return 0; + // Can't get Gen1 In-Game trades with this char, but handle it anyways since the game can handle it. + if (value[0] == G1TradeOT) // Handle "[TRAINER]" { destBuffer[0] = G1TradeOTCode; destBuffer[1] = G1TerminatorCode; diff --git a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs index df42f7a99..23412d54b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/ItemInfo6.cs @@ -10,16 +10,16 @@ public sealed class ItemInfo6 : SaveBlock private const int BoundItemCount = 4; private const int RecentItemCount = 12; - public int[] SelectItems + public ushort[] SelectItems { // UP,RIGHT,DOWN,LEFT get { var span = Data.AsSpan(Offset + 10); - int[] list = new int[BoundItemCount]; - for (int i = 0; i < list.Length; i++) - list[i] = ReadUInt16LittleEndian(span[(2 * i)..]); - return list; + var result = new ushort[BoundItemCount]; + for (int i = 0; i < result.Length; i++) + result[i] = ReadUInt16LittleEndian(span[(2 * i)..]); + return result; } set { @@ -27,20 +27,20 @@ public sealed class ItemInfo6 : SaveBlock throw new ArgumentException(nameof(value)); var span = Data.AsSpan(Offset + 10); for (int i = 0; i < value.Length; i++) - WriteUInt16LittleEndian(span[(2 * i)..], (ushort)value[i]); + WriteUInt16LittleEndian(span[(2 * i)..], value[i]); } } - public int[] RecentItems + public ushort[] RecentItems { // Items recently interacted with (Give, Use) get { var span = Data.AsSpan(Offset + 20); - int[] list = new int[RecentItemCount]; - for (int i = 0; i < list.Length; i++) - list[i] = ReadUInt16LittleEndian(span[(2 * i)..]); - return list; + var result = new ushort[RecentItemCount]; + for (int i = 0; i < result.Length; i++) + result[i] = ReadUInt16LittleEndian(span[(2 * i)..]); + return result; } set { @@ -48,7 +48,7 @@ public sealed class ItemInfo6 : SaveBlock throw new ArgumentException(nameof(value)); var span = Data.AsSpan(Offset + 20); for (int i = 0; i < value.Length; i++) - WriteUInt16LittleEndian(span[(2 * i)..], (ushort)value[i]); + WriteUInt16LittleEndian(span[(2 * i)..], value[i]); } } } diff --git a/Tests/PKHeX.Core.Tests/Legality/LegalityTests.cs b/Tests/PKHeX.Core.Tests/Legality/LegalityTests.cs index 5eb40e049..98629e23a 100644 --- a/Tests/PKHeX.Core.Tests/Legality/LegalityTests.cs +++ b/Tests/PKHeX.Core.Tests/Legality/LegalityTests.cs @@ -99,8 +99,8 @@ public class LegalityTest var info = legality.Info; foreach (var m in info.Moves.Where(z => !z.Valid)) - yield return m.Summary(legality.Info.Entity, legality.Info.EvoChainsAllGens); + yield return m.Summary(info.Entity, info.EvoChainsAllGens); foreach (var r in info.Relearn.Where(z => !z.Valid)) - yield return r.Summary(legality.Info.Entity, legality.Info.EvoChainsAllGens); + yield return r.Summary(info.Entity, info.EvoChainsAllGens); } }