Add more savedata abstractions

Array sizes for the top level structures are now noted, should make it easier to determine the shape of them & offsets.
This commit is contained in:
Kurt 2021-11-25 16:38:58 -08:00
parent 5b04a10fa3
commit 95bdde69a7
6 changed files with 65 additions and 36 deletions

View file

@ -21,10 +21,10 @@ namespace PKHeX.Core
Work = new FlagWork8b(this, 0x00004);
Items = new MyItem8b(this, 0x0563C);
Underground = new UndergroundItemList8b(this, 0x111BC);
// saveItemShortcut
// saveItemShortcut; ushort[4]
PartyInfo = new Party8b(this, 0x14098);
BoxLayout = new BoxLayout8b(this, 0x148AA);
// Box[]
// Box[40]
// PLAYER_DATA:
Config = new ConfigSave8b(this, 0x79B74); // size: 0x40
@ -37,28 +37,29 @@ namespace PKHeX.Core
// 0x7E9F8 - Menu selections (TopMenuItemTypeInt32, bool IsNew)[8], TopMenuItemTypeInt32 LastSelected
// 0x7EA3C - _FIELDOBJ_SAVE Objects[1000] (sizeof (0x44, 17 int fields), total size 0x109A0
Records = new Record8b(this, 0x8F3DC); // size: 0x78
// 0x8F454 - ENC_SV_DATA
// 0x8F454 - ENC_SV_DATA; 21 honey trees, 3 sway grass info, 2 mvpoke
// PLAYER_SAVE_DATA
// SaveBallDecoData
// SaveBallDecoData CapsuleData[99], AffixSealData[20]
SealList = new SealList8b(this, 0x93E0C); // size: 0x960 SaveSealData[200]
// _RANDOM_GROUP
// FIeldGimmickSaveData; int[3] gearRotate
BerryTrees = new BerryTreeGrowSave8b(this, 0x94DA8); // size: 0x808
Poffins = new PoffinSaveData8b(this, 0x955B0); // size: 0x644
BattleTower = new BattleTowerWork8b(this, 0x95BF4); // size: 0x1B8
System = new SystemData8b(this, 0x95DAC);
Poketch = new Poketch8b(this, 0); // todo
Daycare = new Daycare8b(this, 0x96080); // 0x2C0
// 0x96340 - _DENDOU_SAVEDATA
// BadgeSaveData
// BoukenNote
// TV_DATA
// 0x96340 - _DENDOU_SAVEDATA; DENDOU_RECORD[30], POKEMON_DATA_INSIDE[6], ushort[4] ?
// BadgeSaveData; byte[8]
// BoukenNote; byte[24]
// TV_DATA (int[48], TV_STR_DATA[42]), (int[37], bool[37])*2, (int[8], int[8]), TV_STR_DATA[10]; 144 128bit zeroed (900 bytes?)?
UgSaveData = new UgSaveData8b(this, 0x9A89C); // size: 0x27A0
// 0x9D03C - GMS_DATA // size: 0x31304
// 0xCE340 - PLAYER_NETWORK_DATA
// 0x9D03C - GMS_DATA // size: 0x31304, (GMS_POINT_DATA[650], ushort, ushort, byte)?; substructure GMS_POINT_HISTORY_DATA[5]
// 0xCE340 - PLAYER_NETWORK_DATA; bcatFlagArray byte[1300]
// UnionSaveData
// CON_PHOTO_LANG_DATA -- contest photo language data
// ZUKAN_PERSONAL_RND_DATA
// CON_PHOTO_EXT_DATA[]
// CON_PHOTO_LANG_DATA -- contest photo language data; photo_data[5], photo_fx[5]
// ZUKAN_PERSONAL_RND_DATA -- Spinda PID storage; uint[4] see, uint[4] get, uint[17] reserve
// CON_PHOTO_EXT_DATA[5]
// GMS_POINT_HISTORY_EXT_DATA[]
// UgCountRecord
// ReBuffnameData
@ -66,12 +67,11 @@ namespace PKHeX.Core
// v1.1 additions
// 0xE9828 -- RECORD_ADD_DATA: 0x30-sized[12] (0x120 bytes)
// MysteryGiftSaveData
// ZUKAN_PERSONAL_RND_DATA -- Spinda PID storage (17 * 2)
// POKETCH_POKETORE_COUNT_ARRAY -- (u16 species, u16 unused, i32 count, i32 reserved, i32 reserved) = 0x10bytes
// PLAYREPORT_DATA -- reporting player progress online?
// MysteryGiftSaveData, RecvData[50], byte[0x100] receiveFlag, OneDayData[10], uint[66] reserve
// POKETCH_POKETORE_COUNT_ARRAY -- (u16 species, u16 unused, i32 count, i32 reserved, i32 reserved)[3] = 0x10bytes
// PLAYREPORT_DATA -- reporting player progress online? 248 bytes?
// MT_DATA mtData; -- 0x400 bytes
// DENDOU_SAVE_ADD -- language tracking of members (hall of fame?)
// DENDOU_SAVE_ADD -- language tracking of members (hall of fame?); ADD_POKE_MEMBER[30], ADD_POKE[6]
Initialize();
}
@ -259,7 +259,7 @@ namespace PKHeX.Core
set => BitConverter.GetBytes(value).CopyTo(Data, 0x5634);
}
public float TimeScale
public float TimeScale // default 1440.0f
{
get => BitConverter.ToSingle(Data, 0x5638);
set => BitConverter.GetBytes(value).CopyTo(Data, 0x5638);

View file

@ -13,6 +13,15 @@ namespace PKHeX.Core
public const int ItemSaveSize = 3000;
public MyItem8b(SAV8BS sav, int offset) : base(sav) => Offset = offset;
public void SetItemQuantity(ushort item, int quantity)
{
var ofs = InventoryPouch8b.GetItemOffset(item, Offset);
var data = InventoryPouch8b.ReadItem(item, Data, Offset);
data.Count = quantity;
InventoryPouch8b.WriteItem(data, Data, ofs);
}
public override IReadOnlyList<InventoryPouch> Inventory { get => ConvertToPouches(); set => LoadFromPouches(value); }
private IReadOnlyList<InventoryPouch> ConvertToPouches()

View file

@ -17,7 +17,7 @@ namespace PKHeX.Core
// int CookingCount;
// 0x640 bytes of data is for poffins
public const int COUNT_POFFIN = 50;
public const int COUNT_POFFIN = 50; // 100?
public const int SIZE_POFFIN = 0x20;
public int CookingCount { get => BitConverter.ToInt32(Data, Offset + 0x640); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x640); }

View file

@ -9,12 +9,12 @@ namespace PKHeX.Core
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class Poketch8b : SaveBlock
{
public const int APP_REGIST_MAX = 20;
public const int POKETCH_MAP_MARK_MAX = 6;
public const int POKETCH_DOTART_DATA_BYTESIZE = 192;
public const int POKETCH_POKE_HISTORY_COUNT_MAX = 12;
public const int APP_REGIST_MAX = 20; // bool array unlock flags
public const int POKETCH_MAP_MARK_MAX = 6; // mark_map_pos[6]
public const int POKETCH_DOTART_DATA_BYTESIZE = 192; // dotart data bytes
public const int POKETCH_POKE_HISTORY_COUNT_MAX = 12; // poke_history[12]
public const int POKETCH_PEDOMETER_MAX = 99999;
public const int POKETCH_CALENDER_MONTH_MAX = 12;
public const int POKETCH_CALENDER_MONTH_MAX = 12; // calendar markbit uint[12]
public Poketch8b(SAV8BS sav, int offset) : base(sav) => Offset = offset;

View file

@ -12,8 +12,8 @@ namespace PKHeX.Core
{
public const int COUNT_DIGPOINTS = 10;
public const int COUNT_ENCOUNTERS = 15;
public const int COUNT_FRIENDS = 50; // 100 total with Friends+Others, not sure if it's evenly split 50/50, or if this is actually correct.
public const int COUNT_OTHERS = 50;
public const int COUNT_FRIENDS = 100; // 100 total with Friends+Others, savedata ctor allocates 100 for FriendPlayerList
public const int COUNT_OTHERS = 0; // unused
public const int COUNT_TRAINERS = 100;
public const int SIZE_SECRETBASE = 0x14 + (MAX_STONE_STATUE * SIZE_STATUE) + 4; // 0x270
@ -31,8 +31,8 @@ namespace PKHeX.Core
// int ReturnUgZoneID; // 0x1528
// UGRecord ugRecord; (secret base, see below documentation), size 0x270
// UgPlayerInfo[???] FriendPlayerList;
// UgPlayerInfo[???] OtherPlayerList;
// UgPlayerInfo[100] FriendPlayerList;
// UgPlayerInfo[0] OtherPlayerList; // unused
// byte[100] TalkedNPCsID;
private const int OFS_DIGPOINT = 0x24;
@ -107,6 +107,21 @@ namespace PKHeX.Core
public void ClearNPC() => GetTrainers().Fill(0);
public void ClearNPC(int start, int count = COUNT_TRAINERS) => FillNPC(0, start, count);
public int TalkedNPC // Spiritomb Trainer Count (duplicates OK)
{
get
{
var tr = GetTrainers();
int ctr = 0;
foreach (var t in tr)
{
if (t != 0)
ctr++;
}
return ctr;
}
}
public void FillNPC(byte value, int start = 0, int count = COUNT_TRAINERS)
{
if ((uint)start + (uint)count > COUNT_TRAINERS)

View file

@ -21,14 +21,10 @@ namespace PKHeX.Core
foreach (var index in LegalItems)
{
var ofs = GetItemOffset(index, Offset);
var count = BitConverter.ToInt32(data, ofs);
if (count == 0)
var item = ReadItem(index, data, ofs);
if (item.Count == 0)
continue;
bool isNew = BitConverter.ToInt32(data, ofs + 4) == 0;
bool isFavorite = BitConverter.ToInt32(data, ofs + 0x8) == 1;
// ushort sortOrder = BitConverter.ToUInt16(data, ofs + 0xE);
Items[ctr++] = new InventoryItem { Index = index, Count = count, New = isNew, FreeSpace = isFavorite };
Items[ctr++] = item;
}
while (ctr != LegalItems.Length)
@ -73,6 +69,15 @@ namespace PKHeX.Core
public static int GetItemOffset(ushort index, int baseOffset) => baseOffset + (SIZE_ITEM * index);
public static InventoryItem ReadItem(ushort index, byte[] data, int ofs)
{
var count = BitConverter.ToInt32(data, ofs);
bool isNew = BitConverter.ToInt32(data, ofs + 4) == 0;
bool isFavorite = BitConverter.ToInt32(data, ofs + 0x8) == 1;
// ushort sortOrder = BitConverter.ToUInt16(data, ofs + 0xE);
return new InventoryItem { Index = index, Count = count, New = isNew, FreeSpace = isFavorite };
}
public static void WriteItem(InventoryItem item, byte[] data, int ofs)
{
BitConverter.GetBytes((uint)item.Count).CopyTo(data, ofs);