Use span for savefile slot API

No more offset fetch
This commit is contained in:
Kurt
2023-03-25 23:14:50 -07:00
parent 5ce3e734b8
commit 0087ccb44f
50 changed files with 317 additions and 292 deletions

View File

@@ -1,3 +1,5 @@
using System;
namespace PKHeX.Core;
/// <summary>
@@ -21,15 +23,17 @@ public sealed record SlotInfoMisc(byte[] Data, int Slot, int Offset, bool PartyF
public bool WriteTo(SaveFile sav, PKM pk, PKMImportSetting setting = PKMImportSetting.UseDefault)
{
var span = Data.AsSpan(Offset);
if (PartyFormat)
sav.SetSlotFormatParty(pk, Data, Offset, setting, setting);
sav.SetSlotFormatParty(pk, span, setting, setting);
else
sav.SetSlotFormatStored(pk, Data, Offset, setting, setting);
sav.SetSlotFormatStored(pk, span, setting, setting);
return true;
}
public PKM Read(SaveFile sav)
{
return PartyFormat ? sav.GetPartySlot(Data, Offset) : sav.GetStoredSlot(Data, Offset);
var span = Data.AsSpan(Offset);
return PartyFormat ? sav.GetPartySlot(span) : sav.GetStoredSlot(span);
}
}

View File

@@ -120,7 +120,7 @@ static T[] SetArray<T>(ICollection<T> arr)
// rather than use Linq to build an array, just do it the quick way directly.
var result = new T[arr.Count];
((IReadOnlySet<T>)arr).CopyTo(result);
arr.CopyTo(result, 0);
return result;
}

View File

@@ -107,7 +107,7 @@ public partial class MemoryContext6
/// <summary>
/// 24bits of flags allowing certain feelings for a given memory index.
/// </summary>
private static ReadOnlySpan<uint> MemoryFeelings => new uint[]
private static ReadOnlySpan<int> MemoryFeelings => new[]
{
0x000000, 0x04CBFD, 0x004BFD, 0x04CBFD, 0x04CBFD, 0xFFFBFB, 0x84FFF9, 0x47FFFF, 0xBF7FFA, 0x7660B0,
0x80BDF9, 0x88FB7A, 0x083F79, 0x0001FE, 0xCFEFFF, 0x84EBAF, 0xB368B0, 0x091F7E, 0x0320A0, 0x080DDD,

View File

@@ -291,7 +291,7 @@ public partial class MemoryContext8
/// 24bits of flags allowing certain feelings for a given memory index.
/// </summary>
/// <remarks>Beware, there was an off-by-one error in the game that made Feeling 0 unobtainable, and thus the Happy feeling bit (rightmost) is omitted.</remarks>
private static ReadOnlySpan<uint> MemoryFeelings => new uint[]
private static ReadOnlySpan<int> MemoryFeelings => new[]
{
0x000000, 0x04CBFD, 0x004BFD, 0x04CBFD, 0x04CBFD, 0xFFFBFB, 0x84FFF9, 0x47FFFF, 0xBF7FFA, 0x7660B0,
0x80BDF9, 0x88FB7A, 0x083F79, 0x0001FE, 0xCFEFFF, 0x84EBAF, 0xB368B0, 0x091F7E, 0x0320A0, 0x080DDD,

View File

@@ -43,7 +43,7 @@ protected GBPKML(byte[] data, bool jp = false) : base(data)
RawNickname.AsSpan().Fill(StringConverter12.G1TerminatorCode);
}
public override void SetNotNicknamed(int language) => GetNonNickname(language).CopyTo(RawNickname);
public override void SetNotNicknamed(int language) => GetNonNickname(language).AsSpan().CopyTo(RawNickname);
protected override byte[] GetNonNickname(int language)
{

View File

@@ -77,7 +77,7 @@ private void Initialize(GameVersion versionOverride)
UnpackBox(Offsets.CurrentBox, baseDest, stored, CurrentBox, capacity);
}
var party = GetData(Offsets.Party, SIZE_STOREDPARTY);
var party = Data.AsSpan(Offsets.Party, SIZE_STOREDPARTY).ToArray();
var partyPL = new PokeList1(party, PokeListType.Party, Japanese);
for (int i = 0; i < partyPL.Pokemon.Length; i++)
{
@@ -107,7 +107,7 @@ private void Initialize(GameVersion versionOverride)
private void UnpackBox(int srcOfs, int destOfs, int boxSize, int boxIndex, PokeListType boxCapacity)
{
var boxData = GetData(srcOfs, boxSize);
var boxData = Data.AsSpan(srcOfs, boxSize).ToArray();
var boxDest = destOfs + (boxIndex * SIZE_BOX);
var boxPL = new PokeList1(boxData, boxCapacity, Japanese);
for (int i = 0; i < boxPL.Pokemon.Length; i++)
@@ -137,11 +137,11 @@ private void PackBox(int boxDest, int boxIndex, PokeListType boxCapacity)
// copy to box location
var boxData = boxPL.Write();
int boxSrc = GetBoxRawDataOffset(boxIndex);
SetData(Data, boxData, boxSrc);
SetData(Data.AsSpan(boxSrc), boxData);
// copy to active loc if current box
if (boxIndex == CurrentBox)
SetData(Data, boxData, Offsets.CurrentBox);
SetData(Data.AsSpan(Offsets.CurrentBox), boxData);
}
private const int SIZE_RESERVED = 0x8000; // unpacked box data
@@ -176,7 +176,9 @@ protected override byte[] GetFinalData()
int pSlot = 0;
for (int i = 0; i < 6; i++)
{
PK1 partyPK = GetPKM(GetData(GetPartyOffset(i), SIZE_STORED));
var ofs = GetPartyOffset(i);
var data = Data.AsSpan(ofs, SIZE_STORED).ToArray();
PK1 partyPK = GetPKM(data);
if (partyPK.Species > 0)
partyPL[pSlot++] = partyPK;
}
@@ -257,7 +259,7 @@ private byte GetRBYChecksum(int start, int end)
public override string OT
{
get => GetString(Offsets.OT, MaxStringLengthOT);
get => GetString(Data.AsSpan(Offsets.OT, MaxStringLengthOT));
set => SetString(Data.AsSpan(Offsets.OT, MaxStringLengthOT + 1), value, MaxStringLengthOT, StringConverterOption.ClearZero);
}
@@ -285,7 +287,7 @@ public override ushort TID16
public string Rival
{
get => GetString(Offsets.Rival, MaxStringLengthOT);
get => GetString(Data.AsSpan(Offsets.Rival, MaxStringLengthOT));
set => SetString(Data.AsSpan(Offsets.Rival, MaxStringLengthOT), value, MaxStringLengthOT, StringConverterOption.Clear50);
}
@@ -542,20 +544,20 @@ private void SetDexFlag(int region, ushort species, bool value)
SetFlag(region + ofs, bit & 7, value);
}
public override void WriteSlotFormatStored(PKM pk, Span<byte> data, int offset)
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteSlotFormatStored(pk, Data, offset);
base.WriteSlotFormatStored(pk, data);
}
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
public override void WriteBoxSlot(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteBoxSlot(pk, Data, offset);
base.WriteBoxSlot(pk, data);
}
private const int SpawnFlagCount = 0xF0;

View File

@@ -204,7 +204,7 @@ public string GetTeamName(int team)
var ofs = GetTeamOffset(team);
var otOfs = ofs + (Japanese ? 2 : 1);
var str = GetString(otOfs, Japanese ? 5 : 7);
var str = GetString(Data.AsSpan(otOfs, Japanese ? 5 : 7));
if (string.IsNullOrWhiteSpace(str))
return name;
var idOfs = ofs + (Japanese ? 0x8 : 0xC);
@@ -249,25 +249,25 @@ public override SlotGroup GetTeam(int team)
for (int i = 0; i < 6; i++)
{
var rel = ofs + ListHeaderSize + (i * SIZE_STORED);
members[i] = (PK1)GetStoredSlot(Data, rel);
members[i] = (PK1)GetStoredSlot(Data.AsSpan(rel));
}
return new SlotGroup(name, members);
}
public override void WriteSlotFormatStored(PKM pk, Span<byte> data, int offset)
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteSlotFormatStored(pk, Data, offset);
base.WriteSlotFormatStored(pk, data);
}
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
public override void WriteBoxSlot(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteBoxSlot(pk, Data, offset);
base.WriteBoxSlot(pk, data);
}
public static bool IsStadium(ReadOnlySpan<byte> data)

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@@ -118,7 +117,7 @@ public string GetTeamName(int team)
var name = $"Team {team + 1}";
var ofs = GetTeamOffset(team);
var str = GetString(ofs + 2, 5);
var str = GetString(Data.AsSpan(ofs + 2, 5));
if (string.IsNullOrWhiteSpace(str))
return name;
var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 8));
@@ -136,25 +135,25 @@ public override SlotGroup GetTeam(int team)
for (int i = 0; i < 6; i++)
{
var rel = ofs + ListHeaderSize + (i * SIZE_STORED);
members[i] = (PK1)GetStoredSlot(Data, rel);
members[i] = (PK1)GetStoredSlot(Data.AsSpan(rel));
}
return new SlotGroup(name, members);
}
public override void WriteSlotFormatStored(PKM pk, Span<byte> data, int offset)
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteSlotFormatStored(pk, Data, offset);
base.WriteSlotFormatStored(pk, data);
}
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
public override void WriteBoxSlot(PKM pk, Span<byte> data)
{
// pk that have never been boxed have yet to save the 'current level' for box indication
// set this value at this time
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
base.WriteBoxSlot(pk, Data, offset);
base.WriteBoxSlot(pk, data);
}
public static bool IsStadium(ReadOnlySpan<byte> data)

