Minor tweaks

Rival_Trash -> RivalTrash
BinaryCodedDecimal better method names
Gift dupe checker differentiate by species
This commit is contained in:
Kurt 2024-06-10 01:33:46 -05:00
parent 992deea183
commit 4f23efd939
25 changed files with 120 additions and 123 deletions

View file

@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.CheckIdentifier;
@ -15,14 +17,21 @@ public sealed class DuplicateGiftChecker : IBulkAnalyzer
private static void CheckDuplicateOwnedGifts(BulkAnalysis input)
{
var all = input.AllData;
var combined = new CombinedReference[all.Count];
for (int i = 0; i < combined.Length; i++)
combined[i] = new CombinedReference(all[i], input.AllAnalysis[i]);
var initialCapacity = all.Count / 20; // less likely to have gift eggs
var combined = new List<CombinedReference>(initialCapacity);
for (int i = 0; i < all.Count; i++)
{
var c = all[i];
var la = input.AllAnalysis[i];
if (!IsEventEgg(c, la))
continue;
combined.Add(new(c, la));
}
var dupes = combined.Where(z =>
z.Analysis.Info.Generation >= 3
&& z.Analysis.EncounterMatch is MysteryGift { IsEgg: true } && !z.Slot.Entity.WasTradedEgg)
.GroupBy(z => ((MysteryGift)z.Analysis.EncounterMatch).CardTitle);
if (combined.Count < 2)
return; // not enough to compare
var dupes = combined.GroupBy(EventEggGroupKey);
foreach (var dupe in dupes)
{
@ -38,4 +47,21 @@ public sealed class DuplicateGiftChecker : IBulkAnalyzer
input.AddLine(first, second, $"Receipt of the same egg mystery gifts detected: {dupe.Key}", Encounter);
}
}
private static string EventEggGroupKey(CombinedReference z)
{
var enc = z.Analysis.EncounterMatch;
if (enc is not MysteryGift mg)
throw new Exception("Expected a mystery gift.");
return mg.CardTitle + $"{mg.Species}"; // differentiator for duplicate named event eggs -- species should be enough?
}
private static bool IsEventEgg(SlotCache c, LegalityAnalysis la)
{
if (la.Info.Generation < 3)
return false; // don't care
if (la.EncounterMatch is not MysteryGift { IsEgg: true })
return false; // only interested in ^
return !c.Entity.WasTradedEgg;
}
}

View file

@ -24,13 +24,14 @@ public sealed class EncounterGenerator8X : IEncounterGenerator
public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info) => pk.Version switch
{
GO => EncounterGeneratorGO.Instance.GetEncounters(pk, chain, info),
PLA => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
BD or SP => EncounterGenerator8b.Instance.GetEncounters(pk, chain, info),
PLA when pk is not PK8 => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
BD or SP when pk is not PK8 => EncounterGenerator8b.Instance.GetEncounters(pk, chain, info),
SW when pk.MetLocation == LocationsHOME.SWLA => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
SW when pk.MetLocation == LocationsHOME.SWBD => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, BD),
SH when pk.MetLocation == LocationsHOME.SHSP => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, SP),
SW when pk.MetLocation == LocationsHOME.SWSL => EncounterGenerator9.Instance.GetEncountersSWSH(pk, chain, SL),
SH when pk.MetLocation == LocationsHOME.SHVL => EncounterGenerator9.Instance.GetEncountersSWSH(pk, chain, VL),
_ => EncounterGenerator8.Instance.GetEncounters(pk, chain, info),
SW or SH => EncounterGenerator8.Instance.GetEncounters(pk, chain, info),
_ => [],
};
}

View file

@ -42,8 +42,6 @@ public record struct EncounterEnumerator8a(PKM Entity, EvoCriteria[] Chain) : IE
case YieldState.Start:
if (Chain.Length == 0)
break;
if (Entity is PK8 { SWSH: false })
break;
if (Entity.IsEgg)
break;

View file

