From a5b46d80f579cb34a4a505ff3db9705ba4c00db0 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 14 May 2022 08:28:13 -0700 Subject: [PATCH] Minor tweaks ShowdownSet: Lessen allocation MoveTutor: Remove boxing by calling the generic method instead of object method Xoro8b: Add more xmldoc, use positive constant instead of inverse negative for parity StadiumUtil: Use built-in endianness reversal methods --- PKHeX.Core/Editing/Showdown/ShowdownSet.cs | 52 ++++-- PKHeX.Core/Legality/Moves/MoveTutor.cs | 2 +- .../RNG/Algorithms/Xoroshiro128Plus8b.cs | 158 +++++++++--------- PKHeX.Core/Saves/Util/StadiumUtil.cs | 24 ++- 4 files changed, 130 insertions(+), 106 deletions(-) diff --git a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs index 7ce6c78ba..c16bb78aa 100644 --- a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs +++ b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using static PKHeX.Core.Species; namespace PKHeX.Core @@ -97,9 +96,6 @@ public sealed class ShowdownSet : IBattleTemplate private void LoadLines(IEnumerable lines) { - lines = lines.Select(z => z.Replace('\'', '’').Replace('–', '-').Trim()); // Sanitize apostrophes & dashes - lines = lines.Where(z => z.Length > 2); - ParseLines(lines); FormName = ShowdownParsing.SetShowdownFormName(Species, FormName, Ability); @@ -110,6 +106,23 @@ private void LoadLines(IEnumerable lines) ReviseGenderedForms(); } + private static IEnumerable GetSanitizedLines(IEnumerable lines) + { + foreach (var line in lines) + { + var trim = line.Trim(); + if (trim.Length <= 2) + continue; + + // Sanitize apostrophes & dashes + if (trim.IndexOf('\'') != -1) + trim = trim.Replace('\'', '’'); + if (trim.IndexOf('–') != -1) + trim = trim.Replace('–', '-'); + yield return trim; + } + } + private void ReviseGenderedForms() { if (Gender == 1) // Recognized with (F) @@ -128,6 +141,7 @@ private void ReviseGenderedForms() private void ParseLines(IEnumerable lines) { + lines = GetSanitizedLines(lines); using var e = lines.GetEnumerator(); if (!e.MoveNext()) return; @@ -146,7 +160,7 @@ private void ParseLines(IEnumerable lines) int move = StringUtil.FindIndexIgnoreCase(Strings.movelist, moveString); if (move < 0) InvalidLines.Add($"Unknown Move: {moveString}"); - else if (Moves.Contains(move)) + else if (Array.IndexOf(Moves, move) != -1) InvalidLines.Add($"Duplicate Move: {moveString}"); else Moves[movectr++] = move; @@ -325,15 +339,19 @@ private static IList GetStringStats(ReadOnlySpan stats, int ignore) private IEnumerable GetStringMoves() { - foreach (int move in Moves.Where(move => move != 0 && move < Strings.Move.Count)) + var moves = Strings.Move; + foreach (int move in Moves) { + if ((uint)move >= moves.Count) + continue; + if (move == 237) // Hidden Power { - yield return $"- {Strings.Move[move]} [{Strings.Types[1 + HiddenPowerType]}]"; + yield return $"- {moves[move]} [{Strings.Types[1 + HiddenPowerType]}]"; continue; } - yield return $"- {Strings.Move[move]}"; + yield return $"- {moves[move]}"; } } @@ -431,7 +449,7 @@ private void ParseFirstLineNoItem(string line) } // Nickname Detection - if (line.Contains('(') && line.Contains(')')) + if (line.IndexOf('(') != -1 && line.IndexOf(')') != -1) ParseSpeciesNickname(line); else ParseSpeciesForm(line); @@ -535,7 +553,7 @@ private string ParseLineMove(string line) int hpVal = StringUtil.FindIndexIgnoreCase(Strings.types, type) - 1; // Get HP Type HiddenPowerType = hpVal; - if (IVs.Any(z => z != 31)) + if (!Array.TrueForAll(IVs, z => z == 31)) { if (!HiddenPower.SetIVsForType(hpVal, IVs, Format)) InvalidLines.Add($"Invalid IVs for Hidden Power Type: {type}"); @@ -585,7 +603,19 @@ private void ParseLineIVs(string line) IVs = IVsSpeedFirst; } - private static string RemoveAll(string original, char[] remove) => string.Concat(original.Where(z => !remove.Contains(z))); + private static string RemoveAll(string original, char[] remove) + { + Span result = stackalloc char[original.Length]; + int ctr = 0; + foreach (var c in original) + { + if (Array.IndexOf(remove, c) == -1) + result[ctr++] = c; + } + if (ctr == original.Length) + return original; + return new string(result[..ctr].ToArray()); + } private static string[] SplitLineStats(string line) { diff --git a/PKHeX.Core/Legality/Moves/MoveTutor.cs b/PKHeX.Core/Legality/Moves/MoveTutor.cs index 8f0054e4a..ef9655ed8 100644 --- a/PKHeX.Core/Legality/Moves/MoveTutor.cs +++ b/PKHeX.Core/Legality/Moves/MoveTutor.cs @@ -158,7 +158,7 @@ private static GameVersion GetIsTutor8(PKM pkm, int species, int form, bool spec var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(species, form); if (!pi.IsPresentInGame) return NONE; - var index = Array.IndexOf(MoveShop8_LA, move); + var index = Array.IndexOf(MoveShop8_LA, (ushort)move); if (index != -1 && pi.SpecialTutors[0][index]) return GameVersion.PLA; diff --git a/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus8b.cs b/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus8b.cs index 3957cc5be..2b343ec7c 100644 --- a/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus8b.cs +++ b/PKHeX.Core/Legality/RNG/Algorithms/Xoroshiro128Plus8b.cs @@ -1,100 +1,98 @@ using System.Runtime.CompilerServices; -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Self-modifying RNG structure that implements xoroshiro128+ which split-mixes the initial seed to populate all 128-bits of the initial state, rather than using a fixed 64-bit half. +/// +/// https://en.wikipedia.org/wiki/Xoroshiro128%2B +/// +/// Used by the Brilliant Diamond & Shining Pearl games; differs in how values are yielded by Next calls. +public ref struct Xoroshiro128Plus8b { - /// - /// Self-modifying RNG structure that implements xoroshiro128+ which split-mixes the initial seed to populate all 128-bits of the initial state, rather than using a fixed 64-bit half. - /// - /// https://en.wikipedia.org/wiki/Xoroshiro128%2B - /// - public ref struct Xoroshiro128Plus8b + private ulong s0, s1; + + public Xoroshiro128Plus8b(ulong seed) { - private ulong s0, s1; + s0 = SplitMix64(seed + 0x9E3779B97F4A7C15); + s1 = SplitMix64(seed + 0x3C6EF372FE94F82A); + } - public Xoroshiro128Plus8b(ulong seed) - { - var _s0 = seed - 0x61C8864680B583EB; - var _s1 = seed + 0x3C6EF372FE94F82A; + private static ulong SplitMix64(ulong seed) + { + seed = 0xBF58476D1CE4E5B9 * (seed ^ (seed >> 30)); + seed = 0x94D049BB133111EB * (seed ^ (seed >> 27)); + return seed ^ (seed >> 31); + } - _s0 = 0xBF58476D1CE4E5B9 * (_s0 ^ (_s0 >> 30)); - _s1 = 0xBF58476D1CE4E5B9 * (_s1 ^ (_s1 >> 30)); + public Xoroshiro128Plus8b(ulong s0, ulong s1) + { + this.s0 = s0; + this.s1 = s1; + } - _s0 = 0x94D049BB133111EB * (_s0 ^ (_s0 >> 27)); - _s1 = 0x94D049BB133111EB * (_s1 ^ (_s1 >> 27)); + public (ulong s0, ulong s1) GetState() => (s0, s1); + public string FullState => $"{s1:X16}{s0:X16}"; - s0 = _s0 ^ (_s0 >> 31); - s1 = _s1 ^ (_s1 >> 31); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong RotateLeft(ulong x, int k) + { + return (x << k) | (x >> (64 - k)); + } - public Xoroshiro128Plus8b(ulong s0, ulong s1) - { - this.s0 = s0; - this.s1 = s1; - } + /// + /// Gets the next random . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong Next() + { + var _s0 = s0; + var _s1 = s1; + ulong result = _s0 + _s1; - public (ulong s0, ulong s1) GetState() => (s0, s1); - public string FullState => $"{s1:X16}{s0:X16}"; + _s1 ^= _s0; + // Final calculations and store back to fields + s0 = RotateLeft(_s0, 24) ^ _s1 ^ (_s1 << 16); + s1 = RotateLeft(_s1, 37); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong RotateLeft(ulong x, int k) - { - return (x << k) | (x >> (64 - k)); - } + return result; + } - /// - /// Gets the next random . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong Next() - { - var _s0 = s0; - var _s1 = s1; - ulong result = _s0 + _s1; + /// + /// Gets the next previous . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong Prev() + { + var _s0 = s0; + var _s1 = s1; + _s1 = RotateLeft(_s1, 27); + _s0 = _s0 ^ _s1 ^ (_s1 << 16); + _s0 = RotateLeft(_s0, 40); + _s1 ^= _s0; + ulong result = _s0 + _s1; - _s1 ^= _s0; - // Final calculations and store back to fields - s0 = RotateLeft(_s0, 24) ^ _s1 ^ (_s1 << 16); - s1 = RotateLeft(_s1, 37); + s0 = _s0; + s1 = _s1; + return result; + } - return result; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool NextBool() => (Next() >> 63) != 0; - /// - /// Gets the next previous . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong Prev() - { - var _s0 = s0; - var _s1 = s1; - _s1 = RotateLeft(_s1, 27); - _s0 = _s0 ^ _s1 ^ (_s1 << 16); - _s0 = RotateLeft(_s0, 40); - _s1 ^= _s0; - ulong result = _s0 + _s1; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte NextByte() => (byte)(Next() >> 56); - s0 = _s0; - s1 = _s1; - return result; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort UShort() => (ushort)(Next() >> 48); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NextBool() => (Next() >> 63) != 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint NextUInt() => (uint)(Next() >> 32); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte NextByte() => (byte)(Next() >> 56); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ushort UShort() => (ushort)(Next() >> 48); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint NextUInt() => (uint)(Next() >> 32); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint NextUInt(uint max) - { - var rnd = NextUInt(); - return rnd - ((rnd / max) * max); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint NextUInt(uint max) + { + var rnd = NextUInt(); + return rnd - ((rnd / max) * max); } } diff --git a/PKHeX.Core/Saves/Util/StadiumUtil.cs b/PKHeX.Core/Saves/Util/StadiumUtil.cs index 5d029e7d1..f0ef4d31f 100644 --- a/PKHeX.Core/Saves/Util/StadiumUtil.cs +++ b/PKHeX.Core/Saves/Util/StadiumUtil.cs @@ -43,17 +43,16 @@ public static bool IsMagicPresent(ReadOnlySpan data, int size, uint magic) public static bool IsMagicPresentSwap(ReadOnlySpan data, int size, uint magic) { // Check footers of first few teams to see if the magic value is there. - var left = (ushort)magic; - var right = (ushort)(magic >> 16); - left = (ushort)((left >> 8) | (left << 8)); - right = (ushort)((right >> 8) | (right << 8)); + var right = ReverseEndianness((ushort)(magic >> 16)); + var left = ReverseEndianness((ushort)magic); for (int i = 0; i < 10; i++) { - var ofs = size - 6 + (i * size); - if (ReadUInt16LittleEndian(data[(ofs - 2)..]) != left) // OP + var offset = size - 6 + (i * size); + + if (ReadUInt16LittleEndian(data[(offset + 4)..]) != right) // EK return false; - if (ReadUInt16LittleEndian(data[(ofs + 4)..]) != right) // EK + if (ReadUInt16LittleEndian(data[(offset - 2)..]) != left) // OP return false; } return true; @@ -65,15 +64,12 @@ public static bool IsMagicPresentAbsolute(ReadOnlySpan data, int offset, u if (actual == magic) // POKE return true; - var left = (ushort)magic; - var right = (ushort)(magic >> 16); - left = (ushort)((left >> 8) | (left << 8)); - right = (ushort)((right >> 8) | (right << 8)); - - if (ReadUInt16LittleEndian(data[(offset - 2)..]) != left) // OP - return false; + var right = ReverseEndianness((ushort)(magic >> 16)); if (ReadUInt16LittleEndian(data[(offset + 4)..]) != right) // EK return false; + var left = ReverseEndianness((ushort)magic); + if (ReadUInt16LittleEndian(data[(offset - 2)..]) != left) // OP + return false; return true; }