View File

@@ -74,7 +74,7 @@ private void Initialize()
for (int i = 0; i < BoxCount; i++)
{
int ofs = GetBoxRawDataOffset(i, splitAtIndex);
var box = GetData(ofs, stored);
var box = Data.AsSpan(ofs, stored).ToArray();
var boxDest = baseDest + (i * SIZE_BOX);
var boxPL = new PokeList2(box, capacity, Japanese);
for (int j = 0; j < boxPL.Pokemon.Length; j++)
@@ -87,7 +87,7 @@ private void Initialize()
}
}
var current = GetData(Offsets.CurrentBox, stored);
var current = Data.AsSpan(Offsets.CurrentBox, stored).ToArray();
var curBoxPL = new PokeList2(current, capacity, Japanese);
var curDest = baseDest + (CurrentBox * SIZE_BOX);
for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
@@ -99,7 +99,7 @@ private void Initialize()
pkDat.CopyTo(Data, dest);
}
var party = GetData(Offsets.Party, SIZE_STOREDPARTY);
var party = Data.AsSpan(Offsets.Party, SIZE_STOREDPARTY).ToArray();
var partyPL = new PokeList2(party, PokeListType.Party, Japanese);
for (int i = 0; i < partyPL.Pokemon.Length; i++)
{
@@ -180,7 +180,9 @@ protected override byte[] GetFinalData()
int slot = 0;
for (int j = 0; j < boxPL.Pokemon.Length; j++)
{
PK2 boxPK = GetPKM(GetData(GetBoxOffset(i) + (j * SIZE_STORED), SIZE_STORED));
var ofs = GetBoxOffset(i) + (j * SIZE_STORED);
var data = Data.AsSpan(ofs, SIZE_STORED).ToArray();
PK2 boxPK = GetPKM(data);
if (boxPK.Species > 0)
boxPL[slot++] = boxPK;
}
@@ -195,7 +197,9 @@ protected override byte[] GetFinalData()
int pSlot = 0;
for (int i = 0; i < 6; i++)
{
PK2 partyPK = GetPKM(GetData(GetPartyOffset(i), SIZE_STORED));
var ofs = GetPartyOffset(i);
var data = Data.AsSpan(ofs, SIZE_STORED).ToArray();
PK2 partyPK = GetPKM(data);
if (partyPK.Species > 0)
partyPL[pSlot++] = partyPK;
}
@@ -321,7 +325,7 @@ public override bool ChecksumsValid
public override string OT
{
get => GetString(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT);
get => GetString(Data.AsSpan(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT));
set => SetString(Data.AsSpan(Offsets.Trainer1 + 2, (Korean ? 2 : 1) * MaxStringLengthOT), value, 8, StringConverterOption.Clear50);
}
@@ -333,7 +337,7 @@ public Span<byte> OT_Trash
public string Rival
{
get => GetString(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT);
get => GetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT));
set => SetString(Data.AsSpan(Offsets.Rival, (Korean ? 2 : 1) * MaxStringLengthOT), value, 8, StringConverterOption.Clear50);
}
@@ -557,7 +561,7 @@ public bool CurrentBoxChanged
public override string GetBoxName(int box)
{
int len = Korean ? 17 : 9;
return GetString(Offsets.BoxNames + (box * len), len);
return GetString(Data.AsSpan(Offsets.BoxNames + (box * len), len));
}
public override void SetBoxName(int box, ReadOnlySpan<char> value)

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@@ -137,7 +136,7 @@ public string GetTeamName(int team)
var name = $"{((Stadium2TeamType) (team / TeamCountType)).ToString().Replace('_', ' ')} {(team % 10) + 1}";
var ofs = GetTeamOffset(team);
var str = GetString(ofs + 4, 7);
var str = GetString(Data.AsSpan(ofs + 4, 7));
if (string.IsNullOrWhiteSpace(str))
return name;
var id = ReadUInt16BigEndian(Data.AsSpan(ofs + 2));
@@ -147,7 +146,8 @@ public string GetTeamName(int team)
public override string GetBoxName(int box)
{
var ofs = GetBoxOffset(box) - 0x10;
var str = GetString(ofs, 0x10);
var boxNameSpan = Data.AsSpan(ofs, 0x10);
var str = GetString(boxNameSpan);
if (string.IsNullOrWhiteSpace(str))
return $"Box {box + 1}";
return str;
@@ -164,7 +164,7 @@ public override SlotGroup GetTeam(int team)
for (int i = 0; i < 6; i++)
{
var rel = ofs + ListHeaderSizeTeam + (i * SIZE_STORED);
members[i] = (SK2)GetStoredSlot(Data, rel);
members[i] = (SK2)GetStoredSlot(Data.AsSpan(rel));
}
return new SlotGroup(name, members);
}

View File

@@ -197,8 +197,8 @@ public void WriteBothSaveSlots(Span<byte> data)
protected sealed override PK3 GetPKM(byte[] data) => new(data);
protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data);
protected sealed override byte[] BoxBuffer => Storage;
protected sealed override byte[] PartyBuffer => Large;
protected sealed override Span<byte> BoxBuffer => Storage;
protected sealed override Span<byte> PartyBuffer => Large;
private const int COUNT_BOX = 14;
private const int COUNT_SLOTSPERBOX = 30;
@@ -633,9 +633,9 @@ public override void CopyChangesFrom(SaveFile sav)
{
SetData(sav.Data, 0);
var s3 = (SAV3)sav;
SetData(Small, s3.Small, 0);
SetData(Large, s3.Large, 0);
SetData(Storage, s3.Storage, 0);
SetData(Small, s3.Small);
SetData(Large, s3.Large);
SetData(Storage, s3.Storage);
}
#region External Connections

View File

@@ -65,7 +65,9 @@ private StrategyMemo Initialize()
// Count up how many party slots are active.
for (int i = 0; i < 6; i++)
{
if (GetPartySlot(Data, GetPartyOffset(i)).Species != 0)
var ofs = GetPartyOffset(i);
var span = Data.AsSpan(ofs);
if (ReadUInt16BigEndian(span) != 0) // species is at offset 0x00
PartyCount++;
}
@@ -278,14 +280,16 @@ public override int GetBoxOffset(int box)
return Box + (((30 * SIZE_STORED) + 0x14)*box) + 0x14;
}
private Span<byte> GetBoxNameSpan(int box) => Data.AsSpan(Box + (0x24A4 * box), 16);
public override string GetBoxName(int box)
{
return GetString(Box + (0x24A4 * box), 16);
return GetString(GetBoxNameSpan(box));
}
public override void SetBoxName(int box, ReadOnlySpan<char> value)
{
SetString(Data.AsSpan(Box + (0x24A4 * box), 16), value, 8, StringConverterOption.ClearZero);
SetString(GetBoxNameSpan(box), value, 8, StringConverterOption.ClearZero);
}
protected override CK3 GetPKM(byte[] data)
@@ -363,8 +367,8 @@ public override int PlayedSeconds
}
// Trainer Info (offset 0x78, length 0xB18, end @ 0xB90)
public override string OT { get => GetString(0x78, 20); set { SetString(Data.AsSpan(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } }
public string OT2 { get => GetString(0x8C, 20); set => SetString(Data.AsSpan(0x8C, 20), value, 10, StringConverterOption.ClearZero); }
public override string OT { get => GetString(Data.AsSpan(0x78, 20)); set { SetString(Data.AsSpan(0x78, 20), value, 10, StringConverterOption.ClearZero); OT2 = value; } }
public string OT2 { get => GetString(Data.AsSpan(0x8C, 20)); set => SetString(Data.AsSpan(0x8C, 20), value, 10, StringConverterOption.ClearZero); }
public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(0xA4)); set => WriteUInt32BigEndian(Data.AsSpan(0xA4), value); }
public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0xA4)); set => WriteUInt16BigEndian(Data.AsSpan(0xA4), value); }
@@ -373,7 +377,7 @@ public override int PlayedSeconds
public override int Gender { get => Data[0xAF8]; set => Data[0xAF8] = (byte)value; }
public override uint Money { get => ReadUInt32BigEndian(Data.AsSpan(0xAFC)); set => WriteUInt32BigEndian(Data.AsSpan(0xAFC), value); }
public uint Coupons { get => ReadUInt32BigEndian(Data.AsSpan(0xB00)); set => WriteUInt32BigEndian(Data.AsSpan(0xB00), value); }
public string RUI_Name { get => GetString(0xB3A, 20); set => SetString(Data.AsSpan(0xB3A, 20), value, 10, StringConverterOption.ClearZero); }
public string RUI_Name { get => GetString(Data.AsSpan(0xB3A, 20)); set => SetString(Data.AsSpan(0xB3A, 20), value, 10, StringConverterOption.ClearZero); }
public override IReadOnlyList<InventoryPouch> Inventory
{

View File

@@ -61,14 +61,14 @@ public override uint SecurityKey
public RTC3 ClockInitial
{
get => new(GetData(Small, 0x98, RTC3.Size));
set => SetData(Small, value.Data, 0x98);
get => new(Small.AsSpan(0x98, RTC3.Size).ToArray());
set => SetData(Small.AsSpan(0x98), value.Data);
}
public RTC3 ClockElapsed
{
get => new(GetData(Small, 0xA0, RTC3.Size));
set => SetData(Small, value.Data, 0xA0);
get => new(Small.AsSpan(0xA0, RTC3.Size).ToArray());
set => SetData(Small.AsSpan(0xA0), value.Data);
}
public ushort JoyfulJumpInRow { get => ReadUInt16LittleEndian(Small.AsSpan(0x1FC)); set => WriteUInt16LittleEndian(Small.AsSpan(0x1FC), Math.Min((ushort)9999, value)); }
@@ -146,7 +146,7 @@ protected override InventoryPouch3[] GetItems()
public PokeBlock3Case PokeBlocks
{
get => new(Large, 0x848);
set => SetData(Large, value.Write(), 0x848);
set => SetData(Large.AsSpan(0x848), value.Write());
}
protected override int SeenOffset2 => 0x988;
@@ -155,8 +155,8 @@ public PokeBlock3Case PokeBlocks
public Swarm3 Swarm
{
get => new(Large.Slice(0x2B90, Swarm3.SIZE));
set => SetData(Large, value.Data, 0x2B90);
get => new(Large.AsSpan(0x2B90, Swarm3.SIZE).ToArray());
set => SetData(Large.AsSpan(0x2B90), value.Data);
}
private void ClearSwarm() => Large.AsSpan(0x2B90, Swarm3.SIZE).Clear();
@@ -205,9 +205,9 @@ public int SwarmIndex
private int WonderCardOffset => WonderNewsOffset + (Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE);
private int WonderCardExtraOffset => WonderCardOffset + (Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE);
public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large, value.Data, WonderOffset); }
public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large, value.Data, WonderCardOffset); }
public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large, value.Data, WonderCardExtraOffset); }
public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large.AsSpan(WonderOffset), value.Data); }
public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large.AsSpan(WonderCardOffset), value.Data); }
public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large.AsSpan(WonderCardExtraOffset), value.Data); }
// 0x338: 4 easy chat words
// 0x340: news MENewsJisanStruct
// 0x344: uint[5], uint[5] tracking?
@@ -215,10 +215,10 @@ public int SwarmIndex
public override Gen3MysteryData MysteryData
{
get => new MysteryEvent3(Large.Slice(0x3728, MysteryEvent3.SIZE));
set => SetData(Large, value.Data, 0x3728);
set => SetData(Large.AsSpan(0x3728), value.Data);
}
public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3B14, RecordMixing3Gift.SIZE)); set => SetData(Large, value.Data, 0x3B14); }
public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3B14, RecordMixing3Gift.SIZE)); set => SetData(Large.AsSpan(0x3B14), value.Data); }
protected override int SeenOffset3 => 0x3B24;
@@ -237,6 +237,6 @@ public override Gen3MysteryData MysteryData
public BV3 BattleVideo
{
get => !HasBattleVideo ? new BV3() : new BV3(Data.Slice(OFS_BV + 4, BV3.SIZE));
set => SetData(Data, value.Data, OFS_BV + 4);
set => SetData(Data.AsSpan(OFS_BV + 4, BV3.SIZE), value.Data);
}
}

View File

@@ -162,9 +162,9 @@ protected override InventoryPouch3[] GetItems()
private int WonderCardOffset => WonderNewsOffset + (Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE);
private int WonderCardExtraOffset => WonderCardOffset + (Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE);
public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large, value.Data, WonderOffset); }
public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large, value.Data, WonderCardOffset); }
public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large, value.Data, WonderCardExtraOffset); }
public WonderNews3 WonderNews { get => new(Large.Slice(WonderNewsOffset, Japanese ? WonderNews3.SIZE_JAP : WonderNews3.SIZE)); set => SetData(Large.AsSpan(WonderOffset), value.Data); }
public WonderCard3 WonderCard { get => new(Large.Slice(WonderCardOffset, Japanese ? WonderCard3.SIZE_JAP : WonderCard3.SIZE)); set => SetData(Large.AsSpan(WonderCardOffset), value.Data); }
public WonderCard3Extra WonderCardExtra { get => new(Large.Slice(WonderCardExtraOffset, WonderCard3Extra.SIZE)); set => SetData(Large.AsSpan(WonderCardExtraOffset), value.Data); }
// 0x338: 4 easy chat words
// 0x340: news MENewsJisanStruct
// 0x344: uint[5], uint[5] tracking?
@@ -172,7 +172,7 @@ protected override InventoryPouch3[] GetItems()
public override Gen3MysteryData MysteryData
{
get => new MysteryEvent3(Large.Slice(0x361C, MysteryEvent3.SIZE));
set => SetData(Large, value.Data, 0x361C);
set => SetData(Large.AsSpan(0x361C), value.Data);
}
protected override int SeenOffset3 => 0x3A18;

View File

@@ -57,14 +57,14 @@ public override bool NationalDex
public RTC3 ClockInitial
{
get => new(GetData(Small, 0x98, RTC3.Size));
set => SetData(Small, value.Data, 0x98);
get => new(Small.AsSpan(0x98, RTC3.Size).ToArray());
set => SetData(Small.AsSpan(0x98), value.Data);
}
public RTC3 ClockElapsed
{
get => new(GetData(Small, 0xA0, RTC3.Size));
set => SetData(Small, value.Data, 0xA0);
get => new(Small.AsSpan(0xA0, RTC3.Size).ToArray());
set => SetData(Small.AsSpan(0xA0), value.Data);
}
#endregion
@@ -109,7 +109,7 @@ protected override InventoryPouch3[] GetItems()
public PokeBlock3Case PokeBlocks
{
get => new(Large, 0x7F8);
set => SetData(Large, value.Write(), 0x7F8);
set => SetData(Large.AsSpan(0x7F8), value.Write());
}
protected override int SeenOffset2 => 0x938;
@@ -119,7 +119,7 @@ public PokeBlock3Case PokeBlocks
public Swarm3 Swarm
{
get => new(Large.Slice(0x2AFC, Swarm3.SIZE));
set => SetData(Large, value.Data, 0x2AFC);
set => SetData(Large.AsSpan(0x2AFC), value.Data);
}
private void ClearSwarm() => Large.AsSpan(0x2AFC, Swarm3.SIZE).Clear();
@@ -166,10 +166,10 @@ public int SwarmIndex
public override Gen3MysteryData MysteryData
{
get => new MysteryEvent3RS(Large.Slice(0x3690, MysteryEvent3.SIZE));
set => SetData(Large, value.Data, 0x3690);
set => SetData(Large.AsSpan(0x3690), value.Data);
}
public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3A7C, RecordMixing3Gift.SIZE)); set => SetData(Large, value.Data, 0x3A7C); }
public RecordMixing3Gift RecordMixingGift { get => new(Large.Slice(0x3A7C, RecordMixing3Gift.SIZE)); set => SetData(Large.AsSpan(0x3A7C), value.Data); }
protected override int SeenOffset3 => 0x3A8C;
#endregion

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Buffers.Binary.BinaryPrimitives;
@@ -52,7 +51,7 @@ private void InitializeData()
Array.Copy(Data, b.Offset + 0xC, Data, (int) (Box + (b.ID * copySize)), copySize);
}
private static BlockInfoRSBOX[] ReadBlocks(byte[] data)
private static BlockInfoRSBOX[] ReadBlocks(ReadOnlySpan<byte> data)
{
var blocks = new BlockInfoRSBOX[2 * BLOCK_COUNT];
for (int i = 0; i < blocks.Length; i++)
@@ -91,7 +90,7 @@ private byte[] GetInnerData()
SetChecksums();
return GetData(0, Data.Length - SIZE_RESERVED);
return Data[..^SIZE_RESERVED];
}
// Configuration
@@ -140,11 +139,16 @@ public override int CurrentBox
set => Data[Box + 4] = (byte)(value / 2);
}
private Span<byte> GetBoxNameSpan(int box)
{
int offset = Box + 0x1EC38 + (9 * box);
return Data.AsSpan(offset, 9);
}
protected override int GetBoxWallpaperOffset(int box)
{
// Box Wallpaper is directly after the Box Names
int offset = Box + 0x1ED19 + (box / 2);
return offset;
return Box + 0x1ED19 + (box / 2);
}
public override string GetBoxName(int box)
@@ -155,18 +159,18 @@ public override string GetBoxName(int box)
string boxName = $"[{lo:00}-{hi:00}] ";
box /= 2;
int offset = Box + 0x1EC38 + (9 * box);
if (Data[offset] is 0 or 0xFF)
var span = GetBoxNameSpan(box);
if (span[0] is 0 or 0xFF)
boxName += $"BOX {box + 1}";
boxName += GetString(offset, 9);
else
boxName += GetString(span);
return boxName;
}
public override void SetBoxName(int box, ReadOnlySpan<char> value)
{
int offset = Box + 0x1EC38 + (9 * box);
var span = Data.AsSpan(offset, 9);
var span = GetBoxNameSpan(box);
if (value == $"BOX {box + 1}")
{
span.Clear();
@@ -191,9 +195,9 @@ protected override byte[] DecryptPKM(byte[] data)
protected override void SetDex(PKM pk) { /* No Pokedex for this game, do nothing */ }
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
public override void WriteBoxSlot(PKM pk, Span<byte> data)
{
base.WriteBoxSlot(pk, data, offset);
base.WriteBoxSlot(pk, data);
WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED)..], pk.TID16);
WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED + 2)..], pk.SID16);
}

View File

@@ -131,7 +131,9 @@ private void Initialize()
// Count up how many party slots are active.
for (int i = 0; i < 6; i++)
{
if (GetPartySlot(Data, GetPartyOffset(i)).Species != 0)
var ofs = GetPartyOffset(i);
var span = Data.AsSpan(ofs);
if (ReadUInt16BigEndian(span) != 0) // species is at offset 0x00
PartyCount++;
}
}
@@ -320,7 +322,7 @@ public override int PlayedSeconds
// Trainer Info
public override GameVersion Version { get => GameVersion.XD; protected set { } }
public override string OT { get => GetString(Trainer1 + 0x00, 20); set => SetString(Data.AsSpan(Trainer1 + 0x00, 20), value, 10, StringConverterOption.ClearZero); }
public override string OT { get => GetString(Data.AsSpan(Trainer1 + 0x00, 20)); set => SetString(Data.AsSpan(Trainer1 + 0x00, 20), value, 10, StringConverterOption.ClearZero); }
public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(Trainer1 + 0x2C)); set => WriteUInt32BigEndian(Data.AsSpan(Trainer1 + 0x2C), value); }
public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2C), value); }
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(Trainer1 + 0x2E), value); }
@@ -333,7 +335,7 @@ public override int PlayedSeconds
public override int GetPartyOffset(int slot) => Party + (SIZE_STORED * slot);
private int GetBoxInfoOffset(int box) => Box + (((30 * SIZE_STORED) + 0x14) * box);
public override int GetBoxOffset(int box) => GetBoxInfoOffset(box) + 20;
public override string GetBoxName(int box) => GetString(GetBoxInfoOffset(box), 16);
public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxInfoOffset(box), 16));
public override void SetBoxName(int box, ReadOnlySpan<char> value)
{
@@ -348,12 +350,12 @@ protected override XK3 GetPKM(byte[] data)
}
protected override byte[] DecryptPKM(byte[] data) => data;
public override XK3 GetPartySlot(byte[] data, int offset) => GetStoredSlot(data, offset);
public override XK3 GetPartySlot(ReadOnlySpan<byte> data) => GetStoredSlot(data);
public override XK3 GetStoredSlot(byte[] data, int offset)
public override XK3 GetStoredSlot(ReadOnlySpan<byte> data)
{
// Get Shadow Data
var pk = (XK3)base.GetStoredSlot(data, offset);
var pk = (XK3)base.GetStoredSlot(data);
if (pk.ShadowID > 0 && pk.ShadowID < ShadowInfo.Count)
pk.Purification = ShadowInfo[pk.ShadowID].Purification;
return pk;

View File

@@ -23,8 +23,8 @@ public abstract class SAV4 : SaveFile, IEventFlag37
// SaveData is chunked into two pieces.
protected readonly byte[] Storage;
public readonly byte[] General;
protected sealed override byte[] BoxBuffer => Storage;
protected sealed override byte[] PartyBuffer => General;
protected sealed override Span<byte> BoxBuffer => Storage;
protected sealed override Span<byte> PartyBuffer => General;
protected abstract int StorageStart { get; }
public abstract Zukan4 Dex { get; }
@@ -48,16 +48,16 @@ protected SAV4(byte[] data, int gSize, int sSize, int sStart) : base(data)
var gbo = (GeneralBlockPosition == 0 ? 0 : PartitionSize);
var sbo = (StorageBlockPosition == 0 ? 0 : PartitionSize) + sStart;
General = GetData(gbo, gSize);
Storage = GetData(sbo, sSize);
General = Data.AsSpan(gbo, gSize).ToArray();
Storage = Data.AsSpan(sbo, sSize).ToArray();
}
// Configuration
protected sealed override SAV4 CloneInternal()
{
var sav = CloneInternal4();
SetData(sav.General, General, 0);
SetData(sav.Storage, Storage, 0);
SetData(sav.General, General);
SetData(sav.Storage, Storage);
return sav;
}
@@ -67,8 +67,8 @@ public sealed override void CopyChangesFrom(SaveFile sav)
{
SetData(sav.Data, 0);
var s4 = (SAV4)sav;
SetData(General, s4.General, 0);
SetData(Storage, s4.Storage, 0);
SetData(General, s4.General);
SetData(Storage, s4.Storage);
}
protected sealed override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
@@ -432,12 +432,12 @@ protected sealed override DataMysteryGift[] MysteryGiftCards
for (int i = 0; i < 8; i++) // 8 PGT
{
if (value[i] is PGT)
SetData(General, value[i].Data, WondercardData + (i *PGT.Size));
SetData(General.AsSpan(WondercardData + (i * PGT.Size)), value[i].Data);
}
for (int i = 8; i < 11; i++) // 3 PCD
{
if (value[i] is PCD)
SetData(General, value[i].Data, WondercardData + (8 *PGT.Size) + ((i - 8)*PCD.Size));
SetData(General.AsSpan(WondercardData + (8 * PGT.Size) + ((i - 8) * PCD.Size)), value[i].Data);
}
if (this is SAV4HGSS hgss && value.Length >= 11 && value[^1] is PCD capsule)
hgss.LockCapsuleSlot = capsule;
@@ -531,7 +531,7 @@ public void SetEventFlag(int flagNumber, bool value)
private const byte SealMaxCount = 99;
public byte[] GetSealCase() => General.Slice(Seal, (int)Seal4.MAX);
public void SetSealCase(byte[] value) => SetData(General, value, Seal);
public void SetSealCase(ReadOnlySpan<byte> value) => SetData(General.AsSpan(Seal), value);
public byte GetSealCount(Seal4 id) => General[Seal + (int)id];
public byte SetSealCount(Seal4 id, byte count) => General[Seal + (int)id] = Math.Min(SealMaxCount, count);

View File

@@ -200,16 +200,21 @@ public override ushort SID16
private int BoxName = -1;
private const int BoxNameLength = 0x28;
private Span<byte> GetBoxNameSpan(int box)
{
int ofs = BoxName + (box * BoxNameLength);
return Data.AsSpan(ofs, BoxNameLength);
}
public override string GetBoxName(int box)
{
if (BoxName < 0)
return $"BOX {box + 1}";
int ofs = BoxName + (box * BoxNameLength);
var span = Data.AsSpan(ofs, BoxNameLength);
var span = GetBoxNameSpan(box);
if (ReadUInt16BigEndian(span) == 0)
return $"BOX {box + 1}";
return GetString(ofs, BoxNameLength);
return GetString(span);
}
public override void SetBoxName(int box, ReadOnlySpan<char> value)
@@ -217,8 +222,7 @@ public override void SetBoxName(int box, ReadOnlySpan<char> value)
if (BoxName < 0)
return;
int ofs = BoxName + (box * BoxNameLength);
var span = Data.AsSpan(ofs, BoxNameLength);
var span = GetBoxNameSpan(box);
if (ReadUInt16BigEndian(span) == 0)
return;

View File

@@ -102,7 +102,7 @@ public uint PoketchStepCounter
public void SetPoketchDotArtistData(ReadOnlySpan<byte> value)
{
if (value.Length != 120)
throw new ArgumentException($"Expected {120} bytes.", nameof(value.Length));
throw new ArgumentOutOfRangeException($"Expected {120} bytes.", nameof(value.Length));
value.CopyTo(General.AsSpan(PoketchStart + 0x2A));
}
@@ -124,7 +124,7 @@ public HoneyTreeValue GetHoneyTree(int index)
public void SetHoneyTree(HoneyTreeValue tree, int index)
{
if (index <= 21)
SetData(General, tree.Data, OFS_HONEY + (HONEY_SIZE * index));
SetData(General.AsSpan(OFS_HONEY + (HONEY_SIZE * index)), tree.Data);
}
public MunchlaxTreeSet4 GetMunchlaxTrees() => CalculateMunchlaxTrees(TID16, SID16);

View File

@@ -212,7 +212,7 @@ public byte[] CGearSkinData
public EntreeForest EntreeData
{
get => new(GetData(EntreeForestOffset, EntreeForest.SIZE));
get => new(Data.AsSpan(EntreeForestOffset, EntreeForest.SIZE).ToArray());
set => SetData(value.Write(), EntreeForestOffset);
}
@@ -230,7 +230,7 @@ public EntreeForest EntreeData
public abstract Encount5 Encount { get; }
public static int GetMailOffset(int index) => (index * Mail5.SIZE) + 0x1DD00;
public byte[] GetMailData(int offset) => GetData(offset, Mail5.SIZE);
public byte[] GetMailData(int offset) => Data.AsSpan(offset, Mail5.SIZE).ToArray();
public int GetBattleBoxSlot(int slot) => BattleBoxOffset + (slot * SIZE_STORED);
public MailDetail GetMail(int mailIndex)