@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace PKHeX.Core;
@ -57,7 +56,6 @@ public record struct EncounterEnumerator8b(PKM Entity, EvoCriteria[] Chain, Game
switch (State)
{
case YieldState.Start:
Debug.Assert(Entity is not PK8);
if (Chain.Length == 0)
break;

View file

@ -24,7 +24,7 @@ public static class EncounterVerifier
EncounterEgg e => VerifyEncounterEgg(pk, e.Generation),
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LG3EReader),
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LEncUnreleasedEMewJP),
EncounterStatic3 { Species: (int)Species.Deoxys } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased),
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased),
EncounterStatic4 { Roaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(LG4InvalidTileR45Surf),
MysteryGift g => VerifyEncounterEvent(pk, g),
{ IsEgg: true } when !pk.IsEgg => VerifyEncounterEgg(pk, enc.Generation),

View file

@ -26,7 +26,7 @@ public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture
// Trash Bytes
public override Span<byte> OriginalTrainerTrash => Data.AsSpan(0x18, 22);
public Span<byte> NicknameDisplay_Trash => Data.AsSpan(0x2E, 22);
public Span<byte> NicknameDisplayTrash => Data.AsSpan(0x2E, 22);
public override Span<byte> NicknameTrash => Data.AsSpan(0x44, 22);
public override int TrashCharCountTrainer => 11;
public override int TrashCharCountNickname => 11;
@ -48,12 +48,12 @@ public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture
public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x14)); set => WriteUInt16BigEndian(Data.AsSpan(0x14), value); }
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x16)); set => WriteUInt16BigEndian(Data.AsSpan(0x16), value); }
public override string OriginalTrainerName { get => GetString(OriginalTrainerTrash); set => SetString(OriginalTrainerTrash, value, 10, StringConverterOption.None); }
public string NicknameDisplay { get => GetString(NicknameDisplay_Trash); set => SetString(NicknameDisplay_Trash, value, 10, StringConverterOption.None); }
public string NicknameDisplay { get => GetString(NicknameDisplayTrash); set => SetString(NicknameDisplayTrash, value, 10, StringConverterOption.None); }
public override string Nickname { get => GetString(NicknameTrash); set { SetString(NicknameTrash, value, 10, StringConverterOption.None); ResetNicknameDisplay(); } }
public void ResetNicknameDisplay()
{
var current = NicknameDisplay_Trash;
var current = NicknameDisplayTrash;
NicknameTrash.CopyTo(current);
if (CurrentRegion == GCRegion.NTSC_J)
current[10..].Clear(); // clamp to 5 chars at most

View file

@ -26,7 +26,7 @@ public sealed class XK3 : G3PKM, IShadowCapture
// Trash Bytes
public override Span<byte> OriginalTrainerTrash => Data.AsSpan(0x38, 22);
public Span<byte> NicknameDisplay_Trash => Data.AsSpan(0x4E, 22);
public Span<byte> NicknameDisplayTrash => Data.AsSpan(0x4E, 22);
public override Span<byte> NicknameTrash => Data.AsSpan(0x64, 22);
public override int TrashCharCountTrainer => 11;
public override int TrashCharCountNickname => 11;
@ -90,12 +90,12 @@ public sealed class XK3 : G3PKM, IShadowCapture
public GCRegion OriginalRegion { get => (GCRegion)Data[0x36]; set => Data[0x36] = (byte)value; }
public override int Language { get => Core.Language.GetMainLangIDfromGC(Data[0x37]); set => Data[0x37] = Core.Language.GetGCLangIDfromMain((byte)value); }
public override string OriginalTrainerName { get => GetString(OriginalTrainerTrash); set => SetString(OriginalTrainerTrash, value, 10, StringConverterOption.None); }
public string NicknameDisplay { get => GetString(NicknameDisplay_Trash); set => SetString(NicknameDisplay_Trash, value, 10, StringConverterOption.None); }
public string NicknameDisplay { get => GetString(NicknameDisplayTrash); set => SetString(NicknameDisplayTrash, value, 10, StringConverterOption.None); }
public override string Nickname { get => GetString(NicknameTrash); set { SetString(NicknameTrash, value, 10, StringConverterOption.None); ResetNicknameDisplay(); } }
public void ResetNicknameDisplay()
{
var current = NicknameDisplay_Trash;
var current = NicknameDisplayTrash;
NicknameTrash.CopyTo(current);
if (CurrentRegion == GCRegion.NTSC_J)
current[10..].Clear(); // clamp to 5 chars at most

View file

@ -295,7 +295,7 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo
set => SetString(Data.AsSpan(Offsets.Rival, MaxStringLengthTrainer), value, MaxStringLengthTrainer, StringConverterOption.Clear50);
}
public Span<byte> Rival_Trash { get => Data.AsSpan(Offsets.Rival, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Rival)); } }
public Span<byte> RivalTrash { get => Data.AsSpan(Offsets.Rival, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Rival)); } }
public byte RivalStarter { get => Data[Offsets.Starter - 2]; set => Data[Offsets.Starter - 2] = value; }
public bool Yellow => Starter == 0x54; // Pikachu
@ -312,10 +312,10 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo
set => Data[Offsets.PikaFriendship] = value;
}
public int PikaBeachScore
public uint PikaBeachScore
{
get => BinaryCodedDecimal.ToInt32LE(Data.AsSpan(Offsets.PikaBeachScore, 2));
set => BinaryCodedDecimal.WriteBytesLE(Data.AsSpan(Offsets.PikaBeachScore, 2), Math.Min(9999, value));
get => BinaryCodedDecimal.ReadUInt32LittleEndian(Data.AsSpan(Offsets.PikaBeachScore, 2));
set => BinaryCodedDecimal.WriteUInt32LittleEndian(Data.AsSpan(Offsets.PikaBeachScore, 2), Math.Min(9999, value));
}
public override string PlayTimeString => !PlayedMaximum ? base.PlayTimeString : $"{base.PlayTimeString} {Checksums.CRC16_CCITT(Data):X4}";
@ -400,21 +400,21 @@ public sealed class SAV1 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo
public override uint Money
{
get => (uint)BinaryCodedDecimal.ToInt32BE(Data.AsSpan(Offsets.Money, 3));
get => BinaryCodedDecimal.ReadUInt32BigEndian(Data.AsSpan(Offsets.Money, 3));
set
{
value = (uint)Math.Min(value, MaxMoney);
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Money, 3), (int)value);
BinaryCodedDecimal.WriteUInt32BigEndian(Data.AsSpan(Offsets.Money, 3), value);
}
}
public uint Coin
{
get => (uint)BinaryCodedDecimal.ToInt32BE(Data.AsSpan(Offsets.Coin, 2));
get => BinaryCodedDecimal.ReadUInt32BigEndian(Data.AsSpan(Offsets.Coin, 2));
set
{
value = (ushort)Math.Min(value, MaxCoins);
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Coin, 2), (int)value);
BinaryCodedDecimal.WriteUInt32BigEndian(Data.AsSpan(Offsets.Coin, 2), value);
}
}

