forked from kwsch/PKHeX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSAV7.cs
277 lines (240 loc) · 12 KB
/
SAV7.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary>
/// Generation 7 <see cref="SaveFile"/> object.
/// </summary>
public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin, IGameSync, IEventFlag37
{
// Save Data Attributes
protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}";
public override string Extension => string.Empty;
public override IReadOnlyList<string> PKMExtensions => Array.FindAll(PKM.Extensions, f =>
{
int gen = f[^1] - 0x30;
return gen <= 7 && f[1] != 'b'; // ignore PB7
});
protected SAV7(byte[] data, int biOffset) : base(data, biOffset)
{
}
protected SAV7(int size, int biOffset) : base(size, biOffset)
{
}
protected void ReloadBattleTeams()
{
var demo = this is SAV7SM && new ReadOnlySpan<byte>(Data, BoxLayout.Offset, 0x4C4).IsRangeEmpty(); // up to Battle Box values
if (demo || !State.Exportable)
{
BoxLayout.ClearBattleTeams();
}
else // Valid slot locking info present
{
BoxLayout.LoadBattleTeams();
}
}
#region Blocks
public abstract MyItem Items { get; }
public abstract MysteryBlock7 MysteryGift { get; }
public abstract PokeFinder7 PokeFinder { get; }
public abstract JoinFesta7 Festa { get; }
public abstract Daycare7 Daycare { get; }
public abstract RecordBlock6 Records { get; }
public abstract PlayTime6 Played { get; }
public abstract MyStatus7 MyStatus { get; }
public abstract FieldMoveModelSave7 Overworld { get; }
public abstract Situation7 Situation { get; }
public abstract ConfigSave7 Config { get; }
public abstract GameTime7 GameTime { get; }
public abstract Misc7 Misc { get; }
public abstract Zukan7 Zukan { get; }
public abstract BoxLayout7 BoxLayout { get; }
public abstract BattleTree7 BattleTree { get; }
public abstract ResortSave7 ResortSave { get; }
public abstract FieldMenu7 FieldMenu { get; }
public abstract FashionBlock7 Fashion { get; }
public abstract HallOfFame7 Fame { get; }
#endregion
// Configuration
protected override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
protected override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
public override PKM BlankPKM => new PK7();
public override Type PKMType => typeof(PK7);
public override int BoxCount => 32;
public override int MaxEV => 252;
public override int Generation => 7;
public override EntityContext Context => EntityContext.Gen7;
protected override int GiftCountMax => 48;
protected override int GiftFlagMax => 0x100 * 8;
public abstract int EventFlagCount { get; }
public int EventWorkCount => 1000;
private int EventWork => AllBlocks[05].Offset;
private int EventFlag => EventWork + (EventWorkCount * 2); // After Event Const (u16)*n
public override int OTLength => 12;
public override int NickLength => 12;
public override int MaxBallID => Legal.MaxBallID_7; // 26
public override int MaxGameID => Legal.MaxGameID_7;
protected override PKM GetPKM(byte[] data) => new PK7(data);
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray6(data);
// Feature Overrides
// Blocks & Offsets
private const int MemeCryptoBlock = 36;
protected void ClearMemeCrypto()
{
new byte[0x80].CopyTo(Data, AllBlocks[MemeCryptoBlock].Offset + 0x100);
}
protected override byte[] GetFinalData()
{
BoxLayout.SaveBattleTeams();
SetChecksums();
var result = MemeCrypto.Resign7(Data);
Debug.Assert(result != Data);
return result;
}
public override GameVersion Version => Game switch
{
(int)GameVersion.SN => GameVersion.SN,
(int)GameVersion.MN => GameVersion.MN,
(int)GameVersion.US => GameVersion.US,
(int)GameVersion.UM => GameVersion.UM,
_ => GameVersion.Invalid,
};
public sealed override string GetString(ReadOnlySpan<byte> data) => StringConverter7.GetString(data);
public sealed override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
{
return StringConverter7.SetString(destBuffer, value, maxLength, Language, option);
}
// Player Information
public override int TID { get => MyStatus.TID; set => MyStatus.TID = value; }
public override int SID { get => MyStatus.SID; set => MyStatus.SID = value; }
public override int Game { get => MyStatus.Game; set => MyStatus.Game = value; }
public override int Gender { get => MyStatus.Gender; set => MyStatus.Gender = value; }
public int GameSyncIDSize => MyStatus7.GameSyncIDSize; // 64 bits
public string GameSyncID { get => MyStatus.GameSyncID; set => MyStatus.GameSyncID = value; }
public byte Region { get => MyStatus.Region; set => MyStatus.Region = value; }
public byte Country { get => MyStatus.Country; set => MyStatus.Country = value; }
public byte ConsoleRegion { get => MyStatus.ConsoleRegion; set => MyStatus.ConsoleRegion = value; }
public override int Language { get => MyStatus.Language; set => MyStatus.Language = value; }
public override string OT { get => MyStatus.OT; set => MyStatus.OT = value; }
public int MultiplayerSpriteID { get => MyStatus.MultiplayerSpriteID; set => MyStatus.MultiplayerSpriteID = value; }
public override uint Money { get => Misc.Money; set => Misc.Money = value; }
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = value; }
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = value; }
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = value; }
public override uint SecondsToStart { get => GameTime.SecondsToStart; set => GameTime.SecondsToStart = value; }
public override uint SecondsToFame { get => GameTime.SecondsToFame; set => GameTime.SecondsToFame = value; }
// Stat Records
public int RecordCount => 200;
public int GetRecord(int recordID) => Records.GetRecord(recordID);
public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value);
public int GetRecordMax(int recordID) => Records.GetRecordMax(recordID);
public int GetRecordOffset(int recordID) => Records.GetRecordOffset(recordID);
// Inventory
public override IReadOnlyList<InventoryPouch> Inventory { get => Items.Inventory; set => Items.Inventory = value; }
// Storage
public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot);
public override int GetBoxOffset(int box) => Box + (SIZE_STORED * box * 30);
protected override int GetBoxWallpaperOffset(int box) => BoxLayout.GetBoxWallpaperOffset(box);
public override int GetBoxWallpaper(int box) => BoxLayout.GetBoxWallpaper(box);
public override void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value);
public override string GetBoxName(int box) => BoxLayout[box];
public override void SetBoxName(int box, string value) => BoxLayout[box] = value;
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; }
public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = value; }
public override byte[] BoxFlags { get => BoxLayout.BoxFlags; set => BoxLayout.BoxFlags = value; }
protected override void SetPKM(PKM pk, bool isParty = false)
{
PK7 pk7 = (PK7)pk;
// Apply to this Save File
int CT = pk7.CurrentHandler;
DateTime Date = DateTime.Now;
pk7.Trade(this, Date.Day, Date.Month, Date.Year);
if (CT != pk7.CurrentHandler) // Logic updated Friendship
{
// Copy over the Friendship Value only under certain circumstances
if (pk7.HasMove(216)) // Return
pk7.CurrentFriendship = pk7.OppositeFriendship;
else if (pk7.HasMove(218)) // Frustration
pk.CurrentFriendship = pk7.OppositeFriendship;
}
pk7.FormArgumentElapsed = pk7.FormArgumentMaximum = 0;
pk7.FormArgumentRemain = (byte)GetFormArgument(pk);
pk.RefreshChecksum();
AddCountAcquired(pk);
}
private void AddCountAcquired(PKM pk)
{
Records.AddRecord(pk.WasEgg ? 008 : 006); // egg, capture
if (pk.CurrentHandler == 1)
Records.AddRecord(011); // trade
if (!pk.WasEgg)
{
Records.AddRecord(004); // wild encounters
Records.AddRecord(042); // balls used
}
}
private static uint GetFormArgument(PKM pk)
{
if (pk.Form == 0)
return 0;
// Gen7 allows forms to be stored in the box with the current duration & form
// Just cap out the form duration anyways
return pk.Species switch
{
(int)Species.Furfrou => 5u, // Furfrou
(int)Species.Hoopa => 3u, // Hoopa
_ => 0u,
};
}
protected override void SetDex(PKM pk) => Zukan.SetDex(pk);
public override bool GetCaught(ushort species) => Zukan.GetCaught(species);
public override bool GetSeen(ushort species) => Zukan.GetSeen(species);
public override int PartyCount
{
get => Data[Party + (6 * SIZE_PARTY)];
protected set => Data[Party + (6 * SIZE_PARTY)] = (byte)value;
}
public override StorageSlotSource GetSlotFlags(int index)
{
int team = Array.IndexOf(TeamSlots, index);
if (team < 0)
return StorageSlotSource.None;
team /= 6;
var result = (StorageSlotSource)((int)StorageSlotSource.BattleTeam1 << team);
if (BoxLayout.GetIsTeamLocked(team))
result |= StorageSlotSource.Locked;
return result;
}
private int FusedCount => this is SAV7USUM ? 3 : 1;
public int GetFusedSlotOffset(int slot)
{
if ((uint)slot >= FusedCount)
return -1;
return AllBlocks[08].Offset + (PokeCrypto.SIZE_6PARTY * slot); // 0x104*slot
}
public override int DaycareSeedSize => Daycare7.DaycareSeedSize; // 128 bits
public override int GetDaycareSlotOffset(int loc, int slot) => Daycare.GetDaycareSlotOffset(slot);
public override bool? IsDaycareOccupied(int loc, int slot) => Daycare.GetIsOccupied(slot);
public override string GetDaycareRNGSeed(int loc) => Daycare.RNGSeed;
public override bool? IsDaycareHasEgg(int loc) => Daycare.HasEgg;
public override void SetDaycareOccupied(int loc, int slot, bool occupied) => Daycare.SetOccupied(slot, occupied);
public override void SetDaycareRNGSeed(int loc, string seed) => Daycare.RNGSeed = seed;
public override void SetDaycareHasEgg(int loc, bool hasEgg) => Daycare.HasEgg = hasEgg;
protected override bool[] MysteryGiftReceivedFlags { get => MysteryGift.MysteryGiftReceivedFlags; set => MysteryGift.MysteryGiftReceivedFlags = value; }
protected override DataMysteryGift[] MysteryGiftCards { get => MysteryGift.MysteryGiftCards; set => MysteryGift.MysteryGiftCards = value; }
public bool GetEventFlag(int flagNumber)
{
if ((uint)flagNumber >= EventFlagCount)
throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to get ({flagNumber}) is greater than max ({EventFlagCount}).");
return GetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7);
}
public void SetEventFlag(int flagNumber, bool value)
{
if ((uint)flagNumber >= EventFlagCount)
throw new ArgumentOutOfRangeException(nameof(flagNumber), $"Event Flag to set ({flagNumber}) is greater than max ({EventFlagCount}).");
SetFlag(EventFlag + (flagNumber >> 3), flagNumber & 7, value);
}
public ushort GetWork(int index) => ReadUInt16LittleEndian(Data.AsSpan(EventWork + (index * 2)));
public void SetWork(int index, ushort value) => WriteUInt16LittleEndian(Data.AsSpan(EventWork)[(index * 2)..], value);
}