mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 14:00:21 +00:00
Deduplicate Gen 6-8 PlayTime Logic (#4208)
Slightly overengineered but a fun experiment to de-duplicate some logic.
This commit is contained in:
parent
25c7b3cc8c
commit
c651c6f6cd
13 changed files with 129 additions and 131 deletions
|
@ -9,7 +9,9 @@ namespace PKHeX.Core;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates differences in the Event Blocks between two <see cref="SaveFile"/>.
|
/// Calculates differences in the Event Blocks between two <see cref="SaveFile"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : class, IEventFlagArray, IEventWorkArray<T2> where T2 : unmanaged, IEquatable<T2>
|
public sealed class EventBlockDiff<TSave, TWorkValue> : IEventWorkDiff
|
||||||
|
where TSave : class, IEventFlagArray, IEventWorkArray<TWorkValue>
|
||||||
|
where TWorkValue : unmanaged, IEquatable<TWorkValue>
|
||||||
{
|
{
|
||||||
public List<int> SetFlags { get; } = [];
|
public List<int> SetFlags { get; } = [];
|
||||||
public List<int> ClearedFlags { get; } = [];
|
public List<int> ClearedFlags { get; } = [];
|
||||||
|
@ -19,7 +21,7 @@ public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : class, IEve
|
||||||
|
|
||||||
private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB
|
private const int MAX_SAVEFILE_SIZE = 0x10_0000; // 1 MB
|
||||||
|
|
||||||
public EventBlockDiff(T s1, T s2) => Diff(s1, s2);
|
public EventBlockDiff(TSave s1, TSave s2) => Diff(s1, s2);
|
||||||
|
|
||||||
public EventBlockDiff(string f1, string f2)
|
public EventBlockDiff(string f1, string f2)
|
||||||
{
|
{
|
||||||
|
@ -37,16 +39,16 @@ public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : class, IEve
|
||||||
Diff(t1, t2);
|
Diff(t1, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T? GetBlock(SaveFile s1)
|
private static TSave? GetBlock(SaveFile s1)
|
||||||
{
|
{
|
||||||
if (s1 is T t1)
|
if (s1 is TSave t1)
|
||||||
return t1;
|
return t1;
|
||||||
if (s1 is IEventFlagProvider37 p1)
|
if (s1 is IEventFlagProvider37 p1)
|
||||||
return p1.EventWork as T;
|
return p1.EventWork as TSave;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EventWorkDiffCompatibility SanityCheckSaveInfo(T s1, T s2)
|
private static EventWorkDiffCompatibility SanityCheckSaveInfo(TSave s1, TSave s2)
|
||||||
{
|
{
|
||||||
if (s1.GetType() != s2.GetType())
|
if (s1.GetType() != s2.GetType())
|
||||||
return DifferentGameGroup;
|
return DifferentGameGroup;
|
||||||
|
@ -57,7 +59,7 @@ public sealed class EventBlockDiff<T, T2> : IEventWorkDiff where T : class, IEve
|
||||||
return Valid;
|
return Valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Diff(T s1, T s2)
|
private void Diff(TSave s1, TSave s2)
|
||||||
{
|
{
|
||||||
Message = SanityCheckSaveInfo(s1, s2);
|
Message = SanityCheckSaveInfo(s1, s2);
|
||||||
if (Message != Valid)
|
if (Message != Valid)
|
||||||
|
|
|
@ -13,5 +13,5 @@ public interface ISaveBlock8LA
|
||||||
MyItem8a Items { get; }
|
MyItem8a Items { get; }
|
||||||
Epoch1970Value AdventureStart { get; }
|
Epoch1970Value AdventureStart { get; }
|
||||||
Epoch1900DateTimeValue LastSaved { get; }
|
Epoch1900DateTimeValue LastSaved { get; }
|
||||||
PlayTime8a Played { get; }
|
PlayTime8b Played { get; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ public sealed class SaveBlockAccessor8LA(SAV8LA sav) : SCBlockAccessor, ISaveBlo
|
||||||
public Coordinates8a Coordinates { get; } = new(sav, Block(sav, KCoordinates));
|
public Coordinates8a Coordinates { get; } = new(sav, Block(sav, KCoordinates));
|
||||||
public Epoch1900DateTimeValue LastSaved { get; } = new(Block(sav, KLastSaved));
|
public Epoch1900DateTimeValue LastSaved { get; } = new(Block(sav, KLastSaved));
|
||||||
public PlayerFashion8a FashionPlayer { get; } = new(sav, Block(sav, KFashionPlayer));
|
public PlayerFashion8a FashionPlayer { get; } = new(sav, Block(sav, KFashionPlayer));
|
||||||
public PlayTime8a Played { get; } = new(sav, Block(sav, KPlayTime));
|
public PlayTime8b Played { get; } = new(sav, Block(sav, KPlayTime));
|
||||||
|
|
||||||
public int DetectRevision() => HasBlock(0x8184EFB4) ? 1 : 0;
|
public int DetectRevision() => HasBlock(0x8184EFB4) ? 1 : 0;
|
||||||
|
|
||||||
|
|
|
@ -261,9 +261,9 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
|
||||||
public override string OT { get => MyStatus.OT; set => MyStatus.OT = value; }
|
public override string OT { get => MyStatus.OT; set => MyStatus.OT = value; }
|
||||||
public override uint Money { get => MyStatus.Money; set => MyStatus.Money = value; }
|
public override uint Money { get => MyStatus.Money; set => MyStatus.Money = value; }
|
||||||
|
|
||||||
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = (ushort)value; }
|
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = value; }
|
||||||
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = (byte)value; }
|
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = value; }
|
||||||
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = (byte)value; }
|
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = value; }
|
||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
public override IReadOnlyList<InventoryPouch> Inventory { get => Items.Inventory; set => Items.Inventory = value; }
|
public override IReadOnlyList<InventoryPouch> Inventory { get => Items.Inventory; set => Items.Inventory = value; }
|
||||||
|
|
|
@ -115,7 +115,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe
|
||||||
public Epoch1970Value AdventureStart => Blocks.AdventureStart;
|
public Epoch1970Value AdventureStart => Blocks.AdventureStart;
|
||||||
public Coordinates8a Coordinates => Blocks.Coordinates;
|
public Coordinates8a Coordinates => Blocks.Coordinates;
|
||||||
public Epoch1900DateTimeValue LastSaved => Blocks.LastSaved;
|
public Epoch1900DateTimeValue LastSaved => Blocks.LastSaved;
|
||||||
public PlayTime8a Played => Blocks.Played;
|
public PlayTime8b Played => Blocks.Played;
|
||||||
public AreaSpawnerSet8a AreaSpawners => new(Blocks.GetBlock(SaveBlockAccessor8LA.KSpawners));
|
public AreaSpawnerSet8a AreaSpawners => new(Blocks.GetBlock(SaveBlockAccessor8LA.KSpawners));
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -123,9 +123,9 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe
|
||||||
public override uint Money { get => (uint)Blocks.GetBlockValue(SaveBlockAccessor8LA.KMoney); set => Blocks.SetBlockValue(SaveBlockAccessor8LA.KMoney, value); }
|
public override uint Money { get => (uint)Blocks.GetBlockValue(SaveBlockAccessor8LA.KMoney); set => Blocks.SetBlockValue(SaveBlockAccessor8LA.KMoney, value); }
|
||||||
public override int MaxMoney => 9_999_999;
|
public override int MaxMoney => 9_999_999;
|
||||||
|
|
||||||
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = (ushort)value; }
|
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = value; }
|
||||||
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = (byte)value; }
|
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = value; }
|
||||||
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = (byte)value; }
|
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = value; }
|
||||||
|
|
||||||
protected override Span<byte> BoxBuffer => BoxInfo.Data;
|
protected override Span<byte> BoxBuffer => BoxInfo.Data;
|
||||||
protected override Span<byte> PartyBuffer => PartyInfo.Data;
|
protected override Span<byte> PartyBuffer => PartyInfo.Data;
|
||||||
|
|
|
@ -3,11 +3,12 @@ using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
public sealed class PlayTime6 : SaveBlock<SaveFile>
|
/// <summary>
|
||||||
|
/// Simple 4-byte block storing time played in a save file.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PlayTime<TSave>(TSave sav, Memory<byte> raw) : SaveBlock<TSave>(sav, raw)
|
||||||
|
where TSave : SaveFile
|
||||||
{
|
{
|
||||||
public PlayTime6(SAV6 sav, Memory<byte> raw) : base(sav, raw) { }
|
|
||||||
public PlayTime6(SAV7 sav, Memory<byte> raw) : base(sav, raw) { }
|
|
||||||
|
|
||||||
public int PlayedHours
|
public int PlayedHours
|
||||||
{
|
{
|
||||||
get => ReadUInt16LittleEndian(Data);
|
get => ReadUInt16LittleEndian(Data);
|
||||||
|
@ -26,39 +27,53 @@ public sealed class PlayTime6 : SaveBlock<SaveFile>
|
||||||
set => Data[3] = (byte)value;
|
set => Data[3] = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint LastSaved { get => ReadUInt32LittleEndian(Data[0x4..]); set => WriteUInt32LittleEndian(Data[0x4..], value); }
|
public string PlayedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
|
||||||
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); }
|
/// <summary>
|
||||||
private int LastSavedHour { get => (int)((LastSaved >> 21) & 0x1F); set => LastSaved = (LastSaved & 0xFC1FFFFF) | (((uint)value & 0x1F) << 21); }
|
/// Object storing the playtime of a save file as well as the last saved date.
|
||||||
private int LastSavedMinute { get => (int)((LastSaved >> 26) & 0x3F); set => LastSaved = (LastSaved & 0x03FFFFFF) | (((uint)value & 0x3F) << 26); }
|
/// </summary>
|
||||||
public string LastSavedTime => $"{LastSavedYear:0000}-{LastSavedMonth:00}-{LastSavedDay:00} {LastSavedHour:00}ː{LastSavedMinute:00}"; // not :
|
/// <typeparam name="TSave">Type of Save File</typeparam>
|
||||||
|
/// <typeparam name="TEpoch">Type of Epoch for the <see cref="LastSaved"/> timestamp.</typeparam>
|
||||||
|
public abstract class PlayTimeLastSaved<TSave, TEpoch>(TSave sav, Memory<byte> raw) : PlayTime<TSave>(sav, raw)
|
||||||
|
where TSave : SaveFile
|
||||||
|
where TEpoch : EpochDateTime
|
||||||
|
{
|
||||||
|
protected abstract TEpoch LastSaved { get; }
|
||||||
|
public string LastSavedTime => $"{LastSaved.Year:0000}-{LastSaved.Month:00}-{LastSaved.Day:00} {LastSaved.Hour:00}ː{LastSaved.Minute:00}"; // not :
|
||||||
|
|
||||||
public DateTime? LastSavedDate
|
public DateTime? LastSavedDate
|
||||||
{
|
{
|
||||||
get => !DateUtil.IsDateValid(LastSavedYear, LastSavedMonth, LastSavedDay)
|
get => !DateUtil.IsDateValid(LastSaved.Year, LastSaved.Month, LastSaved.Day)
|
||||||
? null
|
? null
|
||||||
: new DateTime(LastSavedYear, LastSavedMonth, LastSavedDay, LastSavedHour, LastSavedMinute, 0);
|
: LastSaved.Timestamp;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// Only update the properties if a value is provided.
|
// Only update the properties if a value is provided.
|
||||||
if (value is { } dt)
|
if (value is { } dt)
|
||||||
{
|
{
|
||||||
LastSavedYear = dt.Year;
|
LastSaved.Timestamp = dt;
|
||||||
LastSavedMonth = dt.Month;
|
|
||||||
LastSavedDay = dt.Day;
|
|
||||||
LastSavedHour = dt.Hour;
|
|
||||||
LastSavedMinute = dt.Minute;
|
|
||||||
}
|
}
|
||||||
else // Clear the date.
|
else // Clear the date.
|
||||||
{
|
{
|
||||||
// If code tries to access MetDate again, null will be returned.
|
// If code tries to access MetDate again, null will be returned.
|
||||||
LastSavedYear = 0;
|
LastSaved.Year = 0;
|
||||||
LastSavedMonth = 0;
|
LastSaved.Month = 0;
|
||||||
LastSavedDay = 0;
|
LastSaved.Day = 0;
|
||||||
LastSavedHour = 0;
|
LastSaved.Hour = 0;
|
||||||
LastSavedMinute = 0;
|
LastSaved.Minute = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PlayTime object with a zero-epoch Last Saved timestamp.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PlayTime6 : PlayTimeLastSaved<SaveFile, Epoch0000DateTime>
|
||||||
|
{
|
||||||
|
public PlayTime6(SAV6 sav, Memory<byte> raw) : base(sav, raw) { }
|
||||||
|
public PlayTime6(SAV7 sav, Memory<byte> raw) : base(sav, raw) { }
|
||||||
|
|
||||||
|
protected override Epoch0000DateTime LastSaved => new(Raw.Slice(0x4, 4));
|
||||||
|
}
|
||||||
|
|
|
@ -1,55 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
public sealed class PlayTime7b : SaveBlock<SaveFile>
|
/// <summary>
|
||||||
|
/// PlayTime object with a 1900-epoch Last Saved timestamp.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PlayTime7b : PlayTimeLastSaved<SaveFile, Epoch1900DateTimeValue>
|
||||||
{
|
{
|
||||||
public PlayTime7b(SAV7b sav, Memory<byte> raw) : base(sav, raw) { }
|
public PlayTime7b(SAV7b sav, Memory<byte> raw) : base(sav, raw) { }
|
||||||
public PlayTime7b(SAV8SWSH sav, SCBlock block) : base(sav, block.Data) { }
|
public PlayTime7b(SAV8SWSH sav, SCBlock block) : base(sav, block.Data) { }
|
||||||
|
|
||||||
public int PlayedHours
|
protected override Epoch1900DateTimeValue LastSaved => new(Raw.Slice(0x4, 4));
|
||||||
{
|
|
||||||
get => ReadUInt16LittleEndian(Data);
|
|
||||||
set => WriteUInt16LittleEndian(Data, (ushort)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int PlayedMinutes
|
|
||||||
{
|
|
||||||
get => Data[2];
|
|
||||||
set => Data[2] = (byte)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int PlayedSeconds
|
|
||||||
{
|
|
||||||
get => Data[3];
|
|
||||||
set => Data[3] = (byte)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Epoch1900DateTimeValue LastSaved => new(Raw.Slice(0x4, 4));
|
|
||||||
public string LastSavedTime => LastSaved.DisplayValue;
|
|
||||||
|
|
||||||
public DateTime? LastSavedDate
|
|
||||||
{
|
|
||||||
get => !DateUtil.IsDateValid(LastSaved.Year, LastSaved.Month, LastSaved.Day)
|
|
||||||
? null
|
|
||||||
: LastSaved.Timestamp;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
// Only update the properties if a value is provided.
|
|
||||||
if (value is { } dt)
|
|
||||||
{
|
|
||||||
LastSaved.Timestamp = dt;
|
|
||||||
}
|
|
||||||
else // Clear the date.
|
|
||||||
{
|
|
||||||
// If code tries to access MetDate again, null will be returned.
|
|
||||||
LastSaved.Year = 0;
|
|
||||||
LastSaved.Month = 0;
|
|
||||||
LastSaved.Day = 0;
|
|
||||||
LastSaved.Hour = 0;
|
|
||||||
LastSaved.Minute = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Playtime storage
|
/// PlayTime object without a Last Saved timestamp.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
public sealed class PlayTime8b(SAV8BS sav, Memory<byte> raw) : SaveBlock<SAV8BS>(sav, raw)
|
public sealed class PlayTime8b : PlayTime<SaveFile>
|
||||||
{
|
{
|
||||||
public ushort PlayedHours
|
public PlayTime8b(SAV8BS sav, Memory<byte> raw) : base(sav, raw) { }
|
||||||
{
|
public PlayTime8b(SAV8LA sav, SCBlock block) : base(sav, block.Data) { }
|
||||||
get => ReadUInt16LittleEndian(Data);
|
|
||||||
set => WriteUInt16LittleEndian(Data, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte PlayedMinutes { get => Data[2]; set => Data[2] = value; }
|
|
||||||
public byte PlayedSeconds { get => Data[3]; set => Data[3] = value; }
|
|
||||||
public string LastSavedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the amount of time the save file has been played.
|
|
||||||
/// </summary>
|
|
||||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
|
||||||
public sealed class PlayTime8a(SAV8LA sav, SCBlock block) : SaveBlock<SAV8LA>(sav, block.Data)
|
|
||||||
{
|
|
||||||
public ushort PlayedHours
|
|
||||||
{
|
|
||||||
get => ReadUInt16LittleEndian(Data);
|
|
||||||
set => WriteUInt16LittleEndian(Data, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte PlayedMinutes { get => Data[2]; set => Data[2] = value; }
|
|
||||||
public byte PlayedSeconds { get => Data[3]; set => Data[3] = value; }
|
|
||||||
public string PlayedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
|
40
PKHeX.Core/Saves/Substructures/Time/Epoch0000DateTime.cs
Normal file
40
PKHeX.Core/Saves/Substructures/Time/Epoch0000DateTime.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
|
public sealed class Epoch0000DateTime(Memory<byte> Data): EpochDateTime(Data)
|
||||||
|
{
|
||||||
|
// Data should be 4 or 8 bytes where we only care about the first 4 bytes i.e. 32 bits
|
||||||
|
// First 12 bits are year from 0000, next 4 bits are month, next 5 are days, next 5 are hours, next 6 bits are minutes
|
||||||
|
|
||||||
|
private static DateTime Epoch => new(0, 1, 1);
|
||||||
|
|
||||||
|
public override int Year { get => (int)(RawDate & 0xFFF); set => RawDate = (RawDate & 0xFFFFF000) | (uint)(value); }
|
||||||
|
public override int Month { get => (int)((RawDate >> 12) & 0xF); set => RawDate = (RawDate & 0xFFFF0FFF) | (((uint)value & 0xF) << 12); }
|
||||||
|
|
||||||
|
public override DateTime Timestamp
|
||||||
|
{
|
||||||
|
get => new(Year, Month, Day, Hour, Minute, 0);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Year = value.Year;
|
||||||
|
Month = value.Month;
|
||||||
|
Day = value.Day;
|
||||||
|
Hour = value.Hour;
|
||||||
|
Minute = value.Minute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string DisplayValue => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}ː{Timestamp.Second:00}"; // not :
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// time_t
|
||||||
|
/// </summary>
|
||||||
|
public override ulong TotalSeconds
|
||||||
|
{
|
||||||
|
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
||||||
|
set => Timestamp = Epoch.AddSeconds(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,22 +8,17 @@ namespace PKHeX.Core;
|
||||||
/// Stores the <see cref="Timestamp"/> to indicate the seconds since 1900 that an event occurred.
|
/// Stores the <see cref="Timestamp"/> to indicate the seconds since 1900 that an event occurred.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||||
public sealed class Epoch1900DateTimeValue(Memory<byte> Data)
|
public sealed class Epoch1900DateTimeValue(Memory<byte> Data) : EpochDateTime(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
|
// 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
|
// 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) { }
|
public Epoch1900DateTimeValue(SCBlock block) : this(block.Data) { }
|
||||||
|
|
||||||
private static DateTime Epoch => new(1900, 1, 1);
|
private static DateTime Epoch => new(1900, 1, 1);
|
||||||
|
|
||||||
private uint RawDate { get => ReadUInt32LittleEndian(Span); set => WriteUInt32LittleEndian(Span, value); }
|
public override int Year { get => (int)(RawDate & 0xFFF) + Epoch.Year; set => RawDate = (RawDate & 0xFFFFF000) | (uint)(value - Epoch.Year); }
|
||||||
public int Year { get => (int)(RawDate & 0xFFF) + Epoch.Year; set => RawDate = (RawDate & 0xFFFFF000) | (uint)(value - Epoch.Year); }
|
public override int Month { get => (int)((RawDate >> 12) & 0xF) + 1; set => RawDate = (RawDate & 0xFFFF0FFF) | (((uint)(value - 1) & 0xF) << 12); }
|
||||||
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 bool HasSeconds => Span.Length > 4;
|
||||||
public int Second {
|
public int Second {
|
||||||
get => HasSeconds ? Span[4] : 0;
|
get => HasSeconds ? Span[4] : 0;
|
||||||
|
@ -32,7 +27,7 @@ public sealed class Epoch1900DateTimeValue(Memory<byte> Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime Timestamp
|
public override DateTime Timestamp
|
||||||
{
|
{
|
||||||
get => new(Year, Month, Day, Hour, Minute, Second);
|
get => new(Year, Month, Day, Hour, Minute, Second);
|
||||||
set
|
set
|
||||||
|
@ -46,12 +41,12 @@ public sealed class Epoch1900DateTimeValue(Memory<byte> Data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DisplayValue => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}" + (HasSeconds ? $"ː{Timestamp.Second:00}" : ""); // not :
|
public override string DisplayValue => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}" + (HasSeconds ? $"ː{Timestamp.Second:00}" : ""); // not :
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// time_t (seconds since 1900 Epoch)
|
/// time_t (seconds since 1900 Epoch)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong TotalSeconds
|
public override ulong TotalSeconds
|
||||||
{
|
{
|
||||||
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
get => (ulong)(Timestamp - Epoch).TotalSeconds;
|
||||||
set => Timestamp = Epoch.AddSeconds(value);
|
set => Timestamp = Epoch.AddSeconds(value);
|
||||||
|
|
18
PKHeX.Core/Saves/Substructures/Time/EpochDateTime.cs
Normal file
18
PKHeX.Core/Saves/Substructures/Time/EpochDateTime.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public abstract class EpochDateTime(Memory<byte> Data)
|
||||||
|
{
|
||||||
|
protected Span<byte> Span => Data.Span;
|
||||||
|
protected uint RawDate { get => ReadUInt32LittleEndian(Span); set => WriteUInt32LittleEndian(Span, value); }
|
||||||
|
public abstract int Year { get; set;}
|
||||||
|
public abstract int Month { get; set;}
|
||||||
|
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 abstract DateTime Timestamp { get; set; }
|
||||||
|
public abstract string DisplayValue { get; }
|
||||||
|
public abstract ulong TotalSeconds { get; set; }
|
||||||
|
}
|
Loading…
Reference in a new issue