View file

@ -328,7 +328,7 @@ public sealed class SAV2 : SaveFile, ILangDeviantSave, IEventFlagArray, IEventWo
set => SetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthTrainer), value, 8, StringConverterOption.Clear50);
}
public Span<byte> Rival_Trash
public Span<byte> RivalTrash
{
get => Data.AsSpan(Offsets.Rival, StringLength);
set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Rival)); }

View file

@ -334,11 +334,11 @@ public abstract class SAV4 : SaveFile, IEventFlag37, IDaycareStorage, IDaycareRa
public string Rival
{
get => GetString(Rival_Trash);
set => SetString(Rival_Trash, value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
get => GetString(RivalTrash);
set => SetString(RivalTrash, value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
}
public abstract Span<byte> Rival_Trash { get; set; }
public abstract Span<byte> RivalTrash { get; set; }
public abstract int X2 { get; set; }
public abstract int Y2 { get; set; }

View file

@ -115,7 +115,7 @@ public sealed class SAV4DP : SAV4Sinnoh
public override int X { get => ReadUInt16LittleEndian(General[0x1240..]); set => WriteUInt16LittleEndian(General[0x1240..], (ushort)(X2 = value)); }
public override int Y { get => ReadUInt16LittleEndian(General[0x1244..]); set => WriteUInt16LittleEndian(General[0x1244..], (ushort)(Y2 = value)); }
public override Span<byte> Rival_Trash
public override Span<byte> RivalTrash
{
get => General.Slice(0x25A8, MaxStringLengthTrainer * 2);
set { if (value.Length == MaxStringLengthTrainer * 2) value.CopyTo(General[0x25A8..]); }

View file

@ -188,7 +188,7 @@ public sealed class SAV4HGSS : SAV4, IBoxDetailName, IBoxDetailWallpaper
public override int X { get => ReadUInt16LittleEndian(General[0x123C..]); set => WriteUInt16LittleEndian(General[0x123C..], (ushort)(X2 = value)); }
public override int Y { get => ReadUInt16LittleEndian(General[0x1240..]); set => WriteUInt16LittleEndian(General[0x1240..], (ushort)(Y2 = value)); }
public override Span<byte> Rival_Trash
public override Span<byte> RivalTrash
{
get => RivalSpan;
set { if (value.Length == MaxStringLengthTrainer * 2) value.CopyTo(RivalSpan); }

View file

@ -154,7 +154,7 @@ public sealed class SAV4Pt : SAV4Sinnoh
public override int X { get => ReadUInt16LittleEndian(General[0x1288..]); set => WriteUInt16LittleEndian(General[0x1288..], (ushort)(X2 = value)); }
public override int Y { get => ReadUInt16LittleEndian(General[0x128C..]); set => WriteUInt16LittleEndian(General[0x128C..], (ushort)(Y2 = value)); }
public override Span<byte> Rival_Trash
public override Span<byte> RivalTrash
{
get => RivalSpan;
set { if (value.Length == MaxStringLengthTrainer * 2) value.CopyTo(RivalSpan); }

View file

@ -57,8 +57,9 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlagProvider37, IBox
public override bool ChecksumsValid => AllBlocks.GetChecksumsValid(Data);
public override string ChecksumInfo => AllBlocks.GetChecksumInfo(Data);
protected int CGearInfoOffset;
protected int CGearDataOffset;
private int CGearInfoOffset => AllBlocks[32].Offset; // 0x1C000 - Options / Skin Info
protected abstract int CGearDataOffset { get; } // extdata
public sealed override bool HasPokeDex => true;
// Daycare
public int DaycareSlotCount => 2;

View file

@ -9,31 +9,14 @@ namespace PKHeX.Core;
/// <inheritdoc cref="SAV5" />
public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
{
public SAV5B2W2() : base(SaveUtil.SIZE_G5RAW)
{
Blocks = new SaveBlockAccessor5B2W2(this);
Initialize();
}
public SAV5B2W2(byte[] data) : base(data)
{
Blocks = new SaveBlockAccessor5B2W2(this);
Initialize();
}
public SAV5B2W2() : base(SaveUtil.SIZE_G5RAW) => Blocks = new SaveBlockAccessor5B2W2(this);
public SAV5B2W2(byte[] data) : base(data) => Blocks = new SaveBlockAccessor5B2W2(this);
public override PersonalTable5B2W2 Personal => PersonalTable.B2W2;
public SaveBlockAccessor5B2W2 Blocks { get; }
protected override SAV5B2W2 CloneInternal() => new((byte[]) Data.Clone());
public override int MaxItemID => Legal.MaxItemID_5_B2W2;
private void Initialize()
{
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52800;
}
public override bool HasPokeDex => true;
public override IReadOnlyList<BlockInfo> AllBlocks => Blocks.BlockInfo;
public override MyItem Items => Blocks.Items;
public override Zukan5 Zukan => Blocks.Zukan;
@ -65,11 +48,11 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
public string Rival
{
get => GetString(Rival_Trash);
set => SetString(Rival_Trash, value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
get => GetString(RivalTrash);
set => SetString(RivalTrash, value, MaxStringLengthTrainer, StringConverterOption.ClearZero);
}
public Span<byte> Rival_Trash
public Span<byte> RivalTrash
{
get => Data.AsSpan(0x23BA4, MaxStringLengthTrainer * 2);
set { if (value.Length == MaxStringLengthTrainer * 2) value.CopyTo(Data.AsSpan(0x23BA4)); }
@ -79,4 +62,5 @@ public sealed class SAV5B2W2 : SAV5, ISaveBlock5B2W2
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4DA00, BattleVideo5.SIZE_USED);
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4F400, BattleVideo5.SIZE_USED);
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50E00, BattleVideo5.SIZE_USED);
protected override int CGearDataOffset => 0x52800; // ^ + 0x1A00 spacing
}

View file

@ -9,31 +9,14 @@ namespace PKHeX.Core;
/// <inheritdoc cref="SAV5" />
public sealed class SAV5BW : SAV5
{
public SAV5BW() : base(SaveUtil.SIZE_G5RAW)
{
Blocks = new SaveBlockAccessor5BW(this);
Initialize();
}
public SAV5BW(byte[] data) : base(data)
{
Blocks = new SaveBlockAccessor5BW(this);
Initialize();
}
public SAV5BW() : base(SaveUtil.SIZE_G5RAW) => Blocks = new SaveBlockAccessor5BW(this);
public SAV5BW(byte[] data) : base(data) => Blocks = new SaveBlockAccessor5BW(this);
public override PersonalTable5BW Personal => PersonalTable.BW;
public SaveBlockAccessor5BW Blocks { get; }
protected override SAV5BW CloneInternal() => new((byte[])Data.Clone());
public override int MaxItemID => Legal.MaxItemID_5_BW;
public override bool HasPokeDex => true;
private void Initialize()
{
CGearInfoOffset = 0x1C000;
CGearDataOffset = 0x52000;
}
public override IReadOnlyList<BlockInfo> AllBlocks => Blocks.BlockInfo;
public override MyItem5BW Items => Blocks.Items;
public override Zukan5 Zukan => Blocks.Zukan;
@ -62,4 +45,5 @@ public sealed class SAV5BW : SAV5
public override Memory<byte> BattleVideoDownload1 => Data.AsMemory(0x4C000, BattleVideo5.SIZE_USED);
public override Memory<byte> BattleVideoDownload2 => Data.AsMemory(0x4E000, BattleVideo5.SIZE_USED);
public override Memory<byte> BattleVideoDownload3 => Data.AsMemory(0x50000, BattleVideo5.SIZE_USED);
protected override int CGearDataOffset => 0x52000; // ^ + 0x2000 spacing
}

View file

@ -15,6 +15,7 @@ public sealed class BattleVideo5(Memory<byte> Raw) : IBattleVideo
private const int SIZE_FOOTER = 0x14;
public const int SIZE_USED = SIZE + SIZE_FOOTER;
//public const int SIZE_BLOCK = 0x2000;
//bw is 0x2000 between each video, b2w2 is 0x1A00
private Span<byte> Data => Raw.Span[..SIZE_USED];

View file

@ -10,11 +10,11 @@ public sealed class RanchMii(byte[] Data)
public uint MiiId { get => ReadUInt32BigEndian(Data); set => WriteUInt32BigEndian(Data, value); }
public uint SystemId { get => ReadUInt32BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); }
public Span<byte> Name_Trash => Data.AsSpan(0x10, 0x18);
public Span<byte> MiiNameTrash => Data.AsSpan(0x10, 0x18);
public string MiiName
{
get => StringConverter4GC.GetStringUnicode(Name_Trash);
set => StringConverter4GC.SetStringUnicode(value, Name_Trash, value.Length, StringConverterOption.None);
get => StringConverter4GC.GetStringUnicode(MiiNameTrash);
set => StringConverter4GC.SetStringUnicode(value, MiiNameTrash, value.Length, StringConverterOption.None);
}
}

View file

@ -14,7 +14,7 @@ public sealed class RanchTrainerMii(byte[] Data)
public ushort TrainerId { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), value); }
public ushort SecretId { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), value); }
private Span<byte> Trainer_Trash => Data.AsSpan(0x10, 0x10);
private Span<byte> OriginalTrainerTrash => Data.AsSpan(0x10, 0x10);
// 0x20-23: ??
// 0x24: ??
@ -27,7 +27,7 @@ public sealed class RanchTrainerMii(byte[] Data)
public string TrainerName
{
get => StringConverter4.GetString(Trainer_Trash);
set => StringConverter4.SetString(Trainer_Trash, value, 7, Language);
get => StringConverter4.GetString(OriginalTrainerTrash);
set => StringConverter4.SetString(OriginalTrainerTrash, value, 7, Language);
}
}

