From e3cca28a2c1f8501dce72ec9ce5624a0b0e5eaf0 Mon Sep 17 00:00:00 2001 From: Kurt Date: Wed, 14 Jan 2026 21:18:11 -0600 Subject: [PATCH] Update for 3.0.0 (no map editing) Map field items/placement is revised, need a fair bit of time to figure out how best to handle it along with backwards version support. --- NHSE.Core/Save/Files/MainSave.cs | 20 +++--- NHSE.Core/Save/Offsets/MainSaveOffsets.cs | 2 + NHSE.Core/Save/Offsets/MainSaveOffsets30.cs | 70 ++++++++++++++------- NHSE.Core/Save/Offsets/PersonalOffsets.cs | 8 +-- NHSE.Core/Save/Offsets/PersonalOffsets30.cs | 31 +++++---- 5 files changed, 82 insertions(+), 49 deletions(-) diff --git a/NHSE.Core/Save/Files/MainSave.cs b/NHSE.Core/Save/Files/MainSave.cs index 0e1df1f..05919e6 100644 --- a/NHSE.Core/Save/Files/MainSave.cs +++ b/NHSE.Core/Save/Files/MainSave.cs @@ -138,7 +138,7 @@ public void SetDesignsTailor(IReadOnlyList value) { var count = Math.Min(Offsets.PatternCount, value.Count); for (int i = 0; i < count; i++) - value[i].Data.CopyTo(Data.Slice(Offsets.PatternTailor + (i * DesignPatternPRO.SIZE))); + value[i].Data.CopyTo(Data[(Offsets.PatternTailor + (i * DesignPatternPRO.SIZE))..]); } private const int EventFlagsSaveCount = 0x400; @@ -166,14 +166,14 @@ public ushort GetAcre(int index) { if ((uint)index > AcreMax) throw new ArgumentOutOfRangeException(nameof(index)); - return ReadUInt16LittleEndian(Data.Slice(Offsets.OutsideField + (index * 2))); + return ReadUInt16LittleEndian(Data[(Offsets.OutsideField + (index * 2))..]); } public void SetAcre(int index, ushort value) { if ((uint)index > AcreMax) throw new ArgumentOutOfRangeException(nameof(index)); - WriteUInt16LittleEndian(Data.Slice(Offsets.OutsideField + (index * 2)), value); + WriteUInt16LittleEndian(Data[(Offsets.OutsideField + (index * 2))..], value); } public byte[] GetAcreBytes() => Data.Slice(Offsets.OutsideField, AcreSizeAll).ToArray(); @@ -182,7 +182,7 @@ public void SetAcreBytes(ReadOnlySpan data) { if (data.Length != AcreSizeAll) throw new ArgumentOutOfRangeException(nameof(data.Length)); - data.CopyTo(Data.Slice(Offsets.OutsideField)); + data.CopyTo(Data[Offsets.OutsideField..]); } public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE)); @@ -224,20 +224,20 @@ public ushort OutsideFieldTemplateUniqueId public ushort MainFieldParamUniqueID { - get => ReadUInt16LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 2)); - set => WriteUInt16LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 2), value); + get => ReadUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 2)..]); + set => WriteUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 2)..], value); } public uint EventPlazaLeftUpX { - get => ReadUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 4)); - set => WriteUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 4), value); + get => ReadUInt32LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 4)..]); + set => WriteUInt32LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 4)..], value); } public uint EventPlazaLeftUpZ { - get => ReadUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 8)); - set => WriteUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 8), value); + get => ReadUInt32LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 8)..]); + set => WriteUInt32LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll + 8)..], value); } public GSaveVisitorNpc Visitor diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs index 9a03405..7753fb7 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs @@ -54,6 +54,8 @@ public abstract class MainSaveOffsets public abstract int PlayerHouseSize { get; } public abstract int PlayerRoomSize { get; } + public virtual int AcreColumnCount => 7; + public abstract IVillager ReadVillager(Memory data); public abstract IVillagerHouse ReadVillagerHouse(Memory data); public abstract IPlayerHouse ReadPlayerHouse(Memory data); diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs index 9abf99f..9dfb0e9 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets30.cs @@ -5,54 +5,78 @@ namespace NHSE.Core; /// /// /// -/// Same as public class MainSaveOffsets30 : MainSaveOffsets { public override int PatternCount => PatternCount2; #region GSaveLand public const int GSaveLandStart = 0x110; + + // GSaveNpc @ 0x000 public override int Animal => GSaveLandStart + 0x10; + // GSaveWeather + public const int GSaveWeather = GSaveLandStart + 0x1e35f0; + public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere + public override int WeatherRandSeed => GSaveWeather + 0x18; + + // GSaveLandMyDesign public override int LandMyDesign => GSaveLandStart + 0x1e3848; public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE); public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE); public override int PatternTailor => PatternFlag + DesignPattern.SIZE; - public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited? - public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited? - - public const int GSaveWeather = GSaveLandStart + 0x1e35f0; - public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere - public override int WeatherRandSeed => GSaveWeather + 0x18; - + // GSaveEventFlagLand public override int EventFlagLand => GSaveLandStart + 0x22ebf0; // GSaveMainField public const int GSaveMainFieldStart = GSaveLandStart + 0x22f3f0; + + // Map size increased to accommodate the Hotel, by adding 2 columns of acres! + public override int AcreColumnCount => 9; // from 7 to 9 + // does this actually impact anything? We'll eventually find out if so; I hope it is just 2 columns of unused. + + // Layer0: 54000 => 6C000 + // Layer1: 54000 => 6C000 + // ItemSwitch0 1500 => 1B00 + // ItemSwitch1 1500 => 1B00 + // MainField 398 => 3AC public override int FieldItem => GSaveMainFieldStart + 0x00000; - public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00; - public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600; - public override int OutsideField => GSaveMainFieldStart + 0xCF998; - public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34; + public override int LandMakingMap => GSaveMainFieldStart + 0xdb600; // CHANGED*** all following are shifted. + public override int MainFieldStructure => GSaveMainFieldStart + 0x100200; + public override int OutsideField => GSaveMainFieldStart + 0x1005ac; + public override int MyDesignMap => GSaveMainFieldStart + 0x100648; - public override int PlayerHouseList => GSaveLandStart + 0x30a6bc; - public override int NpcHouseList => GSaveLandStart + 0x44f7fc; + // GSavePlayerHouseList + public override int PlayerHouseList => GSaveLandStart + 0x33cad0; - public const int GSaveShop = GSaveLandStart + 0x45b50c; + // GSaveNpcHouseList + public override int NpcHouseList => GSaveLandStart + 0x481c10; + + // GSaveShop + public const int GSaveShop = GSaveLandStart + 0x48d920; public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size - public override int Museum => GSaveLandStart + 0x45ec44; - public override int Visitor => GSaveLandStart + 0x462048; - public override int SaveFg => GSaveLandStart + 0x462278; - public override int BulletinBoard => GSaveLandStart + 0x462bc0; - public override int AirportThemeColor => GSaveLandStart + 0x5437c8; + + // GSaveMuseum + public override int Museum => GSaveLandStart + 0x491110; + // GSaveVisitorNpc + public override int Visitor => GSaveLandStart + 0x494514; + // GSaveSaveFg + public override int SaveFg => GSaveLandStart + 0x494760; + // GSaveBulletinBoard + public override int BulletinBoard => GSaveLandStart + 0x4950a8; + public override int AirportThemeColor => GSaveLandStart + 0x575cb0; #endregion #region GSaveLandOther - public const int GSaveLandOtherStart = 0x547520; + public const int GSaveLandOtherStart = 0x5b3d50; - public override int LostItemBox => GSaveLandOtherStart + 0x3726c0; - public override int LastSavedTime => GSaveLandOtherStart + 0x377020; + public const int GSaveMyDesignHolderList = GSaveLandOtherStart + 0x3c9640; + public override int PatternsEditFlagStart => GSaveMyDesignHolderList; // x100, HasPlayerEdited? NormalHolders + public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited? // ProHolders + + public override int LostItemBox => GSaveLandOtherStart + 0x3c4fc0; + public override int LastSavedTime => GSaveLandOtherStart + 0x3c9920; #endregion public override int VillagerSize => Villager2.SIZE; diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets.cs b/NHSE.Core/Save/Offsets/PersonalOffsets.cs index 52921a6..edc88c7 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets.cs @@ -32,10 +32,10 @@ public abstract class PersonalOffsets public abstract int Bank { get; } public abstract int Recipes { get; } - public int MaxAchievementID { get; } = 512; - public int Pockets1Count { get; } = 20; - public int Pockets2Count { get; } = 20; - public int ItemChestCount { get; } = 5000; + public int MaxAchievementID => 512; + public int Pockets1Count => 20; + public int Pockets2Count => 20; + public virtual int ItemChestCount => 5000; public abstract int MaxRecipeID { get; } public abstract int MaxRemakeBitFlag { get; } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets30.cs b/NHSE.Core/Save/Offsets/PersonalOffsets30.cs index 73961f1..a5657fd 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets30.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets30.cs @@ -7,19 +7,22 @@ namespace NHSE.Core; /// public sealed class PersonalOffsets30 : PersonalOffsets { + // GSavePlayer private const int Player = 0x110; - public override int PersonalId => Player + 0xAFA8; - public override int EventFlagsPlayer => Player + 0xAFE0; + // shifted down as LookPack increased from af90->c120 + public override int PersonalId => Player + 0xc138; + public override int EventFlagsPlayer => Player + 0xc170; - private const int GSaveLifeSupport = Player + 0xBFE0; + // GSaveLifeSupport + private const int GSaveLifeSupport = Player + 0xd170; public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement - public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned - public override int Birthday => Player + 0x1228c; - public override int ProfileMain => Player + 0x122a0; + public override int Birthday => Player + 0x1341c; + + public override int ProfileMain => Player + 0x13430; public override int ProfilePhoto => ProfileMain + 0x14; public override int ProfileBirthday => ProfileMain + 0x23058; public override int ProfileFruit => ProfileMain + 0x2305C; @@ -28,19 +31,23 @@ public sealed class PersonalOffsets30 : PersonalOffsets // end player - private const int PlayerOther = 0x36a50; + private const int PlayerOther = 0x37be0; 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 ItemChest => PlayerOther + 0x18C; - public override int ItemCollectBit => PlayerOther + 0xA058; - public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC; - public override int Manpu => PlayerOther + 0xAF7C; - public override int Bank => PlayerOther + 0x24afc; + + // chest increased in size! 9C44 => 11944 + public override int ItemChestCount => 0x11944 / Item.SIZE; // 9000 + + public override int ItemCollectBit => PlayerOther + 0x11d58; + public override int ItemRemakeCollectBit => PlayerOther + 0x124ac; + public override int Manpu => PlayerOther + 0x12c7c; + public override int Bank => PlayerOther + 0x2d69c; public override int Recipes => Bank + 0x10; - public override int MaxRecipeID => 0x430; + public override int MaxRecipeID => 0x430; // unchanged public override int MaxRemakeBitFlag => 0x7D0 * 32; public override IReactionStore ReadReactions(ReadOnlySpan data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure();