mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 14:00:21 +00:00
Refactor And Deduplicate Some Switch Games Common Logic (#4187)
- Improve Epoch 1900 classes using similar logic from PlayTime7b. - Move Time Classes into non Gen specific folder since it appears the logic is shared across a few of them. - Use Epoch1900DateTimeValue for LastSaved in PlayTime7b since the logic is the same. - Remove TeamIndexes9 since it is a duplicate of TeamIndexes9. Use the similar pattern like Box8 where it is reused in multiple locations. - Add BlueberryClubRoom9 to ISaveBlock9Main since it wasn't added when the class was introduced. - Simplify RaidSevenStar9 creation since GetBlockSafe does basically what the if-else block does. - Change SAV8SWSH Base Raid to RaidGalar to match the same way the SAV9SV does for its Base Raid.
This commit is contained in:
parent
cf274bba70
commit
92c964d6fa
14 changed files with 119 additions and 178 deletions
|
@ -17,7 +17,7 @@ public interface ISaveBlock8Main
|
|||
Daycare8 Daycare { get; }
|
||||
Record8 Records { get; }
|
||||
TrainerCard8 TrainerCard { get; }
|
||||
RaidSpawnList8 Raid { get; }
|
||||
RaidSpawnList8 RaidGalar { get; }
|
||||
RaidSpawnList8 RaidArmor { get; }
|
||||
RaidSpawnList8 RaidCrown { get; }
|
||||
TitleScreen8 TitleScreen { get; }
|
||||
|
|
|
@ -13,7 +13,7 @@ public interface ISaveBlock9Main
|
|||
PlayTime9 Played { get; }
|
||||
Zukan9 Zukan { get; }
|
||||
ConfigSave9 Config { get; }
|
||||
TeamIndexes9 TeamIndexes { get; }
|
||||
TeamIndexes8 TeamIndexes { get; }
|
||||
Epoch1900DateTimeValue LastSaved { get; }
|
||||
Epoch1970Value LastDateCycle { get; }
|
||||
PlayerFashion9 PlayerFashion { get; }
|
||||
|
@ -22,4 +22,5 @@ public interface ISaveBlock9Main
|
|||
RaidSpawnList9 RaidKitakami { get; }
|
||||
RaidSpawnList9 RaidBlueberry { get; }
|
||||
BlueberryQuestRecord9 BlueberryQuestRecord { get; }
|
||||
BlueberryClubRoom9 BlueberryClubRoom { get; }
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public sealed class SaveBlockAccessor8SWSH : SCBlockAccessor, ISaveBlock8Main
|
|||
public Record8 Records { get; }
|
||||
public TrainerCard8 TrainerCard{ get; }
|
||||
public FashionUnlock8 Fashion { get; }
|
||||
public RaidSpawnList8 Raid { get; }
|
||||
public RaidSpawnList8 RaidGalar { get; }
|
||||
public RaidSpawnList8 RaidArmor { get; }
|
||||
public RaidSpawnList8 RaidCrown { get; }
|
||||
public TitleScreen8 TitleScreen { get; }
|
||||
|
@ -48,7 +48,7 @@ public sealed class SaveBlockAccessor8SWSH : SCBlockAccessor, ISaveBlock8Main
|
|||
Daycare = new Daycare8(sav, GetBlock(KDaycare));
|
||||
Records = new Record8(sav, GetBlock(KRecord));
|
||||
Fashion = new FashionUnlock8(sav, GetBlock(KFashionUnlock));
|
||||
Raid = new RaidSpawnList8(sav, GetBlock(KRaidSpawnList), RaidSpawnList8.RaidCountLegal_O0);
|
||||
RaidGalar = new RaidSpawnList8(sav, GetBlock(KRaidSpawnList), RaidSpawnList8.RaidCountLegal_O0);
|
||||
RaidArmor = new RaidSpawnList8(sav, GetBlockSafe(KRaidSpawnListR1), RaidSpawnList8.RaidCountLegal_R1);
|
||||
RaidCrown = new RaidSpawnList8(sav, GetBlockSafe(KRaidSpawnListR2), RaidSpawnList8.RaidCountLegal_R2);
|
||||
TitleScreen = new TitleScreen8(sav, GetBlock(KTitleScreenTeam));
|
||||
|
|
|
@ -21,7 +21,7 @@ public sealed class SaveBlockAccessor9SV : SCBlockAccessor, ISaveBlock9Main
|
|||
public Zukan9 Zukan { get; }
|
||||
public ConfigSave9 Config { get; }
|
||||
public ConfigCamera9 ConfigCamera { get; }
|
||||
public TeamIndexes9 TeamIndexes { get; }
|
||||
public TeamIndexes8 TeamIndexes { get; }
|
||||
public Epoch1900DateTimeValue LastSaved { get; }
|
||||
public Epoch1970Value LastDateCycle { get; }
|
||||
public PlayerFashion9 PlayerFashion { get; }
|
||||
|
@ -46,7 +46,7 @@ public sealed class SaveBlockAccessor9SV : SCBlockAccessor, ISaveBlock9Main
|
|||
Zukan = new Zukan9(sav, GetBlock(KZukan), GetBlockSafe(KZukanT1));
|
||||
Config = new ConfigSave9(sav, GetBlock(KConfig));
|
||||
ConfigCamera = new ConfigCamera9(sav, GetBlockSafe(KConfigCamera));
|
||||
TeamIndexes = new TeamIndexes9(sav, GetBlock(KTeamIndexes), GetBlock(KTeamLocks));
|
||||
TeamIndexes = new TeamIndexes8(sav, GetBlock(KTeamIndexes), GetBlock(KTeamLocks));
|
||||
LastSaved = new Epoch1900DateTimeValue(GetBlock(KLastSaved));
|
||||
LastDateCycle = new Epoch1970Value(GetBlock(KLastDateCycle));
|
||||
PlayerFashion = new PlayerFashion9(sav, GetBlock(KCurrentClothing));
|
||||
|
@ -70,12 +70,7 @@ public sealed class SaveBlockAccessor9SV : SCBlockAccessor, ISaveBlock9Main
|
|||
RaidBlueberry = new RaidSpawnList9(sav, fake, default, RaidSpawnList9.RaidCountLegal_T2, false);
|
||||
}
|
||||
|
||||
var captured = GetBlock(KSevenStarRaidsCapture);
|
||||
var s7 = TryGetBlock(KSevenStarRaidsDefeat, out var defeated);
|
||||
if (!s7)
|
||||
defeated = GetFakeBlock();
|
||||
RaidSevenStar = new RaidSevenStar9(sav, captured, defeated!);
|
||||
|
||||
RaidSevenStar = new RaidSevenStar9(sav, GetBlock(KSevenStarRaidsCapture), GetBlockSafe(KSevenStarRaidsDefeat));
|
||||
EnrollmentDate = new Epoch1900DateValue(GetBlock(KEnrollmentDate));
|
||||
BlueberryQuestRecord = new BlueberryQuestRecord9(sav, GetBlockSafe(KBlueberryQuestRecords));
|
||||
BlueberryClubRoom = new BlueberryClubRoom9(sav, GetBlockSafe(KBlueberryClubRoom));
|
||||
|
|
|
@ -76,7 +76,7 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS
|
|||
public Record8 Records => Blocks.Records;
|
||||
public TrainerCard8 TrainerCard => Blocks.TrainerCard;
|
||||
public FashionUnlock8 Fashion => Blocks.Fashion;
|
||||
public RaidSpawnList8 Raid => Blocks.Raid;
|
||||
public RaidSpawnList8 RaidGalar => Blocks.RaidGalar;
|
||||
public RaidSpawnList8 RaidArmor => Blocks.RaidArmor;
|
||||
public RaidSpawnList8 RaidCrown => Blocks.RaidCrown;
|
||||
public TitleScreen8 TitleScreen => Blocks.TitleScreen;
|
||||
|
|
|
@ -74,7 +74,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile
|
|||
public BoxLayout9 BoxLayout => Blocks.BoxLayout;
|
||||
public PlayTime9 Played => Blocks.Played;
|
||||
public ConfigSave9 Config => Blocks.Config;
|
||||
public TeamIndexes9 TeamIndexes => Blocks.TeamIndexes;
|
||||
public TeamIndexes8 TeamIndexes => Blocks.TeamIndexes;
|
||||
public Epoch1900DateTimeValue LastSaved => Blocks.LastSaved;
|
||||
public Epoch1970Value LastDateCycle => Blocks.LastDateCycle;
|
||||
public PlayerFashion9 PlayerFashion => Blocks.PlayerFashion;
|
||||
|
|
|
@ -25,39 +25,34 @@ public sealed class PlayTime7b : SaveBlock<SAV7b>
|
|||
set => Data[Offset + 3] = (byte)value;
|
||||
}
|
||||
|
||||
private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x4)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x4), value); }
|
||||
private int LastSavedYear { get => (int)(LastSaved & 0xFFF) + 1900; set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)(value - 1900); }
|
||||
private int LastSavedMonth { get => (int)((LastSaved >> 12) & 0xF) + 1; set => LastSaved = (LastSaved & 0xFFFF0FFF) | (((uint)(value - 1) & 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}"; // not :
|
||||
private Epoch1900DateTimeValue LastSaved => new(Data.AsMemory(Offset + 0x4));
|
||||
public string LastSavedTime => $"{LastSaved.Year:0000}-{LastSaved.Month:00}-{LastSaved.Day:00} {LastSaved.Hour:00}ː{LastSaved.Minute:00}"; // not :
|
||||
|
||||
public DateTime? LastSavedDate
|
||||
{
|
||||
get => !DateUtil.IsDateValid(LastSavedYear, LastSavedMonth, LastSavedDay)
|
||||
get => !DateUtil.IsDateValid(LastSaved.Year, LastSaved.Month, LastSaved.Day)
|
||||
? null
|
||||
: new DateTime(LastSavedYear, LastSavedMonth, LastSavedDay, LastSavedHour, LastSavedMinute, 0);
|
||||
: new DateTime(LastSaved.Year, LastSaved.Month, LastSaved.Day, LastSaved.Hour, LastSaved.Minute, 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;
|
||||
LastSaved.Year = dt.Year;
|
||||
LastSaved.Month = dt.Month;
|
||||
LastSaved.Day = dt.Day;
|
||||
LastSaved.Hour = dt.Hour;
|
||||
LastSaved.Minute = 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;
|
||||
LastSaved.Year = 0;
|
||||
LastSaved.Month = 0;
|
||||
LastSaved.Day = 0;
|
||||
LastSaved.Hour = 0;
|
||||
LastSaved.Minute = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,28 @@ using static System.Buffers.Binary.BinaryPrimitives;
|
|||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) : ITeamIndexSet
|
||||
public sealed class TeamIndexes8 : ITeamIndexSet
|
||||
{
|
||||
private const int TeamCount = 6;
|
||||
private const int NONE_SELECTED = -1;
|
||||
private readonly SaveFile SAV;
|
||||
private readonly SCBlock Indexes;
|
||||
private readonly SCBlock Locks;
|
||||
public readonly int[] TeamSlots = new int[TeamCount * 6];
|
||||
|
||||
private TeamIndexes8(SaveFile sav, SCBlock indexes, SCBlock locks)
|
||||
{
|
||||
SAV = sav;
|
||||
Indexes = indexes;
|
||||
Locks = locks;
|
||||
}
|
||||
|
||||
public TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) : this((SaveFile)sav, indexes, locks) { }
|
||||
public TeamIndexes8(SAV9SV sav, SCBlock indexes, SCBlock locks) : this((SaveFile)sav, indexes, locks) { }
|
||||
|
||||
public void LoadBattleTeams()
|
||||
{
|
||||
if (!sav.State.Exportable)
|
||||
if (!SAV.State.Exportable)
|
||||
{
|
||||
ClearBattleTeams();
|
||||
return;
|
||||
|
@ -19,7 +32,7 @@ public sealed class TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) :
|
|||
|
||||
for (int i = 0; i < TeamCount * 6; i++)
|
||||
{
|
||||
short val = ReadInt16LittleEndian(indexes.Data.AsSpan((i * 2)));
|
||||
short val = ReadInt16LittleEndian(Indexes.Data.AsSpan(i * 2));
|
||||
if (val < 0)
|
||||
{
|
||||
TeamSlots[i] = NONE_SELECTED;
|
||||
|
@ -28,7 +41,7 @@ public sealed class TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) :
|
|||
|
||||
int box = val >> 8;
|
||||
int slot = val & 0xFF;
|
||||
int index = (sav.BoxSlotCount * box) + slot;
|
||||
int index = (SAV.BoxSlotCount * box) + slot;
|
||||
TeamSlots[i] = index & 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +60,7 @@ public sealed class TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) :
|
|||
|
||||
public void SaveBattleTeams()
|
||||
{
|
||||
var span = indexes.Data.AsSpan();
|
||||
var span = Indexes.Data.AsSpan();
|
||||
for (int i = 0; i < TeamCount * 6; i++)
|
||||
{
|
||||
int index = TeamSlots[i];
|
||||
|
@ -57,12 +70,12 @@ public sealed class TeamIndexes8(SAV8SWSH sav, SCBlock indexes, SCBlock locks) :
|
|||
continue;
|
||||
}
|
||||
|
||||
sav.GetBoxSlotFromIndex(index, out var box, out var slot);
|
||||
SAV.GetBoxSlotFromIndex(index, out var box, out var slot);
|
||||
index = (box << 8) | slot;
|
||||
WriteInt16LittleEndian(span[(i * 2)..], (short)index);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetIsTeamLocked(int team) => FlagUtil.GetFlag(locks.Data, 0, team);
|
||||
public void SetIsTeamLocked(int team, bool value) => FlagUtil.SetFlag(locks.Data, 0, team, value);
|
||||
public bool GetIsTeamLocked(int team) => FlagUtil.GetFlag(Locks.Data, 0, team);
|
||||
public void SetIsTeamLocked(int team, bool value) => FlagUtil.SetFlag(Locks.Data, 0, team, value);
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the <see cref="Timestamp"/> to indicate the seconds since 1900 that an event occurred.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class Epoch1900DateTimeValue(Memory<byte> Data)
|
||||
{
|
||||
// Data should be 8 bytes where we only care about the first 4.5 bytes i.e. 36 bits
|
||||
// First 12 bits are year from 1900, next 4 bits are 0 indexed month, next 5 are days, next 5 are hours, next 6 bits are minutes, last 4 bits are seconds
|
||||
private Span<byte> Span => Data.Span;
|
||||
|
||||
public Epoch1900DateTimeValue(SCBlock block) : this(block.Data) { }
|
||||
|
||||
private static DateTime Epoch => new(1900, 1, 1);
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get => Epoch
|
||||
.AddSeconds(Span[4])
|
||||
.AddMinutes(((Span[3] & 0b1111_1100) >> 2))
|
||||
.AddHours((Span[3] & 0b0000_0011) << 3 | (Span[2] & 0b1110_0000) >> 5)
|
||||
.AddDays((Span[2] & 0b0001_1111) - 1)
|
||||
.AddMonths((Span[1] & 0b1111_0000) >> 4)
|
||||
.AddYears(((Span[1] & 0b0000_1111) << 8) | Span[0]);
|
||||
set
|
||||
{
|
||||
int day = value.Day;
|
||||
int month = value.Month - Epoch.Month;
|
||||
int year = value.Year - Epoch.Year;
|
||||
int hour = value.Hour;
|
||||
int minute = value.Minute;
|
||||
int second = value.Second;
|
||||
Span[4] = (byte)second;
|
||||
Span[3] = (byte)(((minute & 0b0011_1111) << 2) | ((hour & 0b0001_1000) >> 3));
|
||||
Span[2] = (byte)(((hour & 0b0000_0111) << 5) | (day & 0b0001_1111));
|
||||
Span[1] = (byte)(((month & 0b0000_1111) << 4) | ((year & 0b1111_0000_00000) >> 8));
|
||||
Span[0] = (byte)(year & 0b1111_1111);
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayValue => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}ː{Timestamp.Second:00}"; // not :
|
||||
|
||||
/// <summary>
|
||||
/// time_t (seconds since 1900 Epoch)
|
||||
/// </summary>
|
||||
public ulong Seconds
|
||||
{
|
||||
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
||||
set => Timestamp = Epoch.AddSeconds(value);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class TeamIndexes9(SAV9SV sav, SCBlock indexes, SCBlock locks) : ITeamIndexSet
|
||||
{
|
||||
private const int TeamCount = 6;
|
||||
private const int NONE_SELECTED = -1;
|
||||
public readonly int[] TeamSlots = new int[TeamCount * 6];
|
||||
|
||||
public void LoadBattleTeams()
|
||||
{
|
||||
if (!sav.State.Exportable)
|
||||
{
|
||||
ClearBattleTeams();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < TeamCount * 6; i++)
|
||||
{
|
||||
short val = ReadInt16LittleEndian(indexes.Data.AsSpan(i * 2));
|
||||
if (val < 0)
|
||||
{
|
||||
TeamSlots[i] = NONE_SELECTED;
|
||||
continue;
|
||||
}
|
||||
|
||||
int box = val >> 8;
|
||||
int slot = val & 0xFF;
|
||||
int index = (sav.BoxSlotCount * box) + slot;
|
||||
TeamSlots[i] = index & 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearBattleTeams()
|
||||
{
|
||||
for (int i = 0; i < TeamSlots.Length; i++)
|
||||
TeamSlots[i] = NONE_SELECTED;
|
||||
}
|
||||
|
||||
public void UnlockAllTeams()
|
||||
{
|
||||
for (int i = 0; i < TeamCount; i++)
|
||||
SetIsTeamLocked(i, false);
|
||||
}
|
||||
|
||||
public void SaveBattleTeams()
|
||||
{
|
||||
var span = indexes.Data.AsSpan();
|
||||
for (int i = 0; i < TeamCount * 6; i++)
|
||||
{
|
||||
int index = TeamSlots[i];
|
||||
if (index < 0)
|
||||
{
|
||||
WriteInt16LittleEndian(span[(i * 2)..], (short)index);
|
||||
continue;
|
||||
}
|
||||
|
||||
sav.GetBoxSlotFromIndex(index, out var box, out var slot);
|
||||
index = (box << 8) | slot;
|
||||
WriteInt16LittleEndian(span[(i * 2)..], (short)index);
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetIsTeamLocked(int team) => FlagUtil.GetFlag(locks.Data, 0, team);
|
||||
public void SetIsTeamLocked(int team, bool value) => FlagUtil.SetFlag(locks.Data, 0, team, value);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the <see cref="Timestamp"/> to indicate the seconds since 1900 that an event occurred.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class Epoch1900DateTimeValue(Memory<byte> Data)
|
||||
{
|
||||
// Data should be 4 or 8 bytes where we only care about the first 4 or 4.5 bytes i.e. 32 or 36 bits
|
||||
// First 12 bits are year from 1900, next 4 bits are 0 indexed month, next 5 are days, next 5 are hours, next 6 bits are minutes, (optional) last 4 bits are seconds
|
||||
private Span<byte> Span => Data.Span;
|
||||
|
||||
public Epoch1900DateTimeValue(SCBlock block) : this(block.Data) { }
|
||||
|
||||
private static DateTime Epoch => new(1900, 1, 1);
|
||||
|
||||
private uint RawDate { get => ReadUInt32LittleEndian(Span); set => WriteUInt32LittleEndian(Span, value); }
|
||||
public int Year { get => (int)(RawDate & 0xFFF) + Epoch.Year; set => RawDate = (RawDate & 0xFFFFF000) | (uint)(value - Epoch.Year); }
|
||||
public int Month { get => (int)((RawDate >> 12) & 0xF) + 1; set => RawDate = (RawDate & 0xFFFF0FFF) | (((uint)(value - 1) & 0xF) << 12); }
|
||||
public int Day { get => (int)((RawDate >> 16) & 0x1F); set => RawDate = (RawDate & 0xFFE0FFFF) | (((uint)value & 0x1F) << 16); }
|
||||
public int Hour { get => (int)((RawDate >> 21) & 0x1F); set => RawDate = (RawDate & 0xFC1FFFFF) | (((uint)value & 0x1F) << 21); }
|
||||
public int Minute { get => (int)((RawDate >> 26) & 0x3F); set => RawDate = (RawDate & 0x03FFFFFF) | (((uint)value & 0x3F) << 26); }
|
||||
public bool HasSeconds => Span.Length > 4;
|
||||
public int Second {
|
||||
get => HasSeconds ? Span[4] : 0;
|
||||
set {
|
||||
if (HasSeconds) Span[4] = (byte)value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get => new(Year, Month, Day, Hour, Minute, Second);
|
||||
set
|
||||
{
|
||||
Year = value.Year;
|
||||
Month = value.Month;
|
||||
Day = value.Day;
|
||||
Hour = value.Hour;
|
||||
Minute = value.Minute;
|
||||
Second = value.Second;
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayValue => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}ː{Timestamp.Second:00}"; // not :
|
||||
|
||||
/// <summary>
|
||||
/// time_t (seconds since 1900 Epoch)
|
||||
/// </summary>
|
||||
public ulong TotalSeconds
|
||||
{
|
||||
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
||||
set => Timestamp = Epoch.AddSeconds(value);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
@ -10,26 +11,26 @@ namespace PKHeX.Core;
|
|||
public sealed class Epoch1900DateValue(Memory<byte> Data)
|
||||
{
|
||||
// Data should be 4 bytes where we only care about the first 3 bytes i.e. 24 bits
|
||||
// First 6 bits are day, next 6 bits are 0 indexed month, last 12 bits are year from 1900
|
||||
// First 12 bits are year from 1900, next 6 bits are 0 indexed month, next 6 are days
|
||||
private Span<byte> Span => Data.Span;
|
||||
|
||||
public Epoch1900DateValue(SCBlock block) : this(block.Data) { }
|
||||
|
||||
private static DateTime Epoch => new(1900, 1, 1);
|
||||
|
||||
private uint RawDate { get => ReadUInt32LittleEndian(Span); set => WriteUInt32LittleEndian(Span, value); }
|
||||
public int Year { get => (int)(RawDate & 0xFFF) + Epoch.Year; set => RawDate = (RawDate & 0xFFFFF000) | (uint)(value - Epoch.Year); }
|
||||
public int Month { get => (int)((RawDate >> 12) & 0x3F) + 1; set => RawDate = (RawDate & 0xFFFC0FFF) | (((uint)(value - 1) & 0x3F) << 12); }
|
||||
public int Day { get => (int)((RawDate >> 18) & 0x3F); set => RawDate = (RawDate & 0xFF03FFFF) | (((uint)value & 0x3F) << 18); }
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get => Epoch
|
||||
.AddDays((Span[2] >> 2) - 1)
|
||||
.AddMonths(((Span[2] & 0b0000_0011) << 2) | ((Span[1] & 0b1111_0000) >> 4))
|
||||
.AddYears(((Span[1] & 0b0000_1111) << 8) | Span[0]);
|
||||
set {
|
||||
int day = value.Day;
|
||||
int month = value.Month - Epoch.Month;
|
||||
int year = value.Year - Epoch.Year;
|
||||
Span[2] = (byte)(((day & 0b0011_1111) << 2) | ((month & 0b0011_0000) >> 4));
|
||||
Span[1] = (byte)(((month & 0b0000_1111) << 4) | ((year & 0b1111_0000_00000) >> 8));
|
||||
Span[0] = (byte)(year & 0b1111_1111);
|
||||
get => new(Year, Month, Day);
|
||||
set
|
||||
{
|
||||
Year = value.Year;
|
||||
Month = value.Month;
|
||||
Day = value.Day;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,7 @@ public sealed class Epoch1900DateValue(Memory<byte> Data)
|
|||
/// <summary>
|
||||
/// time_t (seconds since 1900 Epoch rounded to days)
|
||||
/// </summary>
|
||||
public ulong Seconds
|
||||
public ulong TotalSeconds
|
||||
{
|
||||
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
||||
set => Timestamp = Epoch.AddSeconds(value);
|
|
@ -18,7 +18,7 @@ public partial class SAV_Raid8 : Form
|
|||
SAV = (SAV8SWSH)(Origin = sav).Clone();
|
||||
Raids = raidOrigin switch
|
||||
{
|
||||
MaxRaidOrigin.Galar => SAV.Raid,
|
||||
MaxRaidOrigin.Galar => SAV.RaidGalar,
|
||||
MaxRaidOrigin.IsleOfArmor => SAV.RaidArmor,
|
||||
MaxRaidOrigin.CrownTundra => SAV.RaidCrown,
|
||||
_ => throw new ArgumentOutOfRangeException($"Raid Origin {raidOrigin} is not valid for Sword and Shield")
|
||||
|
|
Loading…
Reference in a new issue