View file

@ -39,7 +39,7 @@ public readonly ref struct HallFame6Entity
public bool IsNicknamed { get => Nick == 1; set => Nick = value ? 1u : 0u; }
public bool IsShiny { get => Shiny == 1; set => Shiny = value ? 1u : 0u; }
private Span<byte> Nick_Trash => Data.Slice(0x18, 24);
private Span<byte> NicknameTrash => Data.Slice(0x18, 24);
private Span<byte> OriginalTrainerTrash => Data.Slice(0x30, 24);
// Don't mimic in-game behavior of not clearing strings. First entry should always have clean trash.
@ -47,14 +47,14 @@ public readonly ref struct HallFame6Entity
public void ClearTrash()
{
Nick_Trash.Clear();
NicknameTrash.Clear();
OriginalTrainerTrash.Clear();
}
public string Nickname
{
get => StringConverter6.GetString(Nick_Trash);
set => StringConverter6.SetString(Nick_Trash, value, 12, Language, Option);
get => StringConverter6.GetString(NicknameTrash);
set => StringConverter6.SetString(NicknameTrash, value, 12, Language, Option);
}
public string OriginalTrainerName

View file

@ -11,11 +11,11 @@ public sealed class Misc7b(SAV7b sav, Memory<byte> raw) : SaveBlock<SAV7b>(sav,
set => WriteUInt32LittleEndian(Data[4..], value);
}
private Span<byte> Rival_Trash => Data.Slice(0x200, 0x1A);
private Span<byte> RivalTrash => Data.Slice(0x200, 0x1A);
public string Rival
{
get => SAV.GetString(Rival_Trash);
set => SAV.SetString(Rival_Trash, value, SAV.MaxStringLengthTrainer, StringConverterOption.ClearZero);
get => SAV.GetString(RivalTrash);
set => SAV.SetString(RivalTrash, value, SAV.MaxStringLengthTrainer, StringConverterOption.ClearZero);
}
}