View File

@@ -175,7 +175,7 @@ public override void SetDaycareHasEgg(int loc, bool hasEgg)
}
public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A));
public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty<byte>() : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray();
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;
protected override bool[] MysteryGiftReceivedFlags { get => Blocks.MysteryGift.GetReceivedFlags(); set => Blocks.MysteryGift.SetReceivedFlags(value); }

View File

@@ -112,7 +112,7 @@ public override void SetDaycareRNGSeed(int loc, string seed)
}
public override string JPEGTitle => !HasJPPEGData ? string.Empty : StringConverter6.GetString(Data.AsSpan(JPEG, 0x1A));
public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty<byte>() : GetData(JPEG + 0x54, 0xE004);
public override byte[] GetJPEGData() => !HasJPPEGData ? Array.Empty<byte>() : Data.AsSpan(JPEG + 0x54, 0xE004).ToArray();
private bool HasJPPEGData => Data[JPEG + 0x54] == 0xFF;
public void UnlockAllFriendSafariSlots()

View File

@@ -283,7 +283,7 @@ public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, i
public string Rival
{
get => GetString(0x55F4, 0x1A);
get => GetString(Data.AsSpan(0x55F4, 0x1A));
set => SetString(Data.AsSpan(0x55F4, 0x1A), value, MaxStringLengthOT, StringConverterOption.ClearZero);
}
@@ -333,7 +333,7 @@ public override int PartyCount
}
public override PB8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(Data, offset, SIZE_PARTY)); // party format in boxes!
public override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes!
public enum TopMenuItemType
{

View File

@@ -130,8 +130,8 @@ protected override SAV8LA CloneInternal()
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = (byte)value; }
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = (byte)value; }
protected override byte[] BoxBuffer => BoxInfo.Data;
protected override byte[] PartyBuffer => PartyInfo.Data;
protected override Span<byte> BoxBuffer => BoxInfo.Data;
protected override Span<byte> PartyBuffer => PartyInfo.Data;
private void Initialize()
{

View File

@@ -246,10 +246,10 @@ public override int PartyCount
protected set => PartyInfo.PartyCount = value;
}
protected override byte[] BoxBuffer => BoxInfo.Data;
protected override byte[] PartyBuffer => PartyInfo.Data;
protected override Span<byte> BoxBuffer => BoxInfo.Data;
protected override Span<byte> PartyBuffer => PartyInfo.Data;
public override PK8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(BoxInfo.Data, offset, SIZE_PARTY)); // party format in boxes!
public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes!
public int GetRecord(int recordID) => Records.GetRecord(recordID);
public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value);

View File

@@ -217,10 +217,10 @@ public override int PartyCount
protected set => PartyInfo.PartyCount = value;
}
protected override byte[] BoxBuffer => BoxInfo.Data;
protected override byte[] PartyBuffer => PartyInfo.Data;
protected override Span<byte> BoxBuffer => BoxInfo.Data;
protected override Span<byte> PartyBuffer => PartyInfo.Data;
public override PK9 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(GetData(BoxInfo.Data, offset, SIZE_PARTY)); // party format in boxes!
public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.AsSpan(offset, SIZE_PARTY).ToArray()); // party format in boxes!
//public int GetRecord(int recordID) => Records.GetRecord(recordID);
//public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value);

View File

@@ -67,19 +67,15 @@ protected virtual byte[] GetFinalData()
#endregion
#region Savedata Container Handling
public byte[] GetData(int offset, int length) => GetData(Data, offset, length);
protected static byte[] GetData(byte[] data, int offset, int length) => data.Slice(offset, length);
public void SetData(byte[] input, int offset) => SetData(Data, input, offset);
public void SetData(ReadOnlySpan<byte> input, int offset) => SetData(Data, input, offset);
public void SetData(ReadOnlySpan<byte> input, int offset) => SetData(Data.AsSpan(offset), input);
public void SetData(Span<byte> dest, ReadOnlySpan<byte> input, int offset)
public void SetData(Span<byte> dest, ReadOnlySpan<byte> input)
{
input.CopyTo(dest[offset..]);
input.CopyTo(dest);
State.Edited = true;
}
public abstract string GetString(ReadOnlySpan<byte> data);
public string GetString(int offset, int length) => GetString(Data.AsSpan(offset, length));
public abstract int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option);
#endregion
@@ -206,7 +202,7 @@ public IList<PKM> PartyData
PKM[] data = new PKM[count];
for (int i = 0; i < data.Length; i++)
data[i] = GetPartySlot(PartyBuffer, GetPartyOffset(i));
data[i] = GetPartySlotAtIndex(i);
return data;
}
set
@@ -219,10 +215,10 @@ public IList<PKM> PartyData
#endif
int ctr = 0;
foreach (var exist in value.Where(pk => pk.Species != 0))
SetPartySlot(exist, PartyBuffer, GetPartyOffset(ctr++));
SetPartySlotAtIndex(exist, ctr++);
PartyCount = ctr;
for (int i = ctr; i < 6; i++)
SetPartySlot(BlankPKM, PartyBuffer, GetPartyOffset(i));
SetPartySlotAtIndex(BlankPKM, i);
}
}
#endregion
@@ -248,7 +244,8 @@ public IList<PKM> PartyData
public virtual void SetDaycareOccupied(int loc, int slot, bool occupied) { }
#endregion
public PKM GetPartySlotAtIndex(int index) => GetPartySlot(PartyBuffer, GetPartyOffset(index));
private Span<byte> GetPartySpan(int index) => PartyBuffer[GetPartyOffset(index)..];
public PKM GetPartySlotAtIndex(int index) => GetPartySlot(GetPartySpan(index));
public void SetPartySlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
{
@@ -267,48 +264,39 @@ public void SetPartySlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMI
PartyCount = index;
}
int offset = GetPartyOffset(index);
SetPartySlot(pk, PartyBuffer, offset, trade, dex);
SetPartySlot(pk, GetPartySpan(index), trade, dex);
}
public void SetSlotFormatParty(PKM pk, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
public void SetSlotFormatParty(PKM pk, Span<byte> data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
{
if (pk.GetType() != PKMType)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
UpdatePKM(pk, isParty: true, trade, dex);
SetPartyValues(pk, isParty: true);
WritePartySlot(pk, data, offset);
WritePartySlot(pk, data);
}
public void SetPartySlot(PKM pk, byte[] data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
{
if (pk.GetType() != PKMType)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
UpdatePKM(pk, isParty: true, trade, dex);
SetPartyValues(pk, isParty: true);
WritePartySlot(pk, data, offset);
}
public void SetSlotFormatStored(PKM pk, Span<byte> data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
public void SetSlotFormatStored(PKM pk, Span<byte> data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
{
if (pk.GetType() != PKMType)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
UpdatePKM(pk, isParty: false, trade, dex);
SetPartyValues(pk, isParty: false);
WriteSlotFormatStored(pk, data, offset);
WriteSlotFormatStored(pk, data);
}
public void SetBoxSlot(PKM pk, Span<byte> data, int offset, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
public void SetPartySlot(PKM pk, Span<byte> data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault) => SetSlotFormatParty(pk, data, trade, dex);
public void SetBoxSlot(PKM pk, Span<byte> data, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
{
if (pk.GetType() != PKMType)
throw new ArgumentException($"PKM Format needs to be {PKMType} when setting to this Save File.");
UpdatePKM(pk, isParty: false, trade, dex);
SetPartyValues(pk, isParty: false);
WriteBoxSlot(pk, data, offset);
WriteBoxSlot(pk, data);
}
public void DeletePartySlot(int slot)
@@ -340,23 +328,23 @@ public void DeletePartySlot(int slot)
public abstract int MaxEV { get; }
public virtual int MaxIV => 31;
public abstract ReadOnlySpan<ushort> HeldItems { get; }
protected virtual byte[] BoxBuffer => Data;
protected virtual byte[] PartyBuffer => Data;
protected virtual Span<byte> BoxBuffer => Data;
protected virtual Span<byte> PartyBuffer => Data;
public virtual bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresent(data);
public virtual PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public virtual PKM GetPartySlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_PARTY));
public virtual PKM GetStoredSlot(byte[] data, int offset) => GetDecryptedPKM(GetData(data, offset, SIZE_STORED));
public virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer, offset);
public virtual PKM GetPartySlot(ReadOnlySpan<byte> data) => GetDecryptedPKM(data[..SIZE_PARTY].ToArray());
public virtual PKM GetStoredSlot(ReadOnlySpan<byte> data) => GetDecryptedPKM(data[..SIZE_STORED].ToArray());
public virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer[offset..]);
public virtual byte[] GetDataForFormatStored(PKM pk) => pk.EncryptedBoxData;
public virtual byte[] GetDataForFormatParty(PKM pk) => pk.EncryptedPartyData;
public virtual byte[] GetDataForParty(PKM pk) => pk.EncryptedPartyData;
public virtual byte[] GetDataForBox(PKM pk) => pk.EncryptedBoxData;
public virtual void WriteSlotFormatStored(PKM pk, Span<byte> data, int offset) => SetData(data, GetDataForFormatStored(pk), offset);
public virtual void WriteSlotFormatParty(PKM pk, Span<byte> data, int offset) => SetData(data, GetDataForFormatParty(pk), offset);
public virtual void WritePartySlot(PKM pk, Span<byte> data, int offset) => SetData(data, GetDataForParty(pk), offset);
public virtual void WriteBoxSlot(PKM pk, Span<byte> data, int offset) => SetData(data, GetDataForBox(pk), offset);
public virtual void WriteSlotFormatStored(PKM pk, Span<byte> data) => SetData(data, GetDataForFormatStored(pk));
public virtual void WriteSlotFormatParty(PKM pk, Span<byte> data) => SetData(data, GetDataForFormatParty(pk));
public virtual void WritePartySlot(PKM pk, Span<byte> data) => SetData(data, GetDataForParty(pk));
public virtual void WriteBoxSlot(PKM pk, Span<byte> data) => SetData(data, GetDataForBox(pk));
protected virtual void SetPartyValues(PKM pk, bool isParty)
{
@@ -522,13 +510,15 @@ public void AddBoxData(IList<PKM> data, int box, int index)
public int NextOpenBoxSlot(int lastKnownOccupied = -1)
{
var storage = BoxBuffer.AsSpan();
var storage = BoxBuffer;
int count = SlotCount;
for (int i = lastKnownOccupied + 1; i < count; i++)
{
int offset = GetBoxSlotOffset(i);
if (!IsPKMPresent(storage[offset..]))
return i;
// overwrite protect is only true if there is already data in slot
if (IsPKMPresent(storage[offset..]))
continue;
return i;
}
return StorageFullValue;
}
@@ -593,10 +583,10 @@ public int GetBoxSlotOffset(int index)
}
public void SetBoxSlotAtIndex(PKM pk, int box, int slot, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
=> SetBoxSlot(pk, BoxBuffer, GetBoxSlotOffset(box, slot), trade, dex);
=> SetBoxSlot(pk, BoxBuffer[GetBoxSlotOffset(box, slot)..], trade, dex);
public void SetBoxSlotAtIndex(PKM pk, int index, PKMImportSetting trade = PKMImportSetting.UseDefault, PKMImportSetting dex = PKMImportSetting.UseDefault)
=> SetBoxSlot(pk, BoxBuffer, GetBoxSlotOffset(index), trade, dex);
=> SetBoxSlot(pk, BoxBuffer[GetBoxSlotOffset(index)..], trade, dex);
#endregion
#region Storage Manipulations
@@ -612,7 +602,7 @@ public bool MoveBox(int box, int insertBeforeBox)
return true;
}
private void MoveBox(int box, int insertBeforeBox, byte[] storage)
private void MoveBox(int box, int insertBeforeBox, Span<byte> storage)
{
int pos1 = BoxSlotCount * box;
int pos2 = BoxSlotCount * insertBeforeBox;
@@ -620,7 +610,7 @@ private void MoveBox(int box, int insertBeforeBox, byte[] storage)
int max = Math.Max(pos1, pos2);
int len = BoxSlotCount * SIZE_BOXSLOT;
byte[] boxdata = storage.Slice(GetBoxOffset(0), len * BoxCount); // get all boxes
byte[] boxdata = storage.Slice(GetBoxOffset(0), len * BoxCount).ToArray(); // get all boxes
string[] boxNames = Get(GetBoxName, BoxCount);
int[] boxWallpapers = Get(GetBoxWallpaper, BoxCount);
@@ -646,7 +636,7 @@ static T[] Get<T>(Func<int, T> act, int count)
b = ctr++;
}
Buffer.BlockCopy(boxdata, len * i, storage, GetBoxOffset(b), len);
boxdata.AsSpan(len * i, len).CopyTo(storage[GetBoxOffset(b)..]);
SetBoxName(b, boxNames[i]);
SetBoxWallpaper(b, boxWallpapers[i]);
}
@@ -754,7 +744,7 @@ public int SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func<IEnumerable<PKM>, i
/// <returns>Count of deleted <see cref="PKM"/> slots.</returns>
public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func<PKM, bool>? deleteCriteria = null)
{
var storage = BoxBuffer.AsSpan();
var storage = BoxBuffer;
if ((uint)BoxEnd >= BoxCount)
BoxEnd = BoxCount - 1;
@@ -777,7 +767,7 @@ public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func<PKM, bool>? delete
continue;
}
SetData(storage, blank, ofs);
SetData(storage[ofs..], blank);
++deleted;
}
}
@@ -796,7 +786,7 @@ public int ModifyBoxes(Action<PKM> action, int BoxStart = 0, int BoxEnd = -1)
if ((uint)BoxEnd >= BoxCount)
BoxEnd = BoxCount - 1;
var storage = BoxBuffer.AsSpan();
var storage = BoxBuffer;
int modified = 0;
for (int b = BoxStart; b <= BoxEnd; b++)
{
@@ -805,12 +795,13 @@ public int ModifyBoxes(Action<PKM> action, int BoxStart = 0, int BoxEnd = -1)
if (IsSlotOverwriteProtected(b, s))
continue;
var ofs = GetBoxSlotOffset(b, s);
if (!IsPKMPresent(storage[ofs..]))
var dest = storage[ofs..];
if (!IsPKMPresent(dest))
continue;
var pk = GetBoxSlotAtIndex(b, s);
action(pk);
++modified;
SetBoxSlot(pk, storage, ofs, PKMImportSetting.Skip, PKMImportSetting.Skip);
SetBoxSlot(pk, dest, PKMImportSetting.Skip, PKMImportSetting.Skip);
}
}
return modified;
@@ -846,7 +837,7 @@ public virtual void SetBoxWallpaper(int box, int value)
public byte[] GetPCBinary() => BoxData.SelectMany(GetDataForBox).ToArray();
public byte[] GetBoxBinary(int box) => GetBoxData(box).SelectMany(GetDataForBox).ToArray();
public bool SetPCBinary(byte[] data)
public bool SetPCBinary(ReadOnlySpan<byte> data)
{
if (IsRegionOverwriteProtected(0, SlotCount))
return false;
@@ -855,7 +846,7 @@ public bool SetPCBinary(byte[] data)
return SetConcatenatedBinary(data, expectLength);
}
public bool SetBoxBinary(byte[] data, int box)
public bool SetBoxBinary(ReadOnlySpan<byte> data, int box)
{
int start = box * BoxSlotCount;
int end = start + BoxSlotCount;
@@ -867,16 +858,22 @@ public bool SetBoxBinary(byte[] data, int box)
return SetConcatenatedBinary(data, expectLength, start);
}
private bool SetConcatenatedBinary(byte[] data, int expectLength, int start = 0)
private bool SetConcatenatedBinary(ReadOnlySpan<byte> data, int expectLength, int start = 0)
{
if (data.Length != expectLength)
return false;
var BD = BoxData;
var entryLength = SIZE_BOXSLOT;
var pkdata = ArrayUtil.EnumerateSplit(data, entryLength);
pkdata.Select(GetPKM).CopyTo(BD, IsSlotOverwriteProtected, start);
for (int i = 0, ctr = start; i < data.Length; i += entryLength)
{
if (IsSlotOverwriteProtected(ctr))
continue;
var src = data.Slice(i, entryLength);
var arr = src.ToArray();
var pk = GetPKM(arr);
SetBoxSlotAtIndex(pk, ctr++);
}
BoxData = BD;
return true;
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
@@ -22,6 +21,6 @@ public sealed class Bank3 : BulkStorage
private int BoxDataSize => SlotsPerBox * SIZE_STORED;
public override int GetBoxOffset(int box) => Box + (BoxDataSize * box);
public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BoxNameSize);
public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize));
private static int GetBoxNameOffset(int box) => 0x25800 + (9 * box);
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
@@ -22,6 +21,6 @@ public sealed class Bank4 : BulkStorage
private int BoxDataSize => SlotsPerBox * SIZE_STORED;
public override int GetBoxOffset(int box) => Box + (BoxDataSize * box);
public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BoxNameSize / 2);
public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BoxNameSize / 2));
private static int GetBoxNameOffset(int box) => 0x3FC00 + (0x19 * box);
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@@ -28,7 +27,7 @@ public string GetGroupName(int group)
if ((uint)group > 10)
throw new ArgumentOutOfRangeException(nameof(group), $"{nameof(group)} must be 0-10.");
int offset = 0x8 + (GroupNameSpacing * group) + 2; // skip over " "
return GetString(offset, GroupNameSize / 2);
return GetString(Data.AsSpan(offset, GroupNameSize / 2));
}
public override int BoxCount => BankCount;
@@ -47,7 +46,7 @@ private int BankCount
private int BoxDataSize => (SlotsPerBox * SIZE_STORED) + BankNameSpacing;
public override int GetBoxOffset(int box) => Box + (BoxDataSize * box);
public override string GetBoxName(int box) => GetString(GetBoxNameOffset(box), BankNameSize / 2);
public override string GetBoxName(int box) => GetString(Data.AsSpan(GetBoxNameOffset(box), BankNameSize / 2));
public int GetBoxNameOffset(int box) => GetBoxOffset(box) + (SlotsPerBox * SIZE_STORED);
public int GetBoxIndex(int box) => ReadUInt16LittleEndian(Data.AsSpan(GetBoxNameOffset(box) + BankNameSize));

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using static System.Buffers.Binary.BinaryPrimitives;
@@ -51,7 +50,7 @@ public SAV4Ranch(byte[] data) : base(data, typeof(RK4), 0)
{
Version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP;
OT = GetString(0x770, 0x12);
OT = GetString(Data.AsSpan(0x770, 0x12));
// 0x18 starts the header table: [u32 BlockID, u32 Offset]
// Block 00, Offset = Metadata object
@@ -109,7 +108,7 @@ public void SetRanchToy(RanchToy toy, int index)
toy = BlankToy;
int toyOffset = ToyBaseOffset + (RanchToy.SIZE * index);
SetData(Data, toy.Data, toyOffset);
SetData(Data.AsSpan(toyOffset), toy.Data);
}
public RanchMii GetRanchMii(int index)
@@ -128,7 +127,7 @@ public void SetRanchMii(RanchMii trainer, int index)
throw new ArgumentOutOfRangeException(nameof(index));
int offset = MiiDataOffset + (RanchMii.SIZE * index);
SetData(Data, trainer.Data, offset);
SetData(Data.AsSpan(offset), trainer.Data);
}
public RanchTrainerMii GetRanchTrainerMii(int index)
@@ -147,7 +146,7 @@ public void SetRanchTrainerMii(RanchTrainerMii mii, int index)
throw new ArgumentOutOfRangeException(nameof(index));
int offset = TrainerMiiDataOffset + (RanchTrainerMii.SIZE * index);
SetData(Data, mii.Data, offset);
SetData(Data.AsSpan(offset), mii.Data);
}
private const int sha1HashSize = 20;
@@ -155,9 +154,8 @@ public void SetRanchTrainerMii(RanchTrainerMii mii, int index)
protected override void SetChecksums()
{
var data = Data.AsSpan();
// ensure the final data is cleared if the user screws stuff up
WriteInt32BigEndian(data[DataEndMarkerOffset..], DataEndMarker);
data[(DataEndMarkerOffset + 4)..].Clear();
var slotCount = GetOccupiedSlotCount();
UpdateMetadata(slotCount * SIZE_STORED);
// 20 byte SHA checksum at the top of the file, which covers all data that follows.
var hash = data[..sha1HashSize];
@@ -165,6 +163,20 @@ protected override void SetChecksums()
SHA1.HashData(payload, hash);
}
private int GetOccupiedSlotCount()
{
int count = SlotCount;
for (int i = count - 1; i >= 0; i--)
{
var ofs = GetBoxSlotOffset(i);
var span = Data.AsSpan(ofs, SIZE_STORED);
var type = ReadUInt64LittleEndian(span[0x88..]);
if (type != 0)
return i + 1;
}
return 0;
}
protected override byte[] DecryptPKM(byte[] data)
{
var pokeData = PokeCrypto.DecryptArray45(data.Slice(0, PokeCrypto.SIZE_4STORED));
@@ -176,7 +188,7 @@ protected override byte[] DecryptPKM(byte[] data)
return finalData;
}
public void WriteBoxSlotInternal(PKM pk, Span<byte> data, int offset, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley)
public void WriteBoxSlotInternal(PKM pk, Span<byte> data, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley)
{
RK4 rk = (RK4)this.GetCompatiblePKM(pk);
rk.OwnershipType = type;
@@ -184,33 +196,38 @@ public void WriteBoxSlotInternal(PKM pk, Span<byte> data, int offset, string htN
rk.HT_SID = htSID;
rk.HT_Name = htName;
WriteBoxSlot(rk, data, offset);
WriteBoxSlot(rk, data);
}
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
public override void WriteBoxSlot(PKM pk, Span<byte> data)
{
bool isBlank = pk.Data.SequenceEqual(BlankPKM.Data);
if (pk is not RK4 rk4)
{
WriteBoxSlotInternal(pk, data, offset);
WriteBoxSlotInternal(pk, data);
return;
}
bool isBlank = pk.Data.SequenceEqual(BlankPKM.Data);
if (!isBlank && rk4.OwnershipType == RanchOwnershipType.None)
rk4.OwnershipType = RanchOwnershipType.Hayley; // Pokemon without an Ownership type get erased when the save is loaded. Hayley is considered 'default'.
base.WriteBoxSlot(rk4, data, offset);
if ((offset + SIZE_STORED) > DataEndMarkerOffset)
base.WriteBoxSlot(rk4, data);
}
private void UpdateMetadata(int pkEnd)
{
var data = Data.AsSpan();
// ensure the final data is cleared if the user screws stuff up
{
DataEndMarkerOffset = (offset + SIZE_STORED);
WriteInt32BigEndian(Data.AsSpan(0x3C), DataEndMarkerOffset);
WriteInt32BigEndian(Data.AsSpan(DataEndMarkerOffset), DataEndMarker);
DataEndMarkerOffset = pkEnd;
WriteInt32BigEndian(data[0x3C..], pkEnd);
WriteInt32BigEndian(data[pkEnd..], DataEndMarker);
data[(pkEnd + 4)..].Clear();
}
int pkStart = PokemonCountOffset + 4;
int pkEnd = DataEndMarkerOffset;
int pkCount = (pkEnd - pkStart) / SIZE_STORED;
WriteInt32BigEndian(Data.AsSpan(PokemonCountOffset), pkCount);
WriteInt32BigEndian(data[PokemonCountOffset..], pkCount);
}
private TimeSpan PlayedSpan

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PKHeX.Core;
@@ -36,7 +37,11 @@ private static void WritePoffins(SAV4Sinnoh sav, int offset, IReadOnlyList<Poffi
{
Debug.Assert(poffins.Count == Count);
for (int i = 0; i < poffins.Count; i++)
sav.SetData(sav.General, poffins[i].Data, offset + (i * Poffin4.SIZE));
{
var o = offset + (i * Poffin4.SIZE);
var span = sav.General.AsSpan(o);
sav.SetData(span, poffins[i].Data);
}
}
public void FillCase()

View File

@@ -23,7 +23,7 @@ public EncryptedMysteryGiftAlbum GiftAlbum
get
{
uint seed = ReadUInt32LittleEndian(Data.AsSpan(SeedOffset));
byte[] wcData = SAV.GetData(Offset + FlagStart, 0xA90); // Encrypted, Decrypt
byte[] wcData = SAV.Data.AsSpan(Offset + FlagStart, 0xA90).ToArray(); // Encrypted, Decrypt
return GetAlbum(seed, wcData);
}
set

View File

@@ -46,7 +46,7 @@ public DataMysteryGift GetGift(int index)
throw new ArgumentOutOfRangeException(nameof(index));
var offset = GetGiftOffset(index);
var data = SAV.GetData(offset, WC6.Size);
var data = SAV.Data.AsSpan(offset, WC6.Size).ToArray();
return new WC6(data);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@@ -10,8 +10,8 @@ public sealed class Puff6 : SaveBlock<SAV6>
public Puff6(SAV6 SAV, int offset) : base(SAV) => Offset = offset;
public byte[] GetPuffs() => SAV.GetData(Offset, PuffSlots);
public void SetPuffs(byte[] value) => SAV.SetData(value, Offset);
public Span<byte> GetPuffs() => SAV.Data.AsSpan(Offset, PuffSlots);
public void SetPuffs(ReadOnlySpan<byte> value) => SAV.SetData(value, Offset);
public int PuffCount
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace PKHeX.Core;
@@ -13,7 +13,7 @@ public FashionItem7[] Wardrobe
{
get
{
var data = SAV.GetData(Offset, 0x5A8);
var data = SAV.Data.AsSpan(Offset, 0x5A8).ToArray();
return Array.ConvertAll(data, b => new FashionItem7(b));
}
set
@@ -32,17 +32,25 @@ public FashionItem7[] Wardrobe
/// </summary>
public void Reset()
{
var offsetList = SAV is SAV7USUM
? (SAV.Gender == 0
? new[] { 0x03A, 0x109, 0x1DA, 0x305, 0x3D9, 0x4B1, 0x584 } // M
: new[] { 0x05E, 0x208, 0x264, 0x395, 0x3B4, 0x4F9, 0x5A8 }) // F
: (SAV.Gender == 0
? new[] { 0x000, 0x0FB, 0x124, 0x28F, 0x3B4, 0x452, 0x517 } // M
: new[] { 0x000, 0x100, 0x223, 0x288, 0x3B4, 0x452, 0x517 }); // F
var offsetList = GetDefaultFashionOffsets(SAV);
foreach (var ofs in offsetList)
SAV.Data[Offset + ofs] = 3; // owned | new
}
private static ReadOnlySpan<ushort> GetDefaultFashionOffsets(SAV7 sav) => sav switch
{
SAV7SM { Gender: 0 } => DefaultFashionOffsetSM_M,
SAV7SM { Gender: 1 } => DefaultFashionOffsetSM_F,
SAV7USUM { Gender: 0 } => DefaultFashionOffsetUU_M,
SAV7USUM { Gender: 1 } => DefaultFashionOffsetUU_F,
_ => throw new ArgumentOutOfRangeException(nameof(SAV)),
};
// Offsets that are set to '3' when the game starts for a specific gender.
private static ReadOnlySpan<ushort> DefaultFashionOffsetSM_M => new ushort[] { 0x000, 0x0FB, 0x124, 0x28F, 0x3B4, 0x452, 0x517 };
private static ReadOnlySpan<ushort> DefaultFashionOffsetSM_F => new ushort[] { 0x000, 0x100, 0x223, 0x288, 0x3B4, 0x452, 0x517 };
private static ReadOnlySpan<ushort> DefaultFashionOffsetUU_M => new ushort[] { 0x03A, 0x109, 0x1DA, 0x305, 0x3D9, 0x4B1, 0x584 };
private static ReadOnlySpan<ushort> DefaultFashionOffsetUU_F => new ushort[] { 0x05E, 0x208, 0x264, 0x395, 0x3B4, 0x4F9, 0x5A8 };
}
// Every fashion item is 2 bits, New Flag (high) & Owned Flag (low)

View File

@@ -13,7 +13,7 @@ public sealed class FestaFacility
public FestaFacility(SAV7 sav, int index)
{
ofs = (index * SIZE) + sav.Festa.Offset + 0x310;
Data = sav.GetData(ofs, SIZE);
Data = sav.Data.AsSpan(ofs, SIZE).ToArray();
Language = sav.Language;
}

View File

@@ -20,7 +20,9 @@ public ushort RotomAffection
public string RotomOT
{
get => SAV.GetString(Offset + 0x30, 0x1A);
set => SAV.SetString(Data.AsSpan(Offset + 0x30, 0x1A), value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero);
get => SAV.GetString(RotomNameSpan);
set => SAV.SetString(RotomNameSpan, value, SAV.MaxStringLengthOT, StringConverterOption.ClearZero);
}
private Span<byte> RotomNameSpan => Data.AsSpan(Offset + 0x30, 0x1A);
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static System.Buffers.Binary.BinaryPrimitives;
@@ -109,10 +108,10 @@ private int[] LoadPointerData()
return list;
}
private void SetPointerData(IList<int> vals)
private void SetPointerData(ReadOnlySpan<int> vals)
{
var span = Data.AsSpan(Offset);
for (int i = 0; i < vals.Count; i++)
for (int i = 0; i < vals.Length; i++)
WriteUInt16LittleEndian(span[(i*2)..], (ushort)vals[i]);
vals.CopyTo(PokeListInfo);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace PKHeX.Core;
@@ -50,7 +50,7 @@ private WC7 GetGift(int index)
throw new ArgumentOutOfRangeException(nameof(index));
var offset = GetGiftOffset(index);
var data = SAV.GetData(offset, WC7.Size);
var data = SAV.Data.AsSpan(offset, WC7.Size).ToArray();
return new WC7(data);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace PKHeX.Core;
@@ -13,13 +13,14 @@ public PK7[] ResortPKM
{
get
{
PK7[] data = new PK7[ResortCount];
for (int i = 0; i < data.Length; i++)
PK7[] result = new PK7[ResortCount];
for (int i = 0; i < result.Length; i++)
{
var bytes = SAV.GetData(GetResortSlotOffset(i), PokeCrypto.SIZE_6STORED);
data[i] = new PK7(bytes);
var ofs = GetResortSlotOffset(i);
var data = SAV.Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED).ToArray();
result[i] = new PK7(data);
}
return data;
return result;
}
set
{
@@ -27,7 +28,11 @@ public PK7[] ResortPKM
throw new ArgumentException(nameof(ResortCount));
for (int i = 0; i < value.Length; i++)
SAV.SetSlotFormatStored(value[i], Data, GetResortSlotOffset(i));
{
var ofs = GetResortSlotOffset(i);
var dest = Data.AsSpan(ofs, PokeCrypto.SIZE_6STORED);
SAV.SetSlotFormatStored(value[i], dest);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using static System.Buffers.Binary.BinaryPrimitives;

View File

@@ -1,4 +1,6 @@
namespace PKHeX.Core;
using System;
namespace PKHeX.Core;
/// <summary>
/// Storage for the species that was fused into <see cref="Species.Kyurem"/> and <see cref="Species.Necrozma"/>.
@@ -14,21 +16,11 @@ public static int GetFusedSlotOffset(int slot)
return PokeCrypto.SIZE_8PARTY * slot;
}
public PK8 Kyurem
{
get => (PK8) SAV.GetStoredSlot(Data, GetFusedSlotOffset(0));
set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(0));
}
private Span<byte> GetSlotSpan(int index) => Data.AsSpan(GetFusedSlotOffset(index), PokeCrypto.SIZE_8STORED);
private PK8 GetStoredSlot(int index) => (PK8)SAV.GetStoredSlot(GetSlotSpan(index));
private void SetStoredSlot(PK8 pk, int index) => pk.EncryptedBoxData.CopyTo(GetSlotSpan(index));
public PK8 NecrozmaSolgaleo
{
get => (PK8)SAV.GetStoredSlot(Data, GetFusedSlotOffset(1));
set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(1));
}
public PK8 NecrozmaLunala
{
get => (PK8)SAV.GetStoredSlot(Data, GetFusedSlotOffset(2));
set => value.EncryptedBoxData.CopyTo(Data, GetFusedSlotOffset(2));
}
public PK8 Kyurem { get => GetStoredSlot(0); set => SetStoredSlot(value, 0); }
public PK8 NecrozmaSolgaleo { get => GetStoredSlot(1); set => SetStoredSlot(value, 1); }
public PK8 NecrozmaLunala { get => GetStoredSlot(2); set => SetStoredSlot(value, 2); }
}

