2022-06-18 18:04:24 +00:00
|
|
|
using System;
|
2022-01-03 05:35:59 +00:00
|
|
|
using static System.Buffers.Binary.BinaryPrimitives;
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
namespace PKHeX.Core;
|
|
|
|
|
2024-03-04 05:13:16 +00:00
|
|
|
public sealed class MysteryBlock5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw), IMysteryGiftStorage, IMysteryGiftFlags
|
2019-06-09 02:56:11 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
private const int FlagStart = 0;
|
|
|
|
private const int MaxReceivedFlag = 2048;
|
|
|
|
private const int MaxCardsPresent = 12;
|
|
|
|
private const int FlagRegionSize = (MaxReceivedFlag / 8); // 0x100
|
|
|
|
private const int CardStart = FlagStart + FlagRegionSize;
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private const int DataSize = 0xA90;
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// Everything is stored encrypted, and only decrypted on demand. Only crypt on object fetch...
|
2024-03-04 05:13:16 +00:00
|
|
|
private uint AlbumSeed
|
|
|
|
{
|
|
|
|
get => ReadUInt32LittleEndian(Data[DataSize..]);
|
|
|
|
set => WriteUInt32LittleEndian(Data[DataSize..], value);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Span<byte> DataRegion => Data[..^4]; // 0xA90
|
|
|
|
private Span<byte> FlagRegion => Data[..CardStart]; // 0x100
|
|
|
|
|
|
|
|
private bool IsDecrypted;
|
|
|
|
public void EndAccess() => EnsureDecrypted(false);
|
|
|
|
private void EnsureDecrypted(bool state = true)
|
|
|
|
{
|
|
|
|
if (IsDecrypted == state)
|
|
|
|
return;
|
|
|
|
PokeCrypto.CryptArray(DataRegion, AlbumSeed);
|
|
|
|
IsDecrypted = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void ClearReceivedFlags()
|
|
|
|
{
|
|
|
|
EnsureDecrypted();
|
|
|
|
FlagRegion.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int GetGiftOffset(int index)
|
|
|
|
{
|
|
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxCardsPresent);
|
|
|
|
return CardStart + (index * PGF.Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Span<byte> GetCardSpan(int index)
|
|
|
|
{
|
|
|
|
var offset = GetGiftOffset(index);
|
|
|
|
EnsureDecrypted();
|
|
|
|
return Data.Slice(offset, PGF.Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
public PGF GetMysteryGift(int index) => new(GetCardSpan(index).ToArray());
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2024-03-04 05:13:16 +00:00
|
|
|
public void SetMysteryGift(int index, PGF pgf)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
2024-03-04 05:13:16 +00:00
|
|
|
if ((uint)index > MaxCardsPresent)
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
|
|
if (pgf.Data.Length != PGF.Size)
|
|
|
|
throw new InvalidCastException(nameof(pgf));
|
|
|
|
SAV.SetData(GetCardSpan(index), pgf.Data);
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2024-03-04 05:13:16 +00:00
|
|
|
public int MysteryGiftReceivedFlagMax => MaxReceivedFlag;
|
|
|
|
public bool GetMysteryGiftReceivedFlag(int index)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
2024-03-04 05:13:16 +00:00
|
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag);
|
|
|
|
EnsureDecrypted();
|
|
|
|
return FlagUtil.GetFlag(Data, index); // offset 0
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2019-06-09 02:56:11 +00:00
|
|
|
|
2024-03-04 05:13:16 +00:00
|
|
|
public void SetMysteryGiftReceivedFlag(int index, bool value)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
2024-03-04 05:13:16 +00:00
|
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MaxReceivedFlag);
|
|
|
|
EnsureDecrypted();
|
|
|
|
FlagUtil.SetFlag(Data, index, value); // offset 0
|
2019-06-09 02:56:11 +00:00
|
|
|
}
|
2024-03-04 05:13:16 +00:00
|
|
|
|
|
|
|
public int GiftCountMax => MaxCardsPresent;
|
|
|
|
DataMysteryGift IMysteryGiftStorage.GetMysteryGift(int index) => GetMysteryGift(index);
|
|
|
|
void IMysteryGiftStorage.SetMysteryGift(int index, DataMysteryGift gift) => SetMysteryGift(index, (PGF)gift);
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class GTS5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
|
|
|
{
|
|
|
|
// 0x08: Stored Upload
|
|
|
|
private const int SizeStored = PokeCrypto.SIZE_5STORED;
|
|
|
|
|
|
|
|
public Memory<byte> Upload => Raw[..SizeStored];
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class GlobalLink5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
|
|
|
{
|
|
|
|
// 0x08: Stored Upload
|
|
|
|
private const int SizeStored = PokeCrypto.SIZE_5STORED;
|
|
|
|
|
|
|
|
public Memory<byte> Upload => Raw.Slice(8, SizeStored);
|
|
|
|
}
|
|
|
|
|
|
|
|
public sealed class AdventureInfo5(SAV5 sav, Memory<byte> raw) : SaveBlock<SAV5>(sav, raw)
|
|
|
|
{
|
|
|
|
public uint SecondsToStart { get => ReadUInt32LittleEndian(Data[0x34..]); set => WriteUInt32LittleEndian(Data[0x34..], value); }
|
|
|
|
public uint SecondsToFame { get => ReadUInt32LittleEndian(Data[0x3C..]); set => WriteUInt32LittleEndian(Data[0x3C..], value); }
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|