View file

@ -37,9 +37,9 @@ public sealed class RentalTeam9(byte[] Data) : IRentalTeam<PK9>, IPokeGroup
set => WriteUInt16LittleEndian(Data.AsSpan(OFS_META + 0x00), value);
}
private Span<byte> Player_Trash => Data.AsSpan(OFS_META + 0x02, LEN_OT * sizeof(char));
private Span<byte> OriginalTrainerTrash => Data.AsSpan(OFS_META + 0x02, LEN_OT * sizeof(char));
private Span<byte> TeamName_Trash => Data.AsSpan(OFS_META + 0x18, LEN_TEAMNAME * sizeof(char));
private Span<byte> TeamNameTrash => Data.AsSpan(OFS_META + 0x18, LEN_TEAMNAME * sizeof(char));
public uint Language
{
@ -49,14 +49,14 @@ public sealed class RentalTeam9(byte[] Data) : IRentalTeam<PK9>, IPokeGroup
public string PlayerName
{
get => StringConverter8.GetString(Player_Trash);
set => StringConverter8.SetString(Player_Trash, value, 10);
get => StringConverter8.GetString(OriginalTrainerTrash);
set => StringConverter8.SetString(OriginalTrainerTrash, value, 10);
}
public string TeamName
{
get => StringConverter8.GetString(TeamName_Trash);
set => StringConverter8.SetString(TeamName_Trash, value, LEN_TEAMNAME);
get => StringConverter8.GetString(TeamNameTrash);
set => StringConverter8.SetString(TeamNameTrash, value, LEN_TEAMNAME);
}
public uint EntityCount

View file

@ -145,8 +145,7 @@ public static class SaveLanguage
// Check for underscores too; replace the input w/ spaces to underscore
Span<char> tmp = stackalloc char[value.Length];
value.CopyTo(tmp);
tmp.Replace(' ', '_');
value.Replace(tmp, ' ', '_');
return Contains(span, tmp);
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@ -8,34 +8,39 @@ namespace PKHeX.Core;
/// </summary>
public static class BinaryCodedDecimal
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PushDigits(ref uint result, uint b)
=> result = checked((result * 100) + (10 * (b >> 4)) + (b & 0xf));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetLowestTuple(uint value)
=> (byte)((((value / 10) % 10) << 4) | (value % 10));
/// <summary>
/// Returns a 32-bit signed integer converted from bytes in a Binary Coded Decimal format byte array.
/// </summary>
/// <param name="input">Input byte array to read from.</param>
public static int ToInt32BE(ReadOnlySpan<byte> input)
public static uint ReadUInt32BigEndian(ReadOnlySpan<byte> input)
{
int result = 0;
uint result = 0;
foreach (var b in input)
PushDigits(ref result, b);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void PushDigits(ref int result, byte b) => result = (result * 100) + (10 * (b >> 4)) + (b & 0xf);
/// <summary>
/// Writes the <see cref="value"/> to the <see cref="data"/> buffer.
/// </summary>
public static void WriteBytesBE(Span<byte> data, int value)
public static void WriteUInt32BigEndian(Span<byte> data, uint value)
{
for (int i = data.Length - 1; i >= 0; i--, value /= 100)
data[i] = (byte)((((value / 10) % 10) << 4) | (value % 10));
data[i] = GetLowestTuple(value);
}
/// <inheritdoc cref="ToInt32BE(ReadOnlySpan{byte})"/>
public static int ToInt32LE(ReadOnlySpan<byte> input)
/// <inheritdoc cref="ReadUInt32BigEndian"/>
public static uint ReadUInt32LittleEndian(ReadOnlySpan<byte> input)
{
int result = 0;
uint result = 0;
for (int i = input.Length - 1; i >= 0; i--)
PushDigits(ref result, input[i]);
return result;
@ -44,9 +49,9 @@ public static class BinaryCodedDecimal
/// <summary>
/// Writes the <see cref="value"/> to the <see cref="data"/> buffer.
/// </summary>
public static void WriteBytesLE(Span<byte> data, int value)
public static void WriteUInt32LittleEndian(Span<byte> data, uint value)
{
for (int i = 0; i < data.Length; i++, value /= 100)
data[i] = (byte)((((value / 10) % 10) << 4) | (value % 10));
data[i] = GetLowestTuple(value);
}
}

View file

@ -50,32 +50,32 @@ public class ConvertUtilTests
}
[Theory]
[InlineData(0x12345678, 12345678)]
public void CheckConvertBCD_Little(uint raw, int expect)
[InlineData(0x12345678, 12345678u)]
public void CheckConvertBCD_Little(uint raw, uint expect)
{
Span<byte> data = stackalloc byte[4];
WriteUInt32LittleEndian(data, raw);
var result = BinaryCodedDecimal.ToInt32LE(data);
var result = BinaryCodedDecimal.ReadUInt32LittleEndian(data);
result.Should().Be(expect);
Span<byte> newData = stackalloc byte[4];
BinaryCodedDecimal.WriteBytesLE(newData, result);
BinaryCodedDecimal.WriteUInt32LittleEndian(newData, result);
data.SequenceEqual(newData).Should().BeTrue();
}
[Theory]
[InlineData(0x78563412, 12345678)]
public void CheckConvertBCD_Big(uint raw, int expect)
[InlineData(0x12345678, 12345678u)]
public void CheckConvertBCD_Big(uint raw, uint expect)
{
Span<byte> data = stackalloc byte[4];
WriteUInt32LittleEndian(data, raw);
WriteUInt32BigEndian(data, raw);
var result = BinaryCodedDecimal.ToInt32BE(data);
var result = BinaryCodedDecimal.ReadUInt32BigEndian(data);
result.Should().Be(expect);
Span<byte> newData = stackalloc byte[4];
BinaryCodedDecimal.WriteBytesBE(newData, result);
BinaryCodedDecimal.WriteUInt32BigEndian(newData, result);
data.SequenceEqual(newData).Should().BeTrue();
}
}