View File

@@ -25,7 +25,7 @@ public sealed class Mail2 : MailDetail
private const int AUTHOR_LENGTH = 7;
public Mail2(SAV2 sav, int index) : base(sav.GetData(GetMailOffset(index), 0x2F), GetMailOffset(index))
public Mail2(SAV2 sav, int index) : base(sav.Data.AsSpan(GetMailOffset(index), 0x2F).ToArray(), GetMailOffset(index))
{
US = sav is { Japanese: false, Korean: false };
}

View File

@@ -18,7 +18,7 @@ public Mail4(byte? lang, byte? ver) : base(new byte[SIZE])
ResetData();
}
public override void CopyTo(SaveFile sav) => sav.SetData(((SAV4)sav).General, Data, DataOffset);
public override void CopyTo(SaveFile sav) => sav.SetData(((SAV4)sav).General.AsSpan(DataOffset), Data);
private void ResetData()
{

View File

@@ -26,9 +26,9 @@ public static class ArrayUtil
public static int Count<T>(this ReadOnlySpan<T> data, T value) where T : IEquatable<T>
{
int count = 0;
for (int i = data.Length - 1; i >= 0; i--)
foreach (var t in data)
{
if (data[i].Equals(value))
if (t.Equals(value))
count++;
}
return count;
@@ -86,26 +86,6 @@ public static int FindNextValidIndex<T>(IList<T> dest, Func<int, bool> skip, int
}
}
/// <summary>
/// Copies an <see cref="IEnumerable{T}"/> list to the destination list, with an option to copy to a starting point.
/// </summary>
/// <typeparam name="T">Typed object to copy</typeparam>
/// <param name="list">Source list to copy from</param>
/// <param name="dest">Destination list/array</param>
/// <param name="start">Starting point to copy to</param>
/// <returns>Count of <see cref="T"/> copied.</returns>
public static int CopyTo<T>(this IEnumerable<T> list, IList<T> dest, int start = 0)
{
int ctr = start;
foreach (var z in list)
{
if ((uint)ctr >= dest.Count)
break;
dest[ctr++] = z;
}
return ctr - start;
}
internal static T[] ConcatAll<T>(params T[][] arr)
{
int len = 0;

View File

@@ -831,7 +831,7 @@ public bool ExportBackup()
return true;
}
public bool OpenPCBoxBin(byte[] input, out string c)
public bool OpenPCBoxBin(ReadOnlySpan<byte> input, out string c)
{
if (SAV.GetPCBinary().Length == input.Length)
{

View File

@@ -773,7 +773,7 @@ private void B_DumpFC_Click(object sender, EventArgs e)
if (sfd.ShowDialog() != DialogResult.OK)
return;
var data = SAV.GetData(ofsForestCity, ofsForestCitySize);
var data = SAV.Data.AsSpan(ofsForestCity, ofsForestCitySize).ToArray();
File.WriteAllBytes(sfd.FileName, data);
}

View File

@@ -300,9 +300,9 @@ private void Save()
private void LoadBattleAgency()
{
p[0] = SAV.GetStoredSlot(SAV.Data, 0x6C200);
p[1] = SAV.GetPartySlot(SAV.Data, 0x6C2E8);
p[2] = SAV.GetPartySlot(SAV.Data, 0x6C420);
p[0] = SAV.GetStoredSlot(SAV.Data.AsSpan(0x6C200));
p[1] = SAV.GetPartySlot(SAV.Data.AsSpan(0x6C2E8));
p[2] = SAV.GetPartySlot(SAV.Data.AsSpan(0x6C420));
LoadPictureBox();
B_ImportParty.Visible = SAV.HasParty;
CHK_Choosed.Checked = SAV.GetFlag(0x6C55E, 1);