mirror of
https://github.com/kwsch/PKHeX
synced 2024-12-24 19:33:10 +00:00
b0da14b71e
Adds a basic editor for recorded Geonet/Unity Tower locations for the Gen 4/5 games, building on this [post by Danius88](https://projectpokemon.org/home/forums/topic/62055-bw-b2w2-unity-tower-geonet-and-passerby-research/). So far, I've implemented buttons that set all locations (including unused ones), set all legal locations, and clear all recorded locations; and checkboxes to toggle whether the whole globe is visible (used in Japanese games) and whether the ferry to Unity Tower is unlocked. Haven't implemented any UI for editing the status of individual locations, since I'm not sure how to lay it out. Also haven't implemented anything related to how the data of the other players in Unity Tower is stored.
182 lines
6.2 KiB
C#
182 lines
6.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using static System.Buffers.Binary.BinaryPrimitives;
|
|
|
|
namespace PKHeX.Core;
|
|
|
|
/// <summary>
|
|
/// <see cref="SaveFile"/> format for <see cref="GameVersion.DP"/>
|
|
/// </summary>
|
|
public sealed class SAV4DP : SAV4Sinnoh
|
|
{
|
|
public SAV4DP() : base(GeneralSize, StorageSize)
|
|
{
|
|
Initialize();
|
|
Dex = new Zukan4(this, PokeDex);
|
|
}
|
|
|
|
public SAV4DP(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 SAV4DP((byte[])Data.Clone()) : new SAV4DP();
|
|
public override PersonalTable4 Personal => PersonalTable.DP;
|
|
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_DP;
|
|
public override int MaxItemID => Legal.MaxItemID_4_DP;
|
|
|
|
private const int GeneralSize = 0xC100;
|
|
private const int StorageSize = 0x121E0; // Start 0xC100, +4 starts box data
|
|
|
|
private void Initialize()
|
|
{
|
|
Version = GameVersion.DP;
|
|
GetSAVOffsets();
|
|
}
|
|
|
|
protected override int EventWork => 0xD9C;
|
|
protected override int EventFlag => 0xFDC;
|
|
|
|
private void GetSAVOffsets()
|
|
{
|
|
AdventureInfo = 0;
|
|
Trainer1 = 0x64;
|
|
Party = 0x98;
|
|
PokeDex = 0x12DC;
|
|
Geonet = 0x96D8;
|
|
WondercardFlags = 0xA6D0;
|
|
WondercardData = 0xA7fC;
|
|
|
|
DaycareOffset = 0x141C;
|
|
OFS_HONEY = 0x72E4;
|
|
|
|
OFS_UG_Stats = 0x3A2C;
|
|
OFS_UG_Items = 0x42B0;
|
|
|
|
PoketchStart = 0x114C;
|
|
Seal = 0x6178;
|
|
|
|
OFS_PoffinCase = 0x5050;
|
|
|
|
Box = 4;
|
|
}
|
|
|
|
#region Storage
|
|
public override int GetBoxWallpaper(int box)
|
|
{
|
|
if ((uint)box >= 18)
|
|
return 0;
|
|
return Storage[GetBoxWallpaperOffset(box)];
|
|
}
|
|
|
|
public override void SetBoxWallpaper(int box, int value)
|
|
{
|
|
if ((uint)box >= 18)
|
|
return;
|
|
Storage[GetBoxWallpaperOffset(box)] = (byte)value;
|
|
}
|
|
#endregion
|
|
|
|
public override IReadOnlyList<InventoryPouch> Inventory
|
|
{
|
|
get
|
|
{
|
|
var info = ItemStorage4DP.Instance;
|
|
InventoryPouch[] pouch =
|
|
{
|
|
new InventoryPouch4(InventoryType.Items, info, 999, 0x624),
|
|
new InventoryPouch4(InventoryType.KeyItems, info, 1, 0x8B8),
|
|
new InventoryPouch4(InventoryType.TMHMs, info, 99, 0x980),
|
|
new InventoryPouch4(InventoryType.MailItems, info, 999, 0xB10),
|
|
new InventoryPouch4(InventoryType.Medicine, info, 999, 0xB40),
|
|
new InventoryPouch4(InventoryType.Berries, info, 999, 0xBE0),
|
|
new InventoryPouch4(InventoryType.Balls, info, 999, 0xCE0),
|
|
new InventoryPouch4(InventoryType.BattleItems, info, 999, 0xD1C),
|
|
};
|
|
return pouch.LoadAll(General);
|
|
}
|
|
set => value.SaveAll(General);
|
|
}
|
|
|
|
// reverse crc32 polynomial, nice!
|
|
private const uint MysteryGiftDPSlotActive = 0xEDB88320;
|
|
|
|
public bool[] GetMysteryGiftDPSlotActiveFlags()
|
|
{
|
|
var span = General[(WondercardFlags + 0x100)..]; // skip over flags
|
|
bool[] active = new bool[GiftCountMax]; // 8 PGT, 3 PCD
|
|
for (int i = 0; i < active.Length; i++)
|
|
active[i] = ReadUInt32LittleEndian(span[(4*i)..]) == MysteryGiftDPSlotActive;
|
|
|
|
return active;
|
|
}
|
|
|
|
public void SetMysteryGiftDPSlotActiveFlags(ReadOnlySpan<bool> value)
|
|
{
|
|
if (value.Length != GiftCountMax)
|
|
return;
|
|
|
|
var span = General[(WondercardFlags + 0x100)..]; // skip over flags
|
|
for (int i = 0; i < value.Length; i++)
|
|
WriteUInt32LittleEndian(span[(4 * i)..], value[i] ? MysteryGiftDPSlotActive : 0);
|
|
}
|
|
|
|
public override MysteryGiftAlbum GiftAlbum
|
|
{
|
|
get => base.GiftAlbum;
|
|
set
|
|
{
|
|
base.GiftAlbum = value;
|
|
SetActiveGiftFlags(value.Gifts);
|
|
}
|
|
}
|
|
|
|
private void SetActiveGiftFlags(IReadOnlyList<MysteryGift> gifts)
|
|
{
|
|
var arr = new bool[gifts.Count];
|
|
for (int i = 0; i < arr.Length; i++)
|
|
arr[i] = !gifts[i].Empty;
|
|
SetMysteryGiftDPSlotActiveFlags(arr);
|
|
}
|
|
|
|
public override int M { get => ReadUInt16LittleEndian(General[0x1238..]); set => WriteUInt16LittleEndian(General[0x1238..], (ushort)value); }
|
|
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
|
|
{
|
|
get => General.Slice(0x25A8, MaxStringLengthOT * 2);
|
|
set { if (value.Length == MaxStringLengthOT * 2) value.CopyTo(General[0x25A8..]); }
|
|
}
|
|
|
|
public override int X2 { get => ReadUInt16LittleEndian(General[0x25FA..]); set => WriteUInt16LittleEndian(General[0x25FA..], (ushort)value); }
|
|
public override int Y2 { get => ReadUInt16LittleEndian(General[0x25FE..]); set => WriteUInt16LittleEndian(General[0x25FE..], (ushort)value); }
|
|
public override int Z { get => ReadUInt16LittleEndian(General[0x2602..]); set => WriteUInt16LittleEndian(General[0x2602..], (ushort)value); }
|
|
|
|
public override uint SafariSeed { get => ReadUInt32LittleEndian(General[0x53C4..]); set => WriteUInt32LittleEndian(General[0x53C4..], value); }
|
|
public override uint SwarmSeed { get => ReadUInt32LittleEndian(General[0x53C8..]); set => WriteUInt32LittleEndian(General[0x53C8..], value); }
|
|
public override uint SwarmMaxCountModulo => 28;
|
|
|
|
protected override ReadOnlySpan<ushort> TreeSpecies =>new ushort[]
|
|
{
|
|
000, 000, 000, 000, 000, 000,
|
|
265, 266, 415, 412, 420, 190,
|
|
415, 412, 420, 190, 214, 265,
|
|
446, 446, 446, 446, 446, 446,
|
|
};
|
|
|
|
public Roamer4 RoamerMesprit => GetRoamer(0);
|
|
public Roamer4 RoamerCresselia => GetRoamer(1);
|
|
public Roamer4 RoamerUnused => GetRoamer(2); // Darkrai
|
|
|
|
private Roamer4 GetRoamer(int index)
|
|
{
|
|
const int size = Roamer4.SIZE;
|
|
var ofs = 0x73A0 + (index * size);
|
|
var mem = GeneralBuffer.Slice(ofs, size);
|
|
return new Roamer4(mem);
|
|
}
|
|
}
|