using System; using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// format for /// public sealed class SAV4Pt : SAV4Sinnoh { public SAV4Pt() : base(GeneralSize, StorageSize) { Initialize(); Dex = new Zukan4(this, PokeDex); } public SAV4Pt(byte[] data) : base(data, GeneralSize, StorageSize, GeneralSize) { Initialize(); Dex = new Zukan4(this, PokeDex); } public override Zukan4 Dex { get; } protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4Pt((byte[])Data.Clone()) : new SAV4Pt(); public override PersonalTable4 Personal => PersonalTable.Pt; public override ReadOnlySpan HeldItems => Legal.HeldItems_Pt; public override int MaxItemID => Legal.MaxItemID_4_Pt; public const int GeneralSize = 0xCF2C; private const int StorageSize = 0x121E4; // Start 0xCF2C, +4 starts box data private const int VillaFurnitureStart = 0x111F; protected override BlockInfo4[] ExtraBlocks => [ new BlockInfo4(0, 0x20000, 0x2AC0), // Hall of Fame new BlockInfo4(1, 0x23000, 0x0BB0), // Battle Hall new BlockInfo4(2, 0x24000, 0x1D60), // Battle Video (My Video) new BlockInfo4(3, 0x26000, 0x1D60), // Battle Video (Other Videos 1) new BlockInfo4(4, 0x28000, 0x1D60), // Battle Video (Other Videos 2) new BlockInfo4(5, 0x2A000, 0x1D60), // Battle Video (Other Videos 3) ]; private void Initialize() { Version = GameVersion.Pt; GetSAVOffsets(); } protected override int EventWork => 0xDAC; protected override int EventFlag => 0xFEC; public override BattleFrontierFacility4 MaxFacility => BattleFrontierFacility4.Arcade; private void GetSAVOffsets() { AdventureInfo = 0; Trainer1 = 0x68; Party = 0xA0; PokeDex = 0x1328; Extra = 0x2820; ChatterOffset = 0x64EC; Geonet = 0xA4C4; WondercardFlags = 0xB4C0; WondercardData = 0xB5C0; DaycareOffset = 0x1654; OFS_HONEY = 0x7F38; OFS_UG_Stats = 0x3CB4; OFS_UG_Items = 0x4538; PoketchStart = 0x1160; OFS_PoffinCase = 0x52E8; Seal = 0x6494; Box = 4; } #region Storage private static int AdjustWallpaper(int value, int shift) { // Pt's Special Wallpapers 1-8 are shifted by +0x8 // HG/SS Special Wallpapers 1-8 (Primo Phrases) are shifted by +0x10 if (value >= 0x10) // special return value + shift; return value; } public override int GetBoxWallpaper(int box) { if ((uint)box > 18) return 0; int value = Storage[GetBoxWallpaperOffset(box)]; return AdjustWallpaper(value, -0x08); } public override void SetBoxWallpaper(int box, int value) { if ((uint)box >= 18) return; value = AdjustWallpaper(value, 0x08); Storage[GetBoxWallpaperOffset(box)] = (byte)value; } #endregion public override IReadOnlyList Inventory { get { var info = ItemStorage4Pt.Instance; InventoryPouch[] pouch = [ new InventoryPouch4(InventoryType.Items, info, 999, 0x630), new InventoryPouch4(InventoryType.KeyItems, info, 1, 0x8C4), new InventoryPouch4(InventoryType.TMHMs, info, 99, 0x98C), new InventoryPouch4(InventoryType.MailItems, info, 999, 0xB1C), new InventoryPouch4(InventoryType.Medicine, info, 999, 0xB4C), new InventoryPouch4(InventoryType.Berries, info, 999, 0xBEC), new InventoryPouch4(InventoryType.Balls, info, 999, 0xCEC), new InventoryPouch4(InventoryType.BattleItems, info, 999, 0xD28), ]; return pouch.LoadAll(General); } set => value.SaveAll(General); } public override int M { get => ReadUInt16LittleEndian(General[0x1280..]); set => WriteUInt16LittleEndian(General[0x1280..], (ushort)value); } 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 Rival_Trash { get => RivalSpan; set { if (value.Length == MaxStringLengthOT * 2) value.CopyTo(RivalSpan); } } private Span RivalSpan => General.Slice(0x27E8, MaxStringLengthOT * 2); public override int X2 { get => ReadUInt16LittleEndian(General[0x287E..]); set => WriteUInt16LittleEndian(General[0x287E..], (ushort)value); } public override int Y2 { get => ReadUInt16LittleEndian(General[0x2882..]); set => WriteUInt16LittleEndian(General[0x2882..], (ushort)value); } public override int Z { get => ReadUInt16LittleEndian(General[0x2886..]); set => WriteUInt16LittleEndian(General[0x2886..], (ushort)value); } public override uint SafariSeed { get => ReadUInt32LittleEndian(General[0x5660..]); set => WriteUInt32LittleEndian(General[0x5660..], value); } public override uint SwarmSeed { get => ReadUInt32LittleEndian(General[0x5664..]); set => WriteUInt32LittleEndian(General[0x5664..], value); } public override uint SwarmMaxCountModulo => 22; public override int BP { get => ReadUInt16LittleEndian(General[0x7234..]); set => WriteUInt16LittleEndian(General[0x7234..], (ushort)value); } protected override ReadOnlySpan TreeSpecies => [ 000, 000, 000, 000, 000, 000, 415, 265, 412, 420, 190, 190, 412, 420, 415, 190, 190, 214, 446, 446, 446, 446, 446, 446, ]; public Roamer4 RoamerMesprit => GetRoamer(0); public Roamer4 RoamerCresselia => GetRoamer(1); public Roamer4 RoamerUnused => GetRoamer(2); // Darkrai public Roamer4 RoamerArticuno => GetRoamer(3); public Roamer4 RoamerZapdos => GetRoamer(4); public Roamer4 RoamerMoltres => GetRoamer(5); private Roamer4 GetRoamer(int index) { const int size = Roamer4.SIZE; var ofs = 0x7FF4 + (index * size); var mem = GeneralBuffer.Slice(ofs, size); return new Roamer4(mem); } public bool GetVillaFurniturePurchased(VillaFurniture index) { if (index > VillaFurniture.MAX) throw new ArgumentOutOfRangeException(nameof(index)); return FlagUtil.GetFlag(General, VillaFurnitureStart + ((byte)index >> 3), (byte)index & 7); } public void SetVillaFurniturePurchased(VillaFurniture index, bool value = true) { if (index > VillaFurniture.MAX) throw new ArgumentOutOfRangeException(nameof(index)); FlagUtil.SetFlag(General, VillaFurnitureStart + ((byte)index >> 3), (byte)index & 7, value); } } public enum VillaFurniture { BigSofa, SmallSofa, Bed, NightTable, TV, AudioSystem, Bookshelf, Rack, Houseplant, PCDesk, MusicBox, PokemonBust1, PokemonBust2, Piano, GuestSet, WallClock, Masterpiece, TeaSet, Chandelier, MAX = Chandelier, }