mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Minor tweaks
Rival_Trash -> RivalTrash BinaryCodedDecimal better method names Gift dupe checker differentiate by species
This commit is contained in:
parent
992deea183
commit
4f23efd939
25 changed files with 120 additions and 123 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
_ => [],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)); }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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..]); }
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue