mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-18 14:28:33 +00:00
Add sav7b object & detection util
This commit is contained in:
parent
f62e3f43b3
commit
13e1debe3e
10 changed files with 859 additions and 5 deletions
|
@ -39,7 +39,27 @@ namespace PKHeX.Core
|
||||||
blockInfoOffset = data.Length - 0x200 + 0x10;
|
blockInfoOffset = data.Length - 0x200 + 0x10;
|
||||||
if (BitConverter.ToUInt32(data, blockInfoOffset) != SaveUtil.BEEF)
|
if (BitConverter.ToUInt32(data, blockInfoOffset) != SaveUtil.BEEF)
|
||||||
blockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
blockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
||||||
int count = (data.Length - blockInfoOffset - 0x8) / 8;
|
int len = data.Length;
|
||||||
|
return GetBlockInfo(data, ref blockInfoOffset, CheckFunc, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="BlockInfo"/> table for the input <see cref="data"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Complete data array</param>
|
||||||
|
/// <param name="blockInfoOffset">Offset the <see cref="BlockInfo"/> starts at.</param>
|
||||||
|
/// <param name="CheckFunc">Checksum method for validating each block.</param>
|
||||||
|
/// <param name="dataLength"></param>
|
||||||
|
public static BlockInfo[] GetBlockInfoData(byte[] data, ref int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc, int dataLength)
|
||||||
|
{
|
||||||
|
if (BitConverter.ToUInt32(data, blockInfoOffset) != SaveUtil.BEEF)
|
||||||
|
blockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
||||||
|
return GetBlockInfo(data, ref blockInfoOffset, CheckFunc, dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockInfo[] GetBlockInfo(byte[] data, ref int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc, int dataLength)
|
||||||
|
{
|
||||||
|
int count = (dataLength - blockInfoOffset - 0x8) / 8;
|
||||||
blockInfoOffset += 4;
|
blockInfoOffset += 4;
|
||||||
|
|
||||||
var Blocks = new BlockInfo[count];
|
var Blocks = new BlockInfo[count];
|
||||||
|
|
217
PKHeX.Core/Saves/SAV7b.cs
Normal file
217
PKHeX.Core/Saves/SAV7b.cs
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class SAV7b : SaveFile
|
||||||
|
{
|
||||||
|
protected override string BAKText => $"{OT} ({Version}) - {Played.LastSavedTime}";
|
||||||
|
public override string Filter => "Main SAV|*.*";
|
||||||
|
public override string Extension => string.Empty;
|
||||||
|
public override string[] PKMExtensions => PKM.Extensions.Where(f => f[0] == 'B' && 7 == f[f.Length - 1] - 0x30).ToArray();
|
||||||
|
|
||||||
|
public override Type PKMType => typeof(PB7);
|
||||||
|
public override PKM BlankPKM => new PB7();
|
||||||
|
public override int SIZE_STORED => SIZE_PARTY;
|
||||||
|
protected override int SIZE_PARTY => 260;
|
||||||
|
|
||||||
|
public override SaveFile Clone() => new SAV7b(Data);
|
||||||
|
|
||||||
|
public SAV7b() : this(new byte[SaveUtil.SIZE_G7GG]) { }
|
||||||
|
|
||||||
|
public SAV7b(byte[] data)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
BAK = (byte[])Data.Clone();
|
||||||
|
Exportable = !IsRangeEmpty(0, Data.Length);
|
||||||
|
|
||||||
|
// Load Info
|
||||||
|
const int len = 0xB8800; // 1mb always allocated
|
||||||
|
BlockInfoOffset = len - 0x1F0;
|
||||||
|
Blocks = BlockInfo3DS.GetBlockInfoData(Data, ref BlockInfoOffset, SaveUtil.CRC16NoInvert, len);
|
||||||
|
Personal = PersonalTable.GG;
|
||||||
|
|
||||||
|
Box = GetBlockOffset(BelugaBlockIndex.PokeListPokemon);
|
||||||
|
Party = GetBlockOffset(BelugaBlockIndex.PokeListPokemon);
|
||||||
|
EventFlag = GetBlockOffset(BelugaBlockIndex.EventWork);
|
||||||
|
PokeDex = GetBlockOffset(BelugaBlockIndex.Zukan);
|
||||||
|
Zukan = new Zukan7b(this, PokeDex, 0x550);
|
||||||
|
Config = new ConfigSave7b(this);
|
||||||
|
Items = new MyItem7b(this);
|
||||||
|
Storage = new PokeListHeader(this);
|
||||||
|
Status = new MyStatus7b(this);
|
||||||
|
Played = new PlayTime7b(this);
|
||||||
|
Misc = new Misc7b(this);
|
||||||
|
EventWork = new EventWork7b(this);
|
||||||
|
|
||||||
|
HeldItems = Legal.HeldItems_GG;
|
||||||
|
|
||||||
|
if (Exportable)
|
||||||
|
CanReadChecksums();
|
||||||
|
else
|
||||||
|
ClearBoxes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save Block accessors
|
||||||
|
public readonly MyItem Items;
|
||||||
|
public readonly Misc7b Misc;
|
||||||
|
public readonly Zukan7b Zukan;
|
||||||
|
public readonly MyStatus7b Status;
|
||||||
|
public readonly PlayTime7b Played;
|
||||||
|
public readonly ConfigSave7b Config;
|
||||||
|
public readonly EventWork7b EventWork;
|
||||||
|
public readonly PokeListHeader Storage;
|
||||||
|
|
||||||
|
public override InventoryPouch[] Inventory { get => Items.Inventory; set => Items.Inventory = value; }
|
||||||
|
|
||||||
|
// Feature Overrides
|
||||||
|
public override int Generation => 7;
|
||||||
|
public override int MaxMoveID => Legal.MaxMoveID_7b;
|
||||||
|
public override int MaxSpeciesID => Legal.MaxSpeciesID_7b;
|
||||||
|
public override int MaxItemID => Legal.MaxItemID_7b;
|
||||||
|
public override int MaxBallID => Legal.MaxBallID_7b;
|
||||||
|
public override int MaxGameID => Legal.MaxGameID_7b;
|
||||||
|
public override int MaxAbilityID => Legal.MaxAbilityID_7b;
|
||||||
|
|
||||||
|
public override int MaxIV => 31;
|
||||||
|
public override int MaxEV => 252;
|
||||||
|
public override int OTLength => 12;
|
||||||
|
public override int NickLength => 12;
|
||||||
|
protected override int GiftCountMax => 48;
|
||||||
|
protected override int GiftFlagMax => 0x100 * 8;
|
||||||
|
protected override int EventFlagMax => 4160; // 0xDC0 (true max may be up to 0x7F less. 23A8 starts u64 hashvals)
|
||||||
|
protected override int EventConstMax => 1000;
|
||||||
|
|
||||||
|
public override bool HasParty => false; // handled via team slots
|
||||||
|
public override bool HasEvents => true; // advanced!
|
||||||
|
|
||||||
|
// BoxSlotCount => 1000 -- why couldn't this be a multiple of 30...
|
||||||
|
public override int BoxSlotCount => 25;
|
||||||
|
public override int BoxCount => 40; // 1000/25
|
||||||
|
|
||||||
|
// Blocks & Offsets
|
||||||
|
private readonly int BlockInfoOffset;
|
||||||
|
private readonly BlockInfo[] Blocks;
|
||||||
|
public override bool ChecksumsValid => CanReadChecksums() && Blocks.GetChecksumsValid(Data);
|
||||||
|
public override string ChecksumInfo => CanReadChecksums() ? Blocks.GetChecksumInfo(Data) : string.Empty;
|
||||||
|
|
||||||
|
public BlockInfo GetBlock(BelugaBlockIndex index) => Blocks[(int)index];
|
||||||
|
public int GetBlockOffset(BelugaBlockIndex index) => Blocks[(int)index].Offset;
|
||||||
|
|
||||||
|
private bool CanReadChecksums()
|
||||||
|
{
|
||||||
|
if (Blocks.Length <= 3)
|
||||||
|
{ Debug.WriteLine($"Not enough blocks ({Blocks.Length}), aborting {nameof(CanReadChecksums)}"); return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetChecksums()
|
||||||
|
{
|
||||||
|
if (!CanReadChecksums())
|
||||||
|
return;
|
||||||
|
Blocks.SetChecksums(Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FixPreWrite() => Storage.CompressStorage();
|
||||||
|
|
||||||
|
protected override void SetPKM(PKM pkm)
|
||||||
|
{
|
||||||
|
var pk = (PB7)pkm;
|
||||||
|
// Apply to this Save File
|
||||||
|
int CT = pk.CurrentHandler;
|
||||||
|
var Date = DateTime.Now;
|
||||||
|
pk.Trade(OT, TID, SID, Country, SubRegion, Gender, false, Date.Day, Date.Month, Date.Year);
|
||||||
|
if (CT != pk.CurrentHandler) // Logic updated Friendship
|
||||||
|
{
|
||||||
|
// Copy over the Friendship Value only under certain circumstances
|
||||||
|
if (pk.Moves.Contains(216)) // Return
|
||||||
|
pk.CurrentFriendship = pk.OppositeFriendship;
|
||||||
|
else if (pk.Moves.Contains(218)) // Frustration
|
||||||
|
pk.CurrentFriendship = pk.OppositeFriendship;
|
||||||
|
}
|
||||||
|
pk.RefreshChecksum();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetDex(PKM pkm) => Zukan.SetDex(pkm);
|
||||||
|
public override bool GetCaught(int species) => Zukan.GetCaught(species);
|
||||||
|
public override bool GetSeen(int species) => Zukan.GetSeen(species);
|
||||||
|
|
||||||
|
public override PKM GetPKM(byte[] data) => new PB7(data);
|
||||||
|
public override byte[] DecryptPKM(byte[] data) => PKX.DecryptArray(data);
|
||||||
|
public override int GetBoxOffset(int box) => Box + (box * BoxSlotCount * SIZE_STORED);
|
||||||
|
protected override IList<int>[] SlotPointers => new[] { Storage.PokeListInfo };
|
||||||
|
|
||||||
|
public override int GetPartyOffset(int slot) => Storage.GetPartyOffset(slot);
|
||||||
|
public override int PartyCount { get => Storage.PartyCount; protected set => Storage.PartyCount = value; }
|
||||||
|
public override bool IsSlotInBattleTeam(int box, int slot) => Storage.IsSlotInBattleTeam(box, slot);
|
||||||
|
public override bool IsSlotLocked(int box, int slot) => Storage.IsSlotLocked(box, slot);
|
||||||
|
protected override bool IsSlotOverwriteProtected(int box, int slot) => false;
|
||||||
|
|
||||||
|
public override string GetBoxName(int box) => $"Box {box + 1}";
|
||||||
|
public override void SetBoxName(int box, string value) { }
|
||||||
|
|
||||||
|
public override string GetString(int Offset, int Length) => StringConverter.GetString7(Data, Offset, Length);
|
||||||
|
|
||||||
|
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
|
||||||
|
{
|
||||||
|
if (PadToSize == 0)
|
||||||
|
PadToSize = maxLength + 1;
|
||||||
|
return StringConverter.SetString7(value, maxLength, Language, PadToSize, PadWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ulong? Secure1
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14);
|
||||||
|
set => BitConverter.GetBytes(value ?? 0).CopyTo(Data, BlockInfoOffset - 0x14);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ulong? Secure2
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0xC);
|
||||||
|
set => BitConverter.GetBytes(value ?? 0).CopyTo(Data, BlockInfoOffset - 0xC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override GameVersion Version
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (Game)
|
||||||
|
{
|
||||||
|
case (int)GameVersion.GP: return GameVersion.GP;
|
||||||
|
case (int)GameVersion.GE: return GameVersion.GE;
|
||||||
|
default: return GameVersion.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player Information
|
||||||
|
public override int TID { get => Status.TID; set => Status.TID = value; }
|
||||||
|
public override int SID { get => Status.SID; set => Status.SID = value; }
|
||||||
|
public override int Game { get => Status.Game; set => Status.Game = value; }
|
||||||
|
public override int Gender { get => Status.Gender; set => Status.Gender = value; }
|
||||||
|
public override int Language { get => Status.Language; set => Status.Language = value; }
|
||||||
|
public override string OT { get => Status.OT; set => Status.OT = 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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="bool"/> status of a desired Event Flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flagNumber">Event Flag to check</param>
|
||||||
|
/// <returns>Flag is Set (true) or not Set (false)</returns>
|
||||||
|
public override bool GetEventFlag(int flagNumber) => EventWork.GetFlag(flagNumber);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the <see cref="bool"/> status of a desired Event Flag
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flagNumber">Event Flag to check</param>
|
||||||
|
/// <param name="value">Event Flag status to set</param>
|
||||||
|
/// <remarks>Flag is Set (true) or not Set (false)</remarks>
|
||||||
|
public override void SetEventFlag(int flagNumber, bool value) => EventWork.SetFlag(flagNumber, value);
|
||||||
|
}
|
||||||
|
}
|
50
PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs
Normal file
50
PKHeX.Core/Saves/Substructures/Gen7/ConfigSave7b.cs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class ConfigSave7b : SaveBlock
|
||||||
|
{
|
||||||
|
public ConfigSave7b(SAV7b sav) : base(sav)
|
||||||
|
{
|
||||||
|
Offset = sav.GetBlockOffset(BelugaBlockIndex.ConfigSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ConfigValue
|
||||||
|
{
|
||||||
|
get => BitConverter.ToInt32(Data, Offset);
|
||||||
|
set => BitConverter.GetBytes(value).CopyTo(Data, Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TalkingSpeed
|
||||||
|
{
|
||||||
|
get => ConfigValue & 3;
|
||||||
|
set => ConfigValue = (ConfigValue & ~3) | (value & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BattleAnimationSetting BattleAnimation
|
||||||
|
{
|
||||||
|
// Effects OFF = 1, Effects ON = 0
|
||||||
|
get => (BattleAnimationSetting)((ConfigValue >> 2) & 1);
|
||||||
|
set => ConfigValue = (ConfigValue & ~(1 << 2)) | ((byte)value << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BattleStyleSetting BattleStyle
|
||||||
|
{
|
||||||
|
// SET = 1, SWITCH = 0
|
||||||
|
get => (BattleStyleSetting)((ConfigValue >> 3) & 1);
|
||||||
|
set => ConfigValue = (ConfigValue & ~(1 << 3)) | ((byte)value << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BattleAnimationSetting
|
||||||
|
{
|
||||||
|
EffectsON,
|
||||||
|
EffectsOFF,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BattleStyleSetting
|
||||||
|
{
|
||||||
|
SET,
|
||||||
|
SWITCH,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
183
PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs
Normal file
183
PKHeX.Core/Saves/Substructures/Gen7/EventWork7b.cs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class EventWork7b : SaveBlock, IEventWork<int>
|
||||||
|
{
|
||||||
|
public EventWork7b(SAV7b sav) : base(sav)
|
||||||
|
{
|
||||||
|
Offset = sav.GetBlockOffset(BelugaBlockIndex.EventWork);
|
||||||
|
// Zone @ 0x21A0 - 0x21AF (128 flags)
|
||||||
|
// System @ 0x21B0 - 0x21EF (512 flags) -- is this really 256 instead, with another 256 region after for the small vanish?
|
||||||
|
// Vanish @ 0x21F0 - 0x22AF (1536 flags)
|
||||||
|
// Event @ 0x22B0 - 0x23A7 (rest of the flags) (512) -- I think trainer flags are afterwards.... For now, this is a catch-all
|
||||||
|
|
||||||
|
// time flags (39 used flags of 42) = 6 bytes 0x22F0-0x22F5
|
||||||
|
// trainer flags (???) = 0x22F6 - end?
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overall Layout
|
||||||
|
private const int WorkCount = 1000;
|
||||||
|
private const int WorkSize = sizeof(int);
|
||||||
|
private const int FlagStart = WorkCount * WorkSize;
|
||||||
|
private const int FlagCount = EventFlagStart + EventFlagCount;
|
||||||
|
|
||||||
|
// Breakdown!
|
||||||
|
private const int ZoneWorkCount = 0x20; // 32
|
||||||
|
private const int SystemWorkCount = 0x80; // 128
|
||||||
|
private const int SceneWorkCount = 0x200; // 512
|
||||||
|
private const int EventWorkCount = 0x100; // 256
|
||||||
|
// private const int UnusedWorkCount = 72;
|
||||||
|
|
||||||
|
private const int ZoneWorkStart = 0;
|
||||||
|
private const int SystemWorkStart = ZoneWorkStart + ZoneWorkCount;
|
||||||
|
private const int SceneWorkStart = SystemWorkStart + SystemWorkCount;
|
||||||
|
private const int EventWorkStart = SceneWorkStart + SceneWorkCount;
|
||||||
|
|
||||||
|
private const int ZoneFlagCount = 0x80; // 128
|
||||||
|
private const int SystemFlagCount = 0x200; // 512
|
||||||
|
private const int VanishFlagCount = 0x600; // 1536
|
||||||
|
private const int EventFlagCount = 0x7C0; // 1984
|
||||||
|
|
||||||
|
private const int ZoneFlagStart = 0;
|
||||||
|
private const int SystemFlagStart = ZoneFlagStart + ZoneFlagCount;
|
||||||
|
private const int VanishFlagStart = SystemFlagStart + SystemFlagCount;
|
||||||
|
private const int EventFlagStart = VanishFlagStart + VanishFlagCount;
|
||||||
|
|
||||||
|
public int MaxFlag => FlagCount;
|
||||||
|
public int MaxWork => WorkCount;
|
||||||
|
|
||||||
|
public int GetWork(int index) => BitConverter.ToInt32(Data, Offset + (index * WorkSize));
|
||||||
|
public void SetWork(int index, int value) => BitConverter.GetBytes(value).CopyTo(Data, Offset + (index * WorkSize));
|
||||||
|
public int GetWork(EventVarType type, int index) => GetWork(GetWorkRawIndex(type, index));
|
||||||
|
public void SetWork(EventVarType type, int index, int value) => SetWork(GetWorkRawIndex(type, index), value);
|
||||||
|
public bool GetFlag(EventVarType type, int index) => GetFlag(GetFlagRawIndex(type, index));
|
||||||
|
public void SetFlag(EventVarType type, int index, bool value = true) => SetFlag(GetFlagRawIndex(type, index), value);
|
||||||
|
|
||||||
|
public int GetFlagRawIndex(EventVarType type, int index)
|
||||||
|
{
|
||||||
|
int max = GetFlagCount(type);
|
||||||
|
if ((uint)index > max)
|
||||||
|
throw new ArgumentException(nameof(index));
|
||||||
|
var start = GetFlagStart(type);
|
||||||
|
return start + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetWorkRawIndex(EventVarType type, int index)
|
||||||
|
{
|
||||||
|
int max = GetWorkCount(type);
|
||||||
|
if ((uint)index > max)
|
||||||
|
throw new ArgumentException(nameof(index));
|
||||||
|
var start = GetWorkStart(type);
|
||||||
|
return start + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetFlag(int index)
|
||||||
|
{
|
||||||
|
var offset = Offset + FlagStart + (index >> 3);
|
||||||
|
var current = Data[offset];
|
||||||
|
return (current & (1 << (index & 7))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFlag(int index, bool value = true)
|
||||||
|
{
|
||||||
|
var offset = Offset + FlagStart + (index >> 3);
|
||||||
|
var bit = 1 << (index & 7);
|
||||||
|
if (value)
|
||||||
|
Data[offset] |= (byte)bit;
|
||||||
|
else
|
||||||
|
Data[offset] &= (byte)~bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventVarType GetFlagType(int index, out int subIndex)
|
||||||
|
{
|
||||||
|
subIndex = index;
|
||||||
|
if (index < ZoneFlagCount)
|
||||||
|
return EventVarType.Zone;
|
||||||
|
subIndex -= ZoneFlagCount;
|
||||||
|
|
||||||
|
if (subIndex < SystemFlagCount)
|
||||||
|
return EventVarType.System;
|
||||||
|
subIndex -= SystemFlagCount;
|
||||||
|
|
||||||
|
if (subIndex < VanishFlagCount)
|
||||||
|
return EventVarType.Vanish;
|
||||||
|
subIndex -= VanishFlagCount;
|
||||||
|
|
||||||
|
if (subIndex < EventFlagCount)
|
||||||
|
return EventVarType.Event;
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventVarType GetWorkType(int index, out int subIndex)
|
||||||
|
{
|
||||||
|
subIndex = index;
|
||||||
|
if (subIndex < ZoneWorkCount)
|
||||||
|
return EventVarType.Zone;
|
||||||
|
subIndex -= ZoneWorkCount;
|
||||||
|
|
||||||
|
if (subIndex < SystemWorkCount)
|
||||||
|
return EventVarType.System;
|
||||||
|
subIndex -= SystemWorkCount;
|
||||||
|
|
||||||
|
if (subIndex < SceneWorkCount)
|
||||||
|
return EventVarType.Scene;
|
||||||
|
subIndex -= SceneWorkCount;
|
||||||
|
|
||||||
|
if (subIndex < EventWorkCount)
|
||||||
|
return EventVarType.Event;
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetFlagStart(EventVarType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EventVarType.Zone: return ZoneFlagStart;
|
||||||
|
case EventVarType.System: return SystemFlagStart;
|
||||||
|
case EventVarType.Vanish: return VanishFlagStart;
|
||||||
|
case EventVarType.Event: return EventFlagStart;
|
||||||
|
}
|
||||||
|
throw new ArgumentException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetWorkStart(EventVarType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EventVarType.Zone: return ZoneWorkStart;
|
||||||
|
case EventVarType.System: return SystemWorkStart;
|
||||||
|
case EventVarType.Scene: return SceneWorkStart;
|
||||||
|
case EventVarType.Event: return EventWorkStart;
|
||||||
|
}
|
||||||
|
throw new ArgumentException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetFlagCount(EventVarType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EventVarType.Zone: return ZoneFlagCount;
|
||||||
|
case EventVarType.System: return SystemFlagCount;
|
||||||
|
case EventVarType.Vanish: return VanishFlagCount;
|
||||||
|
case EventVarType.Event: return EventFlagCount;
|
||||||
|
}
|
||||||
|
throw new ArgumentException(nameof(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetWorkCount(EventVarType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EventVarType.Zone: return ZoneWorkCount;
|
||||||
|
case EventVarType.System: return SystemWorkCount;
|
||||||
|
case EventVarType.Scene: return SceneWorkCount;
|
||||||
|
case EventVarType.Event: return EventWorkCount;
|
||||||
|
}
|
||||||
|
throw new ArgumentException(nameof(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs
Normal file
27
PKHeX.Core/Saves/Substructures/Gen7/Misc7b.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class Misc7b : SaveBlock
|
||||||
|
{
|
||||||
|
private readonly SaveFile SAV;
|
||||||
|
|
||||||
|
public Misc7b(SaveFile sav) : base(sav)
|
||||||
|
{
|
||||||
|
SAV = sav;
|
||||||
|
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.Misc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Money
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt32(Data, Offset + 4);
|
||||||
|
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Rival
|
||||||
|
{
|
||||||
|
get => SAV.GetString(Offset + 0x200, 0x1A);
|
||||||
|
set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
PKHeX.Core/Saves/Substructures/Gen7/MyItem7b.cs
Normal file
43
PKHeX.Core/Saves/Substructures/Gen7/MyItem7b.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class MyItem7b : MyItem
|
||||||
|
{
|
||||||
|
private const int Medicine = 0x0000; // 0
|
||||||
|
private const int TM = 0x00F0; // 1
|
||||||
|
private const int Candy = 0x02A0; // 2
|
||||||
|
private const int PowerUp = 0x05C0; // 3
|
||||||
|
private const int Catching = 0x0818; // 4
|
||||||
|
private const int Battle = 0x08E0; // 5
|
||||||
|
private const int Key = 0x0B38; // 6
|
||||||
|
|
||||||
|
public MyItem7b(SaveFile SAV) : base(SAV) { }
|
||||||
|
|
||||||
|
public override InventoryPouch[] Inventory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var pouch = new InventoryPouch[]
|
||||||
|
{
|
||||||
|
new InventoryPouch7b(InventoryType.Medicine, Legal.Pouch_Medicine_GG, 999, Medicine, PouchSize7b.Medicine),
|
||||||
|
new InventoryPouch7b(InventoryType.TMHMs, Legal.Pouch_TM_GG, 1, TM, PouchSize7b.TM),
|
||||||
|
new InventoryPouch7b(InventoryType.Balls, Legal.Pouch_Catching_GG, 999, Catching, PouchSize7b.Catching),
|
||||||
|
new InventoryPouch7b(InventoryType.Items, Legal.Pouch_Regular_GG, 999, Key, PouchSize7b.Items),
|
||||||
|
new InventoryPouch7b(InventoryType.BattleItems, Legal.Pouch_Battle_GG, 999, Battle, PouchSize7b.Battle),
|
||||||
|
new InventoryPouch7b(InventoryType.ZCrystals, Legal.Pouch_PowerUp_GG, 999, PowerUp, PouchSize7b.PowerUp),
|
||||||
|
new InventoryPouch7b(InventoryType.FreeSpace, Legal.Pouch_Candy_GG, 999, Candy, PouchSize7b.Candy),
|
||||||
|
};
|
||||||
|
foreach (var p in pouch)
|
||||||
|
p.GetPouch(Data);
|
||||||
|
return pouch;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
foreach (var p in value)
|
||||||
|
{
|
||||||
|
((InventoryPouch7b)p).SanitizeCounts();
|
||||||
|
p.SetPouch(Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs
Normal file
67
PKHeX.Core/Saves/Substructures/Gen7/MyStatus7b.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class MyStatus7b : SaveBlock
|
||||||
|
{
|
||||||
|
private readonly SaveFile SAV;
|
||||||
|
|
||||||
|
public MyStatus7b(SaveFile sav) : base(sav)
|
||||||
|
{
|
||||||
|
SAV = sav;
|
||||||
|
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.MyStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player Information
|
||||||
|
|
||||||
|
// idb uint8 offset: 0x58
|
||||||
|
|
||||||
|
public int TID
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt16(Data, Offset + 0);
|
||||||
|
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SID
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt16(Data, Offset + 2);
|
||||||
|
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Game
|
||||||
|
{
|
||||||
|
get => Data[Offset + 4];
|
||||||
|
set => Data[Offset + 4] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Gender
|
||||||
|
{
|
||||||
|
get => Data[Offset + 5];
|
||||||
|
set => Data[Offset + 5] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Language
|
||||||
|
{
|
||||||
|
get => Data[Offset + 0x35];
|
||||||
|
set => Data[Offset + 0x35] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OT
|
||||||
|
{
|
||||||
|
get => SAV.GetString(Offset + 0x38, 0x1A);
|
||||||
|
set => SAV.SetString(value, SAV.OTLength).CopyTo(Data, Offset + 0x38);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MegaUnlocked
|
||||||
|
{
|
||||||
|
get => (Data[Offset + 0x6D] & 0x01) != 0;
|
||||||
|
set => Data[Offset + 0x6D] = (byte)((Data[Offset + 0x6D] & 0xFE) | (value ? 1 : 0)); // in battle
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ZMoveUnlocked
|
||||||
|
{
|
||||||
|
get => (Data[Offset + 0x6D] & 2) != 0;
|
||||||
|
set => Data[Offset + 0x6D] = (byte)((Data[Offset + 0x6D] & ~2) | (value ? 2 : 0)); // in battle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs
Normal file
67
PKHeX.Core/Saves/Substructures/Gen7/PlayTime7b.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
public sealed class PlayTime7b : SaveBlock
|
||||||
|
{
|
||||||
|
public PlayTime7b(SaveFile sav) : base(sav)
|
||||||
|
{
|
||||||
|
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.PlayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PlayedHours
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt16(Data, Offset);
|
||||||
|
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PlayedMinutes
|
||||||
|
{
|
||||||
|
get => Data[Offset + 2];
|
||||||
|
set => Data[Offset + 2] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PlayedSeconds
|
||||||
|
{
|
||||||
|
get => Data[Offset + 3];
|
||||||
|
set => Data[Offset + 3] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint LastSaved { get => BitConverter.ToUInt32(Data, Offset + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x4); }
|
||||||
|
private int LastSavedYear { get => (int)(LastSaved & 0xFFF); set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)value; }
|
||||||
|
private int LastSavedMonth { get => (int)(LastSaved >> 12 & 0xF); set => LastSaved = (LastSaved & 0xFFFF0FFF) | ((uint)value & 0xF) << 12; }
|
||||||
|
private int LastSavedDay { get => (int)(LastSaved >> 16 & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | ((uint)value & 0x1F) << 16; }
|
||||||
|
private int LastSavedHour { get => (int)(LastSaved >> 21 & 0x1F); set => LastSaved = (LastSaved & 0xFC1FFFFF) | ((uint)value & 0x1F) << 21; }
|
||||||
|
private int LastSavedMinute { get => (int)(LastSaved >> 26 & 0x3F); set => LastSaved = (LastSaved & 0x03FFFFFF) | ((uint)value & 0x3F) << 26; }
|
||||||
|
public string LastSavedTime => $"{LastSavedYear:0000}{LastSavedMonth:00}{LastSavedDay:00}{LastSavedHour:00}{LastSavedMinute:00}";
|
||||||
|
|
||||||
|
public DateTime? LastSavedDate
|
||||||
|
{
|
||||||
|
get => !Util.IsDateValid(LastSavedYear, LastSavedMonth, LastSavedDay)
|
||||||
|
? (DateTime?)null
|
||||||
|
: new DateTime(LastSavedYear, LastSavedMonth, LastSavedDay, LastSavedHour, LastSavedMinute, 0);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// Only update the properties if a value is provided.
|
||||||
|
if (value.HasValue)
|
||||||
|
{
|
||||||
|
var dt = value.Value;
|
||||||
|
LastSavedYear = dt.Year;
|
||||||
|
LastSavedMonth = dt.Month;
|
||||||
|
LastSavedDay = dt.Day;
|
||||||
|
LastSavedHour = dt.Hour;
|
||||||
|
LastSavedMinute = dt.Minute;
|
||||||
|
}
|
||||||
|
else // Clear the date.
|
||||||
|
{
|
||||||
|
// If code tries to access MetDate again, null will be returned.
|
||||||
|
LastSavedYear = 0;
|
||||||
|
LastSavedMonth = 0;
|
||||||
|
LastSavedDay = 0;
|
||||||
|
LastSavedHour = 0;
|
||||||
|
LastSavedMinute = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs
Normal file
150
PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace PKHeX.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Header information for the stored <see cref="PKM"/> data.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This block simply contains the following:
|
||||||
|
/// u16 Party Pointers * 6: Indicates which index occupies this slot. <see cref="SLOT_EMPTY"/> if nothing in slot.
|
||||||
|
/// u16 Follower Pointer: Indicates which index is following the player. <see cref="SLOT_EMPTY"/> if nothing following.
|
||||||
|
/// u16 List Count: Points to the next empty slot, and indicates how many slots are stored in the list.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed class PokeListHeader : SaveBlock
|
||||||
|
{
|
||||||
|
private readonly SaveFile SAV;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw representation of data, casted to ushort[].
|
||||||
|
/// </summary>
|
||||||
|
internal readonly int[] PokeListInfo;
|
||||||
|
|
||||||
|
private const int FOLLOW = 6;
|
||||||
|
private const int COUNT = 7;
|
||||||
|
private const int MAX_SLOTS = 1000;
|
||||||
|
private const int SLOT_EMPTY = 1001;
|
||||||
|
|
||||||
|
public PokeListHeader(SaveFile sav) : base(sav)
|
||||||
|
{
|
||||||
|
SAV = sav;
|
||||||
|
Offset = ((SAV7b)sav).GetBlockOffset(BelugaBlockIndex.PokeListHeader);
|
||||||
|
PokeListInfo = LoadPointerData();
|
||||||
|
PartyCount = PokeListInfo.Take(6).Count(z => z < MAX_SLOTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _partyCount;
|
||||||
|
|
||||||
|
public int PartyCount
|
||||||
|
{
|
||||||
|
get => _partyCount;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_partyCount > value)
|
||||||
|
{
|
||||||
|
for (int i = _partyCount; i < value; i++)
|
||||||
|
ClearPartySlot(i);
|
||||||
|
}
|
||||||
|
_partyCount = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ClearPartySlot(int slot)
|
||||||
|
{
|
||||||
|
if (slot >= 6 || PartyCount <= 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (slot > PartyCount)
|
||||||
|
{
|
||||||
|
slot = PartyCount;
|
||||||
|
}
|
||||||
|
else if (slot != PartyCount - 1)
|
||||||
|
{
|
||||||
|
int countShiftDown = PartyCount - 1 - slot;
|
||||||
|
Array.Copy(PokeListInfo, slot + 1, PokeListInfo, slot, countShiftDown);
|
||||||
|
slot = PartyCount - 1;
|
||||||
|
}
|
||||||
|
PokeListInfo[slot] = SLOT_EMPTY;
|
||||||
|
PartyCount--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveFollower() => FollowerIndex = SLOT_EMPTY;
|
||||||
|
|
||||||
|
public int FollowerIndex
|
||||||
|
{
|
||||||
|
get => PokeListInfo[FOLLOW];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if ((ushort)value > 1000)
|
||||||
|
throw new ArgumentException(nameof(value));
|
||||||
|
PokeListInfo[FOLLOW] = (ushort)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get => BitConverter.ToUInt16(Data, Offset + (COUNT * 2));
|
||||||
|
set => BitConverter.GetBytes((ushort) value).CopyTo(Data, Offset + (COUNT * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] LoadPointerData()
|
||||||
|
{
|
||||||
|
var list = new int[7];
|
||||||
|
for (int i = 0; i < list.Length; i++)
|
||||||
|
list[i] = BitConverter.ToUInt16(Data, Offset + (i * 2));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPointerData(IList<int> vals)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < vals.Count; i++)
|
||||||
|
BitConverter.GetBytes((ushort)vals[i]).CopyTo(Data, Offset + (i * 2));
|
||||||
|
vals.CopyTo(PokeListInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetPartyOffset(int slot)
|
||||||
|
{
|
||||||
|
if ((uint)slot >= 6)
|
||||||
|
throw new ArgumentException(nameof(slot) + " expected to be < 6.");
|
||||||
|
int position = PokeListInfo[slot];
|
||||||
|
return SAV.GetBoxSlotOffset(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSlotInBattleTeam(int box, int slot)
|
||||||
|
{
|
||||||
|
if ((uint)slot >= SAV.SlotCount || (uint)box >= SAV.BoxCount)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int slotIndex = slot + (SAV.BoxSlotCount * box);
|
||||||
|
return PokeListInfo.Take(6).Any(s => s == slotIndex) || FollowerIndex == slotIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSlotLocked(int box, int slot)
|
||||||
|
{
|
||||||
|
if ((uint)slot >= SAV.SlotCount || (uint)box >= SAV.BoxCount)
|
||||||
|
return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CompressStorage()
|
||||||
|
{
|
||||||
|
// Box Data is stored as a list, instead of an array. Empty interstitials are not legal.
|
||||||
|
// Fix stored slots!
|
||||||
|
var arr = PokeListInfo.Take(7).ToArray();
|
||||||
|
var result = SAV.CompressStorage(out int count, arr);
|
||||||
|
Debug.Assert(count <= MAX_SLOTS);
|
||||||
|
arr.CopyTo(PokeListInfo);
|
||||||
|
Count = count;
|
||||||
|
if (FollowerIndex > count && FollowerIndex != SLOT_EMPTY)
|
||||||
|
RemoveFollower();
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
SetPointerData(PokeListInfo);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ namespace PKHeX.Core
|
||||||
{
|
{
|
||||||
public const int BEEF = 0x42454546;
|
public const int BEEF = 0x42454546;
|
||||||
|
|
||||||
|
public const int SIZE_G7GG = 0x100000;
|
||||||
public const int SIZE_G7USUM = 0x6CC00;
|
public const int SIZE_G7USUM = 0x6CC00;
|
||||||
public const int SIZE_G7SM = 0x6BE00;
|
public const int SIZE_G7SM = 0x6BE00;
|
||||||
public const int SIZE_G6XY = 0x65600;
|
public const int SIZE_G6XY = 0x65600;
|
||||||
|
@ -54,7 +55,7 @@ namespace PKHeX.Core
|
||||||
|
|
||||||
private static readonly HashSet<int> SIZES = new HashSet<int>(SIZES_2)
|
private static readonly HashSet<int> SIZES = new HashSet<int>(SIZES_2)
|
||||||
{
|
{
|
||||||
SIZE_G7SM, SIZE_G7USUM,
|
SIZE_G7SM, SIZE_G7USUM, SIZE_G7GG,
|
||||||
SIZE_G6XY, SIZE_G6ORAS, SIZE_G6ORASDEMO,
|
SIZE_G6XY, SIZE_G6ORAS, SIZE_G6ORASDEMO,
|
||||||
SIZE_G5RAW, SIZE_G5BW, SIZE_G5B2W2,
|
SIZE_G5RAW, SIZE_G5BW, SIZE_G5B2W2,
|
||||||
SIZE_G4BR, SIZE_G4RAW,
|
SIZE_G4BR, SIZE_G4RAW,
|
||||||
|
@ -92,6 +93,8 @@ namespace PKHeX.Core
|
||||||
if (GetIsG7SAV(data) != GameVersion.Invalid)
|
if (GetIsG7SAV(data) != GameVersion.Invalid)
|
||||||
return GameVersion.Gen7;
|
return GameVersion.Gen7;
|
||||||
|
|
||||||
|
if (GetIsBelugaSAV(data) != GameVersion.Invalid)
|
||||||
|
return GameVersion.GG;
|
||||||
if (GetIsG3COLOSAV(data) != GameVersion.Invalid)
|
if (GetIsG3COLOSAV(data) != GameVersion.Invalid)
|
||||||
return GameVersion.COLO;
|
return GameVersion.COLO;
|
||||||
if (GetIsG3XDSAV(data) != GameVersion.Invalid)
|
if (GetIsG3XDSAV(data) != GameVersion.Invalid)
|
||||||
|
@ -430,6 +433,23 @@ namespace PKHeX.Core
|
||||||
return GameVersion.Invalid;
|
return GameVersion.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines if the input data belongs to a <see cref="SAV7b"/> save</summary>
|
||||||
|
/// <param name="data">Save data of which to determine the type</param>
|
||||||
|
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||||
|
private static GameVersion GetIsBelugaSAV(byte[] data)
|
||||||
|
{
|
||||||
|
if (data.Length != SIZE_G7GG)
|
||||||
|
return GameVersion.Invalid;
|
||||||
|
|
||||||
|
const int actualLength = 0xB8800;
|
||||||
|
if (BitConverter.ToUInt32(data, actualLength - 0x1F0) != BEEF) // beef table start
|
||||||
|
return GameVersion.Invalid;
|
||||||
|
if (BitConverter.ToUInt16(data, actualLength - 0x200 + 0xB0) != 0x13) // check a block number to double check
|
||||||
|
return GameVersion.Invalid;
|
||||||
|
|
||||||
|
return GameVersion.GG;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool GetIsBank7(byte[] data) => data.Length == SIZE_G7BANK && data[0] != 0;
|
private static bool GetIsBank7(byte[] data) => data.Length == SIZE_G7BANK && data[0] != 0;
|
||||||
|
|
||||||
/// <summary>Creates an instance of a SaveFile using the given save data.</summary>
|
/// <summary>Creates an instance of a SaveFile using the given save data.</summary>
|
||||||
|
@ -477,6 +497,7 @@ namespace PKHeX.Core
|
||||||
case GameVersion.XD: return new SAV3XD(data);
|
case GameVersion.XD: return new SAV3XD(data);
|
||||||
case GameVersion.RSBOX: return new SAV3RSBox(data);
|
case GameVersion.RSBOX: return new SAV3RSBox(data);
|
||||||
case GameVersion.BATREV: return new SAV4BR(data);
|
case GameVersion.BATREV: return new SAV4BR(data);
|
||||||
|
case GameVersion.GG: return new SAV7b(data);
|
||||||
|
|
||||||
// Bulk Storage
|
// Bulk Storage
|
||||||
case GameVersion.USUM: return Bank7.GetBank7(data);
|
case GameVersion.USUM: return Bank7.GetBank7(data);
|
||||||
|
@ -583,6 +604,8 @@ namespace PKHeX.Core
|
||||||
return new SAV7(new byte[SIZE_G7SM]);
|
return new SAV7(new byte[SIZE_G7SM]);
|
||||||
case GameVersion.US: case GameVersion.UM: case GameVersion.USUM:
|
case GameVersion.US: case GameVersion.UM: case GameVersion.USUM:
|
||||||
return new SAV7(new byte[SIZE_G7USUM]);
|
return new SAV7(new byte[SIZE_G7USUM]);
|
||||||
|
case GameVersion.GP: case GameVersion.GE: case GameVersion.GG:
|
||||||
|
return new SAV7b(new byte[SIZE_G7GG]);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
@ -711,10 +734,10 @@ namespace PKHeX.Core
|
||||||
/// <returns>Checksum</returns>
|
/// <returns>Checksum</returns>
|
||||||
public static ushort CRC16(byte[] data, int start, int length, ushort initial)
|
public static ushort CRC16(byte[] data, int start, int length, ushort initial)
|
||||||
{
|
{
|
||||||
ushort chk = (ushort)~initial;
|
ushort chk = initial;
|
||||||
for (var i = start; i < start + length; i++)
|
for (var i = start; i < start + length; i++)
|
||||||
chk = (ushort) (crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8);
|
chk = (ushort) (crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8);
|
||||||
return (ushort)~chk;
|
return chk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||||
|
@ -722,7 +745,14 @@ namespace PKHeX.Core
|
||||||
/// <param name="start">Offset to start checksum at</param>
|
/// <param name="start">Offset to start checksum at</param>
|
||||||
/// <param name="length">Length of array to checksum</param>
|
/// <param name="length">Length of array to checksum</param>
|
||||||
/// <returns>Checksum</returns>
|
/// <returns>Checksum</returns>
|
||||||
public static ushort CRC16(byte[] data, int start, int length) => CRC16(data, start, length, 0);
|
public static ushort CRC16(byte[] data, int start, int length) => (ushort)~CRC16(data, start, length, unchecked((ushort)~0));
|
||||||
|
|
||||||
|
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||||
|
/// <param name="data">Input byte array</param>
|
||||||
|
/// <param name="start">Offset to start checksum at</param>
|
||||||
|
/// <param name="length">Length of array to checksum</param>
|
||||||
|
/// <returns>Checksum</returns>
|
||||||
|
public static ushort CRC16NoInvert(byte[] data, int start, int length) => CRC16(data, start, length, 0);
|
||||||
|
|
||||||
public static byte[] Resign7(byte[] sav7)
|
public static byte[] Resign7(byte[] sav7)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue