Revise Hotel Tickets get/set to indirect access

Allows for expansion in properties available for editing via updating Personal30's get/set list
Seems like a better compromise to only update 1 class for new additions, rather than backporting a stubbed invalid offset.

Behaves similar to WhereAreN being a nullable (optional) property.

In an ideal world, we'd have interfaces and concrete implementations for every structure revision, but that's far too much work to cleanly implement compared to the picky offset slicing.
This commit is contained in:
Kurt 2026-01-16 11:06:00 -06:00
parent bbc5c6eefc
commit 15c6e7169f
19 changed files with 45 additions and 27 deletions

View File

@ -66,12 +66,6 @@ public EncryptedInt32 TotalNookMiles
set => value.Write(Data[Offsets.TotalPoint..]);
}
public EncryptedInt32 Tickets
{
get => EncryptedInt32.ReadVerify(Data, Offsets.Tickets);
set => value.Write(Data[Offsets.Tickets..]);
}
public IReadOnlyList<Item> Bag // Slots 21-40
{
get => Item.GetArray(Data.Slice(Offsets.Pockets1, Offsets.Pockets1Count * Item.SIZE));
@ -179,5 +173,10 @@ public bool ProfileIsMakeVillage
set => Data[Offsets.ProfileIsMakeVillage] = (byte)(value ? 1 : 0);
}
/// <summary>
/// Appended structure added in 3.0.0 for Hotel data.
/// </summary>
public Personal30? Data30 => (Offsets as IPersonal30)?.Get30s_064c1881(Raw);
#endregion
}

View File

@ -13,6 +13,7 @@ public abstract class EncryptedFilePair
private readonly byte[] RawData;
private readonly byte[] RawHeader;
protected Memory<byte> Raw => RawData;
public Span<byte> Data => RawData;
public Span<byte> Header => RawHeader;

View File

@ -0,0 +1,29 @@
using System;
namespace NHSE.Core;
public sealed class Personal30(Memory<byte> raw)
{
public Span<byte> Data => raw.Span;
public EncryptedInt32 HotelTickets // @0x0 size 0x8, align 4
{
get => EncryptedInt32.ReadVerify(Data, 0);
set => value.Write(Data);
}
}
public interface IPersonal30
{
public int Offset30s_064c1881 { get; }
public int Length30s_064c1881 { get; }
}
public static class Personal30Extensions
{
extension(IPersonal30 personal)
{
public Personal30 Get30s_064c1881(Memory<byte> data)
=> new(data.Slice(personal.Offset30s_064c1881, personal.Length30s_064c1881));
}
}

View File

@ -13,7 +13,6 @@ public abstract class PersonalOffsets
public abstract int Wallet { get; }
public abstract int NowPoint { get; }
public abstract int TotalPoint { get; }
public abstract int Tickets { get; }
public abstract int Birthday { get; }
public abstract int ProfileMain { get; }

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets10 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x4;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x180;
public override int ItemCollectBit => PlayerOther + 0xA04C;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7A0;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets11 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -34,7 +34,6 @@ public sealed class PersonalOffsets110 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -34,7 +34,6 @@ public sealed class PersonalOffsets111 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets12 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets13 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets14 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets15 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets16 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets17 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -34,7 +34,6 @@ public sealed class PersonalOffsets18 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -34,7 +34,6 @@ public sealed class PersonalOffsets19 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -33,7 +33,6 @@ public sealed class PersonalOffsets20 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => 0; // Does not exist in saves older than Offsets30 (31)
public override int ItemChest => PlayerOther + 0x18C;
public override int ItemCollectBit => PlayerOther + 0xA058;
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;

View File

@ -5,7 +5,7 @@ namespace NHSE.Core;
/// <summary>
/// <inheritdoc cref="PersonalOffsets"/>
/// </summary>
public sealed class PersonalOffsets30 : PersonalOffsets
public sealed class PersonalOffsets30 : PersonalOffsets, IPersonal30
{
// GSavePlayer
private const int Player = 0x110;
@ -36,7 +36,6 @@ public sealed class PersonalOffsets30 : PersonalOffsets
public override int Pockets1 => PlayerOther + 0x10;
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
public override int Tickets => PlayerOther + 0x3c6d0; //0x742b0;
public override int ItemChest => PlayerOther + 0x18C;
// chest increased in size! 9C44 => 11944
@ -51,6 +50,10 @@ public sealed class PersonalOffsets30 : PersonalOffsets
public override int MaxRecipeID => 0x430; // unchanged
public override int MaxRemakeBitFlag => 0x7D0 * 32;
// Additional struct added in 3.0.0 for Hotel; fetch via Offset's interface extension method.
public int Offset30s_064c1881 => PlayerOther + 0x3C6D0;
public int Length30s_064c1881 => 0x790;
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
}

View File

@ -303,9 +303,9 @@ private void LoadPlayer(int index)
NUD_PocketCount2.Value = Math.Min(int.MaxValue, pers.BagCount);
NUD_StorageCount.Value = Math.Min(int.MaxValue, pers.ItemChestCount);
if (SAV.Main.Info.GetKnownRevisionIndex() >= 31)
if (pers.Data30 is { } addition)
{
NUD_HotelTickets.Value = Math.Min(int.MaxValue, pers.Tickets.Value);
NUD_HotelTickets.Value = Math.Min(int.MaxValue, addition.HotelTickets.Value);
}
else
{
@ -379,11 +379,11 @@ private void SavePlayer(int index)
pers.ItemChestCount = (uint)NUD_StorageCount.Value;
if (SAV.Main.Info.GetKnownRevisionIndex() >= 31)
if (player.Personal.Data30 is { } addition)
{
var tickets = pers.Tickets;
var tickets = addition.HotelTickets;
tickets.Value = (uint)NUD_HotelTickets.Value;
pers.Tickets = tickets;
addition.HotelTickets = tickets;
}
if (player.WhereAreN is { } x)