From 6c40e1c769cfad33d2b721b9ddbc27e16bd07ae2 Mon Sep 17 00:00:00 2001 From: Kurt Date: Wed, 27 Jan 2021 20:04:51 -0800 Subject: [PATCH] Initial v1.7.0 Support (#445) * Pre-patch preparations * Update magic values * Check formatting characters directly rather than string compare "Brown".StartsWith("\u000e") >true what let's be paranoid here, because .NET Core behaves differently from .NET framework. * Update FileHashRevision.cs Postbox with the massive decrease in size; likely the same mutation caused the other massive decreases. * Dump item names * Update flag definitions * Update item info * Add feathers to bug list Might remove later. * Update RecipeList.cs * Update ItemRemakeUtil.cs * Update stack details * Update offsets * Update MainSaveOffsets17.cs * Update GameLists.cs --- NHSE.Core/Encryption/Aes128Ctr.cs | 81 +- NHSE.Core/Encryption/CryptoFile.cs | 2 +- NHSE.Core/Encryption/Encryption.cs | 8 +- NHSE.Core/Hashing/FileHashInfo.cs | 12 +- NHSE.Core/Hashing/FileHashRevision.cs | 199 +- NHSE.Core/Resources/byte/item_kind.bin | Bin 13931 -> 14279 bytes NHSE.Core/Resources/byte/item_menuicon.bin | Bin 27862 -> 28558 bytes NHSE.Core/Resources/byte/item_size.bin | Bin 13931 -> 14279 bytes NHSE.Core/Resources/text/de/text_item_de.txt | 4174 +++++++++-------- NHSE.Core/Resources/text/en/text_item_en.txt | 420 +- NHSE.Core/Resources/text/es/text_item_es.txt | 432 +- NHSE.Core/Resources/text/fr/text_item_fr.txt | 422 +- NHSE.Core/Resources/text/it/text_item_it.txt | 430 +- NHSE.Core/Resources/text/jp/text_item_jp.txt | 420 +- NHSE.Core/Resources/text/ko/text_item_ko.txt | 430 +- .../Resources/text/zhs/text_item_zhs.txt | 420 +- .../Resources/text/zht/text_item_zht.txt | 420 +- NHSE.Core/Save/Meta/FileHeaderInfo.cs | 70 +- NHSE.Core/Save/Meta/HorizonSave.cs | 2 + NHSE.Core/Save/Meta/Player.cs | 15 +- NHSE.Core/Save/Meta/RevisionChecker.cs | 75 +- NHSE.Core/Save/Offsets/MainSaveOffsets.cs | 1 + NHSE.Core/Save/Offsets/MainSaveOffsets17.cs | 53 + NHSE.Core/Save/Offsets/PersonalOffsets.cs | 1 + NHSE.Core/Save/Offsets/PersonalOffsets17.cs | 47 + NHSE.Core/Structures/Item/ItemInfo.cs | 7 + NHSE.Core/Structures/Item/ItemKind.cs | 9 +- NHSE.Core/Structures/Item/ItemMenuIconType.cs | 12 + .../Structures/Item/Remake/ItemRemakeInfo.cs | 5 +- .../Item/Remake/ItemRemakeInfoData.cs | 23 +- .../Structures/Item/Remake/ItemRemakeUtil.cs | 11 + NHSE.Core/Structures/RecipeList.cs | 2 + NHSE.Core/Structures/Records/EventFlagLand.cs | 10 +- .../Structures/Records/EventFlagPlayer.cs | 32 +- .../Structures/Records/EventFlagVillager.cs | 5 + .../Records/EventFlagVillagerMemoryPlayer.cs | 8 +- NHSE.Core/Structures/SEADRandom.cs | 50 - NHSE.Core/Structures/XorShift128.cs | 38 + NHSE.Core/Util/FrameworkUtil.cs | 36 + NHSE.Parsing/GameMSBTDumper.cs | 13 +- NHSE.Parsing/GameMSBTDumperNHSE.cs | 8 +- NHSE.Tests/DumpTests.cs | 28 +- 42 files changed, 5886 insertions(+), 2545 deletions(-) create mode 100644 NHSE.Core/Save/Offsets/MainSaveOffsets17.cs create mode 100644 NHSE.Core/Save/Offsets/PersonalOffsets17.cs delete mode 100644 NHSE.Core/Structures/SEADRandom.cs create mode 100644 NHSE.Core/Structures/XorShift128.cs create mode 100644 NHSE.Core/Util/FrameworkUtil.cs diff --git a/NHSE.Core/Encryption/Aes128Ctr.cs b/NHSE.Core/Encryption/Aes128Ctr.cs index 980af34..61c36c7 100644 --- a/NHSE.Core/Encryption/Aes128Ctr.cs +++ b/NHSE.Core/Encryption/Aes128Ctr.cs @@ -29,48 +29,22 @@ namespace NHSE.Core public sealed class Aes128CounterMode : SymmetricAlgorithm { private readonly byte[] _counter; - private readonly AesManaged _aes; + private readonly AesManaged _aes = new() {Mode = CipherMode.ECB, Padding = PaddingMode.None}; public Aes128CounterMode(byte[] counter) { - if (counter == null) - throw new ArgumentNullException(nameof(counter)); - if (counter.Length != 16) - throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {16})"); - - _aes = new AesManaged - { - Mode = CipherMode.ECB, - Padding = PaddingMode.None - }; - + const int expect = 0x10; + if (counter.Length != expect) + throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {expect})"); _counter = counter; } - public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] ignoredParameter) - { - return new CounterModeCryptoTransform(_aes, rgbKey, _counter); - } + public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter); + public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter); - public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] ignoredParameter) - { - return new CounterModeCryptoTransform(_aes, rgbKey, _counter); - } - - public override void GenerateKey() - { - _aes.GenerateKey(); - } - - public override void GenerateIV() - { - // IV not needed in Counter Mode - } - - protected override void Dispose(bool disposing) - { - _aes.Dispose(); - } + public override void GenerateKey() => _aes.GenerateKey(); + public override void GenerateIV() { /* IV not needed in Counter Mode */ } + protected override void Dispose(bool disposing) => _aes.Dispose(); } public sealed class CounterModeCryptoTransform : ICryptoTransform @@ -82,19 +56,14 @@ public sealed class CounterModeCryptoTransform : ICryptoTransform public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] counter) { - if (symmetricAlgorithm == null) - throw new ArgumentNullException(nameof(symmetricAlgorithm)); - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (counter == null) - throw new ArgumentNullException(nameof(counter)); if (counter.Length != symmetricAlgorithm.BlockSize / 8) throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {symmetricAlgorithm.BlockSize / 8})"); _symmetricAlgorithm = symmetricAlgorithm; + _encryptOutput = new byte[counter.Length]; _counter = counter; - var zeroIv = new byte[_symmetricAlgorithm.BlockSize / 8]; + var zeroIv = new byte[counter.Length]; _counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, zeroIv); } @@ -107,40 +76,39 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { + var xm = _xorMask; for (var i = 0; i < inputCount; i++) { - if (NeedMoreXorMaskBytes()) EncryptCounterThenIncrement(); + if (xm.Count == 0) + EncryptCounterThenIncrement(); - var mask = _xorMask.Dequeue(); + var mask = xm.Dequeue(); outputBuffer[outputOffset + i] = (byte)(inputBuffer[inputOffset + i] ^ mask); } return inputCount; } - private bool NeedMoreXorMaskBytes() - { - return _xorMask.Count == 0; - } + private readonly byte[] _encryptOutput; private void EncryptCounterThenIncrement() { - var counterModeBlock = new byte[_symmetricAlgorithm.BlockSize / 8]; + var counterModeBlock = _encryptOutput; _counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0); IncrementCounter(); + var xm = _xorMask; foreach (var b in counterModeBlock) - { - _xorMask.Enqueue(b); - } + xm.Enqueue(b); } private void IncrementCounter() { - for (var i = _counter.Length - 1; i >= 0; i--) + var ctr = _counter; + for (var i = ctr.Length - 1; i >= 0; i--) { - if (++_counter[i] != 0) + if (++ctr[i] != 0) break; } } @@ -150,9 +118,6 @@ private void IncrementCounter() public bool CanTransformMultipleBlocks => true; public bool CanReuseTransform => false; - public void Dispose() - { - _counterEncryptor.Dispose(); - } + public void Dispose() => _counterEncryptor.Dispose(); } } diff --git a/NHSE.Core/Encryption/CryptoFile.cs b/NHSE.Core/Encryption/CryptoFile.cs index 31296e7..c104b3c 100644 --- a/NHSE.Core/Encryption/CryptoFile.cs +++ b/NHSE.Core/Encryption/CryptoFile.cs @@ -20,4 +20,4 @@ public CryptoFile(byte[] data, byte[] key, byte[] ctr) public static bool operator ==(CryptoFile left, CryptoFile right) => left.Data == right.Data; #endregion } -} \ No newline at end of file +} diff --git a/NHSE.Core/Encryption/Encryption.cs b/NHSE.Core/Encryption/Encryption.cs index 4537c09..d6a5562 100644 --- a/NHSE.Core/Encryption/Encryption.cs +++ b/NHSE.Core/Encryption/Encryption.cs @@ -6,16 +6,16 @@ public static class Encryption { private static byte[] GetParam(uint[] data, in int index) { - var sead = new SEADRandom(data[data[index] & 0x7F]); + var rand = new XorShift128(data[data[index] & 0x7F]); var prms = data[data[index + 1] & 0x7F] & 0x7F; var rndRollCount = (prms & 0xF) + 1; for (var i = 0; i < rndRollCount; i++) - sead.GetU64(); + rand.GetU64(); var result = new byte[0x10]; for (var i = 0; i < result.Length; i++) - result[i] = (byte)(sead.GetU32() >> 24); + result[i] = (byte)(rand.GetU32() >> 24); return result; } @@ -47,7 +47,7 @@ public static void Decrypt(byte[] headerData, byte[] encData) private static CryptoFile GenerateHeaderFile(uint seed, byte[] versionData) { // Generate 128 Random uints which will be used for params - var random = new SEADRandom(seed); + var random = new XorShift128(seed); var encryptData = new uint[128]; for (var i = 0; i < encryptData.Length; i++) encryptData[i] = random.GetU32(); diff --git a/NHSE.Core/Hashing/FileHashInfo.cs b/NHSE.Core/Hashing/FileHashInfo.cs index 089b0aa..beeb5df 100644 --- a/NHSE.Core/Hashing/FileHashInfo.cs +++ b/NHSE.Core/Hashing/FileHashInfo.cs @@ -3,19 +3,21 @@ namespace NHSE.Core { -#pragma warning disable CA2237 // Mark ISerializable types with serializable - public sealed class FileHashInfo : Dictionary -#pragma warning restore CA2237 // Mark ISerializable types with serializable + public sealed class FileHashInfo { + private readonly IReadOnlyDictionary List; + public FileHashInfo(IEnumerable hashSets) { + var list = new Dictionary(); foreach (var hashSet in hashSets) - this[hashSet.FileSize] = hashSet; + list[hashSet.FileSize] = hashSet; + List = list; } public FileHashDetails? GetFile(string nameData) { - return this.FirstOrDefault(z => z.Value.FileName == nameData).Value; + return List.Values.FirstOrDefault(z => z.FileName == nameData); } } } diff --git a/NHSE.Core/Hashing/FileHashRevision.cs b/NHSE.Core/Hashing/FileHashRevision.cs index d1ce7c5..33b4b1c 100644 --- a/NHSE.Core/Hashing/FileHashRevision.cs +++ b/NHSE.Core/Hashing/FileHashRevision.cs @@ -5,17 +5,23 @@ /// public static class FileHashRevision { + private const string FN_MAIN = "main.dat"; + private const string FN_PERSONAL = "personal.dat"; + private const string FN_POSTBOX = "postbox.dat"; + private const string FN_PHOTO = "photo_studio_island.dat"; + private const string FN_PROFILE = "profile.dat"; + #region REVISION 1.0.0 - private const int MAIN_SAVE_SIZE = 0xAC0938; - private const int PERSONAL_SAVE_SIZE = 0x6BC50; - private const int POSTBOX_SAVE_SIZE = 0xB44580; - private const int PHOTO_STUDIO_ISLAND_SIZE = 0x263B4; - private const int PROFILE_SIZE = 0x69508; + internal const int REV_100_MAIN = 0xAC0938; + internal const int REV_100_PERSONAL = 0x6BC50; + internal const int REV_100_POSTBOX = 0xB44580; + internal const int REV_100_PHOTO = 0x263B4; + internal const int REV_100_PROFILE = 0x69508; public static readonly FileHashInfo REV_100 = new(new FileHashDetails[] { - new("main.dat", MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_100_MAIN, new FileHashRegion[] { new(0x000108, 0x1D6D4C), new(0x1D6E58, 0x323384), @@ -37,20 +43,20 @@ public static class FileHashRevision new(0x8223E0, 0x03607C), new(0x858460, 0x2684D4) }), - new("personal.dat", PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_100_PERSONAL, new FileHashRegion[] { new(0x00108, 0x35AC4), new(0x35BD0, 0x3607C) }), - new("postbox.dat", POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_100_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4447C) }), - new("photo_studio_island.dat", PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_100_PHOTO, new FileHashRegion[] { new(0x000100, 0x262B0) }), - new("profile.dat", PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_100_PROFILE, new FileHashRegion[] { new(0x000100, 0x69404) }), @@ -60,15 +66,15 @@ public static class FileHashRevision #region REVISION 1.1.0 - private const int REV_110_MAIN_SAVE_SIZE = 0xAC2AA0; - private const int REV_110_PERSONAL_SAVE_SIZE = 0x6BED0; - private const int REV_110_POSTBOX_SAVE_SIZE = 0xB44590; - private const int REV_110_PHOTO_STUDIO_ISLAND_SIZE = 0x263C0; - private const int REV_110_PROFILE_SIZE = 0x69560; + internal const int REV_110_MAIN = 0xAC2AA0; + internal const int REV_110_PERSONAL = 0x6BED0; + internal const int REV_110_POSTBOX = 0xB44590; + internal const int REV_110_PHOTO = 0x263C0; + internal const int REV_110_PROFILE = 0x69560; public static readonly FileHashInfo REV_110 = new(new FileHashDetails[] { - new("main.dat", REV_110_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_110_MAIN, new FileHashRegion[] { new(0x000110, 0x1D6D5C), new(0x1D6E70, 0x323C0C), @@ -90,20 +96,20 @@ public static class FileHashRevision new(0x823E40, 0x0362BC), new(0x85A100, 0x26899C) }), - new("personal.dat", REV_110_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_110_PERSONAL, new FileHashRegion[] { new(0x00110, 0x35AFC), new(0x35C10, 0x362BC) }), - new("postbox.dat", REV_110_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_110_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_110_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_110_PHOTO, new FileHashRegion[] { new(0x000100, 0x262BC) }), - new("profile.dat", REV_110_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_110_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), @@ -113,15 +119,15 @@ public static class FileHashRevision #region REVISION 1.2.0 - private const int REV_120_MAIN_SAVE_SIZE = 0xACECD0; - private const int REV_120_PERSONAL_SAVE_SIZE = 0x6D6C0; - private const int REV_120_POSTBOX_SAVE_SIZE = REV_110_POSTBOX_SAVE_SIZE; - private const int REV_120_PHOTO_STUDIO_ISLAND_SIZE = 0x2C9C0; - private const int REV_120_PROFILE_SIZE = REV_110_PROFILE_SIZE; + internal const int REV_120_MAIN = 0xACECD0; + internal const int REV_120_PERSONAL = 0x6D6C0; + internal const int REV_120_POSTBOX = REV_110_POSTBOX; + internal const int REV_120_PHOTO = 0x2C9C0; + internal const int REV_120_PROFILE = REV_110_PROFILE; public static readonly FileHashInfo REV_120 = new(new FileHashDetails[] { - new("main.dat", REV_120_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_120_MAIN, new FileHashRegion[] { new(0x000110, 0x1D6D5C), new(0x1D6E70, 0x323EBC), @@ -143,20 +149,20 @@ public static class FileHashRevision new(0x82EAB0, 0x03787C), new(0x866330, 0x26899C) }), - new("personal.dat", REV_120_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_120_PERSONAL, new FileHashRegion[] { new(0x00110, 0x35D2C), new(0x35E40, 0x3787C) }), - new("postbox.dat", REV_120_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_120_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_120_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_120_PHOTO, new FileHashRegion[] { new(0x000100, 0x2C8BC) }), - new("profile.dat", REV_120_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_120_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), @@ -166,15 +172,15 @@ public static class FileHashRevision #region REVISION 1.3.0 - private const int REV_130_MAIN_SAVE_SIZE = 0xACED80; - private const int REV_130_PERSONAL_SAVE_SIZE = 0x6D6D0; - private const int REV_130_POSTBOX_SAVE_SIZE = REV_110_POSTBOX_SAVE_SIZE; - private const int REV_130_PHOTO_STUDIO_ISLAND_SIZE = REV_120_PHOTO_STUDIO_ISLAND_SIZE; - private const int REV_130_PROFILE_SIZE = REV_110_PROFILE_SIZE; + internal const int REV_130_MAIN = 0xACED80; + internal const int REV_130_PERSONAL = 0x6D6D0; + internal const int REV_130_POSTBOX = REV_110_POSTBOX; + internal const int REV_130_PHOTO = REV_120_PHOTO; + internal const int REV_130_PROFILE = REV_110_PROFILE; public static readonly FileHashInfo REV_130 = new(new FileHashDetails[] { - new("main.dat", REV_130_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_130_MAIN, new FileHashRegion[] { new(0x000110, 0x1D6D5C), new(0x1D6E70, 0x323EEC), @@ -196,20 +202,20 @@ public static class FileHashRevision new(0x82EB50, 0x03788C), new(0x8663E0, 0x26899C) }), - new("personal.dat", REV_130_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_130_PERSONAL, new FileHashRegion[] { new(0x00110, 0x35D2C), new(0x35E40, 0x3788C) }), - new("postbox.dat", REV_130_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_130_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_130_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_130_PHOTO, new FileHashRegion[] { new(0x000100, 0x2C8BC) }), - new("profile.dat", REV_130_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_130_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), @@ -219,15 +225,15 @@ public static class FileHashRevision #region REVISION 1.4.0 - private const int REV_140_MAIN_SAVE_SIZE = 0xB05790; - private const int REV_140_PERSONAL_SAVE_SIZE = 0x74420; - private const int REV_140_POSTBOX_SAVE_SIZE = REV_110_POSTBOX_SAVE_SIZE; - private const int REV_140_PHOTO_STUDIO_ISLAND_SIZE = REV_120_PHOTO_STUDIO_ISLAND_SIZE; - private const int REV_140_PROFILE_SIZE = REV_110_PROFILE_SIZE; + internal const int REV_140_MAIN = 0xB05790; + internal const int REV_140_PERSONAL = 0x74420; + internal const int REV_140_POSTBOX = REV_110_POSTBOX; + internal const int REV_140_PHOTO = REV_120_PHOTO; + internal const int REV_140_PROFILE = REV_110_PROFILE; public static readonly FileHashInfo REV_140 = new(new FileHashDetails[] { - new("main.dat", REV_140_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_140_MAIN, new FileHashRegion[] { new(0x000110, 0x1d6d5c), new(0x1d6e70, 0x323f2c), @@ -249,20 +255,20 @@ public static class FileHashRevision new(0x85e8c0, 0x03e5dc), new(0x89cea0, 0x2688ec) }), - new("personal.dat", REV_140_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_140_PERSONAL, new FileHashRegion[] { new(0x00110, 0x35D2C), new(0x35E40, 0x3E5DC) }), - new("postbox.dat", REV_140_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_140_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_140_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_140_PHOTO, new FileHashRegion[] { new(0x000100, 0x2C8BC) }), - new("profile.dat", REV_140_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_140_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), @@ -272,15 +278,15 @@ public static class FileHashRevision #region REVISION 1.5.0 - private const int REV_150_MAIN_SAVE_SIZE = 0xB20750; - private const int REV_150_PERSONAL_SAVE_SIZE = 0x76390; - private const int REV_150_POSTBOX_SAVE_SIZE = REV_110_POSTBOX_SAVE_SIZE; - private const int REV_150_PHOTO_STUDIO_ISLAND_SIZE = REV_120_PHOTO_STUDIO_ISLAND_SIZE; - private const int REV_150_PROFILE_SIZE = REV_110_PROFILE_SIZE; + internal const int REV_150_MAIN = 0xB20750; + internal const int REV_150_PERSONAL = 0x76390; + internal const int REV_150_POSTBOX = REV_110_POSTBOX; + internal const int REV_150_PHOTO = REV_120_PHOTO; + internal const int REV_150_PROFILE = REV_110_PROFILE; public static readonly FileHashInfo REV_150 = new(new FileHashDetails[] { - new("main.dat", REV_150_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_150_MAIN, new FileHashRegion[] { new(0x000110, 0x1e215c), new(0x1e2270, 0x323f6c), @@ -302,20 +308,20 @@ public static class FileHashRevision new(0x878520, 0x03f93c), new(0x8b7e60, 0x2688ec) }), - new("personal.dat", REV_150_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_150_PERSONAL, new FileHashRegion[] { new(0x00110, 0x3693c), new(0x36a50, 0x3f93c) }), - new("postbox.dat", REV_150_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_150_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_150_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_150_PHOTO, new FileHashRegion[] { new(0x000100, 0x2C8BC) }), - new("profile.dat", REV_150_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_150_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), @@ -325,15 +331,15 @@ public static class FileHashRevision #region REVISION 1.6.0 - private const int REV_160_MAIN_SAVE_SIZE = 0xB258E0; - private const int REV_160_PERSONAL_SAVE_SIZE = 0x76CF0; - private const int REV_160_POSTBOX_SAVE_SIZE = REV_110_POSTBOX_SAVE_SIZE; - private const int REV_160_PHOTO_STUDIO_ISLAND_SIZE = REV_120_PHOTO_STUDIO_ISLAND_SIZE; - private const int REV_160_PROFILE_SIZE = REV_110_PROFILE_SIZE; + internal const int REV_160_MAIN = 0xB258E0; + internal const int REV_160_PERSONAL = 0x76CF0; + internal const int REV_160_POSTBOX = REV_110_POSTBOX; + internal const int REV_160_PHOTO = REV_120_PHOTO; + internal const int REV_160_PROFILE = REV_110_PROFILE; public static readonly FileHashInfo REV_160 = new(new FileHashDetails[] { - new("main.dat", REV_160_MAIN_SAVE_SIZE, new FileHashRegion[] + new(FN_MAIN, REV_160_MAIN, new FileHashRegion[] { new(0x000110, 0x1e215c), new(0x1e2270, 0x32403c), @@ -355,20 +361,73 @@ public static class FileHashRevision new(0x87c790, 0x04029c), new(0x8bca30, 0x268eac) }), - new("personal.dat", REV_160_PERSONAL_SAVE_SIZE, new FileHashRegion[] + new(FN_PERSONAL, REV_160_PERSONAL, new FileHashRegion[] { new(0x00110, 0x3693c), new(0x36a50, 0x4029c) }), - new("postbox.dat", REV_160_POSTBOX_SAVE_SIZE, new FileHashRegion[] + new(FN_POSTBOX, REV_160_POSTBOX, new FileHashRegion[] { new(0x000100, 0xB4448C) }), - new("photo_studio_island.dat", REV_160_PHOTO_STUDIO_ISLAND_SIZE, new FileHashRegion[] + new(FN_PHOTO, REV_160_PHOTO, new FileHashRegion[] { new(0x000100, 0x2C8BC) }), - new("profile.dat", REV_160_PROFILE_SIZE, new FileHashRegion[] + new(FN_PROFILE, REV_160_PROFILE, new FileHashRegion[] + { + new(0x000100, 0x6945C) + }), + }); + + #endregion + + #region REVISION 1.7.0 + + internal const int REV_170_MAIN = 0x849C30; // reduced size + internal const int REV_170_PERSONAL = 0x64140; // reduced size + internal const int REV_170_POSTBOX = 0x47430; // reduced size + internal const int REV_170_PHOTO = REV_120_PHOTO; + internal const int REV_170_PROFILE = REV_110_PROFILE; + + public static readonly FileHashInfo REV_170 = new(new FileHashDetails[] + { + new(FN_MAIN, REV_170_MAIN, new FileHashRegion[] + { + new(0x000110, 0x1e215c), + new(0x1e2270, 0x3221fc), + new(0x504580, 0x03693c), + new(0x53aec0, 0x02d6ec), + new(0x5686c0, 0x03693c), + new(0x59f000, 0x02d6ec), + new(0x5cc800, 0x03693c), + new(0x603140, 0x02d6ec), + new(0x630940, 0x03693c), + new(0x667280, 0x02d6ec), + new(0x694a80, 0x03693c), + new(0x6cb3c0, 0x02d6ec), + new(0x6f8bc0, 0x03693c), + new(0x72f500, 0x02d6ec), + new(0x75cd00, 0x03693c), + new(0x793640, 0x02d6ec), + new(0x7c0e40, 0x03693c), + new(0x7f7780, 0x02d6ec), + new(0x824e70, 0x024dbc), + }), + new(FN_PERSONAL, REV_170_PERSONAL, new FileHashRegion[] + { + new(0x00110, 0x3693c), + new(0x36a50, 0x2d6ec), + }), + new(FN_POSTBOX, REV_170_POSTBOX, new FileHashRegion[] + { + new(0x000100, 0x4732c) + }), + new(FN_PHOTO, REV_170_PHOTO, new FileHashRegion[] + { + new(0x000100, 0x2C8BC) + }), + new(FN_PROFILE, REV_170_PROFILE, new FileHashRegion[] { new(0x000100, 0x6945C) }), diff --git a/NHSE.Core/Resources/byte/item_kind.bin b/NHSE.Core/Resources/byte/item_kind.bin index e77a91e069e904fd3f771981f5ac5f9b744c0666..9ed832bbe35f797ee4e7952e24c072db459ef5db 100644 GIT binary patch literal 14279 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 adDPaH^=<9x4P}kBDQBST95=4fee6G7TdsBh literal 27862 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 ry delta 199 zcmX?}|2k)b2^S9o0~a#`5HK?^b4?ah7n$78#JM?^tAd#WBnkqX@A4Y3O}3O1m^?v? zW3sBq9(JH&24-gF$p_>_H~$qm!!tQvmv?f32;1g6iW)4FCor*3zAGm?SwT;CbEujZ z%VcL=naK^DHj^(1^G$Bh5&*L0CLiEr*=(+D&J5NXpvyh^o-o_w9=+_z>r@3MTL=gN r6&V0Eux}2~Phg#_AjGnHi-|Df /// Metadata stored in a file's Header, indicating the revision information. /// - [StructLayout(LayoutKind.Sequential)] - public class FileHeaderInfo : IEquatable + [StructLayout(LayoutKind.Explicit)] + public sealed record FileHeaderInfo { public const int SIZE = 0x40; - /* 0x00 */ public uint Major; - /* 0x04 */ public uint Minor; - /* 0x08 */ public ushort Unk1; - /* 0x0A */ public ushort HeaderRevision; - /* 0x0C */ public ushort Unk2; - /* 0x0E */ public ushort SaveRevision; - - public override string ToString() => $"Major = 0x{Major:X}, Minor = 0x{Minor:X}, HeaderRevision = {HeaderRevision}, Unk1 = {Unk1}, SaveRevision = {SaveRevision}, Unk2 = {Unk2}"; - - public bool Equals(FileHeaderInfo? other) - { - if (other is null) - return false; - if (ReferenceEquals(this, other)) - return true; - - if (Major != other.Major) - return false; - if (Minor != other.Minor) - return false; - if (Unk1 != other.Unk1) - return false; - if (HeaderRevision != other.HeaderRevision) - return false; - if (Unk2 != other.Unk2) - return false; - if (SaveRevision != other.SaveRevision) - return false; - return true; - } - - public override bool Equals(object? obj) - { - if (obj is null) - return false; - if (ReferenceEquals(this, obj)) - return true; - if (obj.GetType() != GetType()) - return false; - return Equals((FileHeaderInfo) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = (int) Major; - hashCode = (hashCode * 397) ^ (int) Minor; - hashCode = (hashCode * 397) ^ Unk1.GetHashCode(); - hashCode = (hashCode * 397) ^ HeaderRevision.GetHashCode(); - hashCode = (hashCode * 397) ^ Unk2.GetHashCode(); - hashCode = (hashCode * 397) ^ SaveRevision.GetHashCode(); - return hashCode; - } - } + [field: FieldOffset(0x00)] public uint Major { get; init; } + [field: FieldOffset(0x04)] public uint Minor { get; init; } + [field: FieldOffset(0x08)] public ushort Unk1 { get; init; } + [field: FieldOffset(0x0A)] public ushort HeaderRevision { get; init; } + [field: FieldOffset(0x0C)] public ushort Unk2 { get; init; } + [field: FieldOffset(0x0E)] public ushort SaveRevision { get; init; } } -} \ No newline at end of file +} diff --git a/NHSE.Core/Save/Meta/HorizonSave.cs b/NHSE.Core/Save/Meta/HorizonSave.cs index d9506ec..ac4258a 100644 --- a/NHSE.Core/Save/Meta/HorizonSave.cs +++ b/NHSE.Core/Save/Meta/HorizonSave.cs @@ -66,6 +66,8 @@ public bool ValidateSizes() var sizes = RevisionChecker.SizeInfo[info]; if (Main.Data.Length != sizes.Main) return false; + + // Each player present in the savedata must have been migrated to this revision. foreach (var p in Players) { if (p.Personal.Data.Length != sizes.Personal) diff --git a/NHSE.Core/Save/Meta/Player.cs b/NHSE.Core/Save/Meta/Player.cs index a6e27d5..40c7173 100644 --- a/NHSE.Core/Save/Meta/Player.cs +++ b/NHSE.Core/Save/Meta/Player.cs @@ -15,17 +15,28 @@ public sealed class Player : IEnumerable public readonly PostBox PostBox; public readonly Profile Profile; + /// + /// Directory Name where the player data was loaded from. Not the full path. + /// public readonly string DirectoryName; + + #region Override Implementations public IEnumerator GetEnumerator() => new EncryptedFilePair[] {Personal, Photo, PostBox, Profile}.AsEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public override string ToString() => Personal.PlayerName; + #endregion + /// + /// Imports Player data from the requested . + /// + /// Folder that contains the Player Villager sub-folders. + /// Player object array loaded from the . public static Player[] ReadMany(string folder) { var dirs = Directory.GetDirectories(folder, "Villager*", SearchOption.TopDirectoryOnly); var result = new Player[dirs.Length]; - for (int i = 0; i public static class RevisionChecker { - // Patches where the sizes of individual files changed + /// + /// Unique save file size list by patch. + /// private static readonly SaveFileSizes[] SizesByRevision = { - new(0xAC0938, 0x6BC50, 0x263B4, 0xB44580, 0x69508), // 1.0.0 - new(0xAC2AA0, 0x6BED0, 0x263C0, 0xB44590, 0x69560), // 1.1.0 - new(0xACECD0, 0x6D6C0, 0x2C9C0, 0xB44590, 0x69560), // 1.2.0 - new(0xACED80, 0x6D6D0, 0x2C9C0, 0xB44590, 0x69560), // 1.3.0 - new(0xB05790, 0x74420, 0x2C9C0, 0xB44590, 0x69560), // 1.4.0 - new(0xB20750, 0x76390, 0x2C9C0, 0xB44590, 0x69560), // 1.5.0 - new(0xB258E0, 0x76CF0, 0x2C9C0, 0xB44590, 0x69560), // 1.6.0 + new(REV_100_MAIN, REV_100_PERSONAL, REV_100_PHOTO, REV_100_POSTBOX, REV_100_PROFILE), // 1.0.0 + new(REV_110_MAIN, REV_110_PERSONAL, REV_110_PHOTO, REV_110_POSTBOX, REV_110_PROFILE), // 1.1.0 + new(REV_120_MAIN, REV_120_PERSONAL, REV_120_PHOTO, REV_120_POSTBOX, REV_120_PROFILE), // 1.2.0 + new(REV_130_MAIN, REV_130_PERSONAL, REV_130_PHOTO, REV_130_POSTBOX, REV_130_PROFILE), // 1.3.0 + new(REV_140_MAIN, REV_140_PERSONAL, REV_140_PHOTO, REV_140_POSTBOX, REV_140_PROFILE), // 1.4.0 + new(REV_150_MAIN, REV_150_PERSONAL, REV_150_PHOTO, REV_150_POSTBOX, REV_150_PROFILE), // 1.5.0 + new(REV_160_MAIN, REV_160_PERSONAL, REV_160_PHOTO, REV_160_POSTBOX, REV_160_PROFILE), // 1.6.0 + new(REV_170_MAIN, REV_170_PERSONAL, REV_170_PHOTO, REV_170_POSTBOX, REV_170_PROFILE), // 1.7.0 }; private static readonly FileHeaderInfo[] RevisionInfo = { - new() { Major = 0x67, Minor = 0x6F, HeaderRevision = 0, Unk1 = 2, SaveRevision = 0, Unk2 = 2 }, // 1.0.0 - new() { Major = 0x6D, Minor = 0x78, HeaderRevision = 0, Unk1 = 2, SaveRevision = 1, Unk2 = 2 }, // 1.1.0 - new() { Major = 0x6D, Minor = 0x78, HeaderRevision = 0, Unk1 = 2, SaveRevision = 2, Unk2 = 2 }, // 1.1.1 - new() { Major = 0x6D, Minor = 0x78, HeaderRevision = 0, Unk1 = 2, SaveRevision = 3, Unk2 = 2 }, // 1.1.2 - new() { Major = 0x6D, Minor = 0x78, HeaderRevision = 0, Unk1 = 2, SaveRevision = 4, Unk2 = 2 }, // 1.1.3 - new() { Major = 0x6D, Minor = 0x78, HeaderRevision = 0, Unk1 = 2, SaveRevision = 5, Unk2 = 2 }, // 1.1.4 - new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 6, Unk2 = 2 }, // 1.2.0 - new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 7, Unk2 = 2 }, // 1.2.1 - new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 8, Unk2 = 2 }, // 1.3.0 - new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 9, Unk2 = 2 }, // 1.3.1 + new() { Major = 0x00067, Minor = 0x0006F, HeaderRevision = 0, Unk1 = 2, SaveRevision = 00, Unk2 = 2 }, // 1.0.0 + new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 01, Unk2 = 2 }, // 1.1.0 + new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 02, Unk2 = 2 }, // 1.1.1 + new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 03, Unk2 = 2 }, // 1.1.2 + new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 04, Unk2 = 2 }, // 1.1.3 + new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 05, Unk2 = 2 }, // 1.1.4 + new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 06, Unk2 = 2 }, // 1.2.0 + new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 07, Unk2 = 2 }, // 1.2.1 + new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 08, Unk2 = 2 }, // 1.3.0 + new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 09, Unk2 = 2 }, // 1.3.1 new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 10, Unk2 = 2 }, // 1.4.0 new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 11, Unk2 = 2 }, // 1.4.1 new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 12, Unk2 = 2 }, // 1.4.2 new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 13, Unk2 = 2 }, // 1.5.0 new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 14, Unk2 = 2 }, // 1.5.1 new() { Major = 0x70001, Minor = 0x70006, HeaderRevision = 0, Unk1 = 2, SaveRevision = 15, Unk2 = 2 }, // 1.6.0 + new() { Major = 0x74001, Minor = 0x74005, HeaderRevision = 0, Unk1 = 2, SaveRevision = 16, Unk2 = 2 }, // 1.7.0 }; public static readonly IReadOnlyList SizeInfo = new[] @@ -58,26 +63,28 @@ public static class RevisionChecker SizesByRevision[5], // 1.5.0 SizesByRevision[5], // 1.5.1 SizesByRevision[6], // 1.6.0 + SizesByRevision[7], // 1.7.0 }; public static readonly IReadOnlyList HashInfo = new[] { - FileHashRevision.REV_100, // 1.0.0 - FileHashRevision.REV_110, // 1.1.0 - FileHashRevision.REV_110, // 1.1.1 - FileHashRevision.REV_110, // 1.1.2 - FileHashRevision.REV_110, // 1.1.3 - FileHashRevision.REV_110, // 1.1.4 - FileHashRevision.REV_120, // 1.2.0 - FileHashRevision.REV_120, // 1.2.1 - FileHashRevision.REV_130, // 1.3.0 - FileHashRevision.REV_130, // 1.3.1 - FileHashRevision.REV_140, // 1.4.0 - FileHashRevision.REV_140, // 1.4.1 - FileHashRevision.REV_140, // 1.4.2 - FileHashRevision.REV_150, // 1.5.0 - FileHashRevision.REV_150, // 1.5.1 - FileHashRevision.REV_160, // 1.6.0 + REV_100, // 1.0.0 + REV_110, // 1.1.0 + REV_110, // 1.1.1 + REV_110, // 1.1.2 + REV_110, // 1.1.3 + REV_110, // 1.1.4 + REV_120, // 1.2.0 + REV_120, // 1.2.1 + REV_130, // 1.3.0 + REV_130, // 1.3.1 + REV_140, // 1.4.0 + REV_140, // 1.4.1 + REV_140, // 1.4.2 + REV_150, // 1.5.0 + REV_150, // 1.5.1 + REV_160, // 1.6.0 + REV_170, // 1.7.0 }; public static bool IsRevisionKnown(this FileHeaderInfo info) => info.GetKnownRevisionIndex() >= 0; diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs index d6b6e81..dc92d87 100644 --- a/NHSE.Core/Save/Offsets/MainSaveOffsets.cs +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets.cs @@ -68,6 +68,7 @@ public static MainSaveOffsets GetOffsets(FileHeaderInfo Info) 13 => new MainSaveOffsets15(), 14 => new MainSaveOffsets15(), 15 => new MainSaveOffsets16(), + 16 => new MainSaveOffsets17(), _ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info), }; } diff --git a/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs b/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs new file mode 100644 index 0000000..f4e10d5 --- /dev/null +++ b/NHSE.Core/Save/Offsets/MainSaveOffsets17.cs @@ -0,0 +1,53 @@ +namespace NHSE.Core +{ + /// + /// + /// + public class MainSaveOffsets17 : MainSaveOffsets + { + #region GSaveLand + public const int GSaveLandStart = 0x110; + public override int Animal => GSaveLandStart + 0x10; + + public override int LandMyDesign => GSaveLandStart + 0x1e2600; + public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE); + public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE); + public override int PatternTailor => PatternFlag + DesignPattern.SIZE; + + public const int GSaveWeather = GSaveLandStart + 0x1e23B0; + public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere + public override int WeatherRandSeed => GSaveWeather + 0x18; + + public override int EventFlagLand => GSaveLandStart + 0x20a408; + + // GSaveMainField + public const int GSaveMainFieldStart = GSaveLandStart + 0x20ac08; + 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 PlayerHouseList => GSaveLandStart + 0x2e5634; + public override int NpcHouseList => GSaveLandStart + 0x417634; + + public const int GSaveShop = GSaveLandStart + 0x41887c; + public override int ShopKabu => GSaveShop + 0x2cb0; // part of shop; tailor increased size + public override int Museum => GSaveLandStart + 0x41bba0; + public override int Visitor => GSaveLandStart + 0x41efa4; + public override int SaveFg => GSaveLandStart + 0x41f1d4; + public override int BulletinBoard => GSaveLandStart + 0x41fb18; + public override int AirportThemeColor => GSaveLandStart + 0x500720; + #endregion + + #region GSaveLandOther + public const int GSaveLandOtherStart = 0x504470; + + public override int LostItemBox => GSaveLandOtherStart + 0x340680; + public override int LastSavedTime => GSaveLandOtherStart + 0x344f18; + #endregion + + public override int VillagerSize => Villager2.SIZE; + public override IVillager ReadVillager(byte[] data) => new Villager2(data); + } +} diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets.cs b/NHSE.Core/Save/Offsets/PersonalOffsets.cs index 796f0fe..d0adb53 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets.cs @@ -60,6 +60,7 @@ public static PersonalOffsets GetOffsets(FileHeaderInfo Info) 13 => new PersonalOffsets15(), 14 => new PersonalOffsets15(), 15 => new PersonalOffsets16(), + 16 => new PersonalOffsets17(), _ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info), }; } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets17.cs b/NHSE.Core/Save/Offsets/PersonalOffsets17.cs new file mode 100644 index 0000000..ec92269 --- /dev/null +++ b/NHSE.Core/Save/Offsets/PersonalOffsets17.cs @@ -0,0 +1,47 @@ +namespace NHSE.Core +{ + /// + /// + /// + public sealed class PersonalOffsets17 : PersonalOffsets + { + private const int Player = 0x110; + + public override int PersonalId => Player + 0xAFA8; + public override int EventFlagsPlayer => Player + 0xAFE0; + + private const int GSaveLifeSupport = Player + 0xBFE0; + 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 ProfilePhoto => ProfileMain + 0x14; + public override int ProfileBirthday => ProfileMain + 0x23058; + public override int ProfileFruit => ProfileMain + 0x2305C; + public override int ProfileTimestamp => ProfileMain + 0x230CC; + public override int ProfileIsMakeVillage => ProfileMain + 0x230D0; + + // end player + + private const int PlayerOther = 0x36a50; + + 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 + 0x22594; + public override int Recipes => Bank + 0x10; + + public override int MaxRecipeID => 0x308; + public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu); + } +} diff --git a/NHSE.Core/Structures/Item/ItemInfo.cs b/NHSE.Core/Structures/Item/ItemInfo.cs index 7f226ca..cf13ece 100644 --- a/NHSE.Core/Structures/Item/ItemInfo.cs +++ b/NHSE.Core/Structures/Item/ItemInfo.cs @@ -93,6 +93,9 @@ public static bool TryGetMaxStackCount(ushort id, out ushort max) {Kind_HandheldPennant, 00001}, {Kind_BigbagPresent, 00001}, {Kind_JuiceFuzzyapple, 00001}, + {Kind_Megaphone, 00001}, + {Kind_SoySet, 00001}, + {Kind_MaracasCarnival, 00001}, {Kind_TreeSeedling, 00010}, {Kind_Tree, 00001}, {Kind_BushSeedling, 00010}, @@ -147,6 +150,7 @@ public static bool TryGetMaxStackCount(ushort id, out ushort max) {Kind_DIYRecipe, 00001}, {Kind_MessageBottle, 00001}, {Kind_WrappingPaper, 00010}, + {Kind_Otoshidama, 00010}, {Kind_HousingKit, 00001}, {Kind_HousingKitRcoQuest, 00001}, {Kind_HousingKitBirdge, 00001}, @@ -170,6 +174,8 @@ public static bool TryGetMaxStackCount(ushort id, out ushort max) {Kind_LoveCrystal, 00030}, {Kind_Candy, 00030}, {Kind_HarvestDish, 00001}, + {Kind_Feather, 00003}, + {Kind_RainbowFeather, 00001}, {Kind_Giftbox, 00001}, {Kind_PinataStick, 00001}, {Kind_NpcOutfit, 00001}, @@ -189,6 +195,7 @@ public static bool TryGetMaxStackCount(ushort id, out ushort max) {Kind_EventObjFtr, 00001}, {Kind_NnpcRoomMarker, 00001}, {Kind_PhotoStudioList, 00001}, + {Kind_DummyWrappingOtoshidama, 00001}, }; /// diff --git a/NHSE.Core/Structures/Item/ItemKind.cs b/NHSE.Core/Structures/Item/ItemKind.cs index 589893e..6f1a5ca 100644 --- a/NHSE.Core/Structures/Item/ItemKind.cs +++ b/NHSE.Core/Structures/Item/ItemKind.cs @@ -47,8 +47,10 @@ public enum ItemKind : byte Kind_DummyPresentbox, Kind_DummyRecipe, Kind_DummyWrapping, + Kind_DummyWrappingOtoshidama, Kind_EasterEgg, Kind_EventObjFtr, + Kind_Feather, Kind_Fence, Kind_FierworkHand, Kind_FireworkM, @@ -83,7 +85,9 @@ public enum ItemKind : byte Kind_LostQuest, Kind_LostQuestDust, Kind_LoveCrystal, + Kind_MaracasCarnival, Kind_Medicine, + Kind_Megaphone, Kind_MessageBottle, Kind_MilePlaneTicket, Kind_Money, @@ -97,6 +101,7 @@ public enum ItemKind : byte Kind_NpcOutfit, Kind_Ocarina, Kind_Ore, + Kind_Otoshidama, Kind_Panflute, Kind_Partyhorn, Kind_PartyPopper, @@ -110,6 +115,7 @@ public enum ItemKind : byte Kind_Poster, Kind_QuestChristmasPresentbox, Kind_QuestWrapping, + Kind_RainbowFeather, Kind_RiverMaker, Kind_RollanTicket, Kind_RoomFloor, @@ -128,6 +134,7 @@ public enum ItemKind : byte Kind_SmartPhone, Kind_SnowCrystal, Kind_Socks, + Kind_SoySet, Kind_StarPiece, Kind_StickLight, Kind_TailorTicket, @@ -150,8 +157,8 @@ public enum ItemKind : byte Kind_Windmill, Kind_WoodenStickTool, Kind_WrappingPaper, - Kind_YutaroWisp, Kind_XmasDeco, + Kind_YutaroWisp, Onepiece_Dress, Onepiece_Long, Onepiece_Middle, diff --git a/NHSE.Core/Structures/Item/ItemMenuIconType.cs b/NHSE.Core/Structures/Item/ItemMenuIconType.cs index 7363049..324089f 100644 --- a/NHSE.Core/Structures/Item/ItemMenuIconType.cs +++ b/NHSE.Core/Structures/Item/ItemMenuIconType.cs @@ -5,6 +5,18 @@ namespace NHSE.Core /// public enum ItemMenuIconType : ushort { + _0x096F00E1, + _0x1178E84F, + _0x2B925207, + _0x3CD47393, + _0x599D99CA, + _0x5A07F70B, + _0x5E0B9075, + _0x631DD141, + _0x6C26EAB8, + _0x8CC66ACC, + _0xD4295E14, + _0xE3CF868D, Akoyagai, Amaebi, Anemone0, diff --git a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs index b8891a5..7047a4e 100644 --- a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs +++ b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfo.cs @@ -8,6 +8,7 @@ namespace NHSE.Core public class ItemRemakeInfo { public const int BodyColorCountMax = 8; + public const int NoColor = (int)ItemCustomColor.None; // 14 public readonly short Index; public readonly ushort ItemUniqueID; @@ -38,8 +39,8 @@ public ItemRemakeInfo(short index, ushort id, sbyte count, byte[] bc0, byte[] bc private const string Invalid = nameof(Invalid); - public bool HasBodyColor(int variant) => ReBodyPatternColors0[variant] != 14 || ReBodyPatternColors1[variant] != 14; - public bool HasFabricColor(int variant) => ReFabricPatternColors0[variant] != 14 || ReFabricPatternColors1[variant] != 14; + public bool HasBodyColor(int variant) => ReBodyPatternColors0[variant] != NoColor || ReBodyPatternColors1[variant] != NoColor; + public bool HasFabricColor(int variant) => ReFabricPatternColors0[variant] != NoColor || ReFabricPatternColors1[variant] != NoColor; public string GetBodyDescription(int colorIndex, IRemakeString str) { diff --git a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfoData.cs b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfoData.cs index e64146e..4b83819 100644 --- a/NHSE.Core/Structures/Item/Remake/ItemRemakeInfoData.cs +++ b/NHSE.Core/Structures/Item/Remake/ItemRemakeInfoData.cs @@ -363,14 +363,14 @@ public static class ItemRemakeInfoData {0512, new ItemRemakeInfo(0512, 04017, 3, new byte[] {11, 07, 09, 10, 14, 14, 14, 14}, new byte[] {10, 07, 09, 10, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // shower set {0513, new ItemRemakeInfo(0513, 04104, 5, new byte[] {10, 10, 10, 10, 10, 10, 14, 14}, new byte[] {10, 06, 08, 01, 07, 09, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // frying pan {0515, new ItemRemakeInfo(0515, 04137, 6, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {12, 12, 12, 12, 12, 12, 12, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // diner chair - {0516, new ItemRemakeInfo(0516, 04138, 6, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner counter chair + {0516, new ItemRemakeInfo(0516, 04138, 6, new byte[] {11, 11, 11, 11, 11, 11, 11, 14}, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner counter chair {0517, new ItemRemakeInfo(0517, 04139, 6, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {11, 11, 11, 11, 11, 11, 11, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // diner counter table {0518, new ItemRemakeInfo(0518, 04140, 5, new byte[] {01, 06, 07, 12, 10, 09, 14, 14}, new byte[] {01, 06, 07, 12, 10, 09, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 06, 07, 14, 14, 14, 14, 14}, false)}, // retro gas pump {0519, new ItemRemakeInfo(0519, 04141, 6, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {12, 12, 12, 12, 12, 12, 12, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // diner sofa - {0520, new ItemRemakeInfo(0520, 04142, 6, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner dining table + {0520, new ItemRemakeInfo(0520, 04142, 6, new byte[] {11, 11, 11, 11, 11, 11, 11, 14}, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner dining table {0521, new ItemRemakeInfo(0521, 04143, 4, new byte[] {04, 07, 01, 02, 01, 14, 14, 14}, new byte[] {01, 00, 06, 03, 04, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // diner neon sign {0522, new ItemRemakeInfo(0522, 04144, 6, new byte[] {01, 04, 07, 06, 02, 12, 03, 14}, new byte[] {01, 04, 07, 06, 02, 12, 03, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {13, 04, 10, 14, 14, 14, 14, 14}, false)}, // diner neon clock - {0523, new ItemRemakeInfo(0523, 04441, 6, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner mini table + {0523, new ItemRemakeInfo(0523, 04441, 6, new byte[] {11, 11, 11, 11, 11, 11, 11, 14}, new byte[] {01, 04, 07, 06, 02, 08, 10, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {01, 04, 07, 05, 02, 12, 10, 14}, false)}, // diner mini table {0524, new ItemRemakeInfo(0524, 03406, 4, new byte[] {08, 12, 01, 04, 07, 14, 14, 14}, new byte[] {08, 12, 06, 10, 09, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // beekeeper's hive {0525, new ItemRemakeInfo(0525, 04023, 2, new byte[] {01, 09, 11, 14, 14, 14, 14, 14}, new byte[] {11, 12, 04, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // pants press {0526, new ItemRemakeInfo(0526, 01161, 3, new byte[] {00, 11, 12, 12, 14, 14, 14, 14}, new byte[] {11, 11, 11, 11, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // cream and sugar @@ -487,7 +487,7 @@ public static class ItemRemakeInfoData {0671, new ItemRemakeInfo(0671, 07143, 5, new byte[] {13, 12, 12, 12, 01, 05, 14, 14}, new byte[] {13, 13, 08, 13, 03, 06, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Mom's candle set {0672, new ItemRemakeInfo(0672, 07146, 5, new byte[] {13, 01, 13, 12, 13, 09, 14, 14}, new byte[] {12, 12, 12, 02, 04, 10, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Mom's homemade cake {0673, new ItemRemakeInfo(0673, 07139, 7, new byte[] {13, 13, 12, 13, 13, 13, 13, 13}, new byte[] {09, 08, 10, 07, 07, 12, 07, 12}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Mom's art - {0674, new ItemRemakeInfo(0674, 07135, 7, new byte[] {12, 00, 01, 02, 00, 04, 05, 06}, new byte[] {12, 00, 01, 02, 00, 04, 05, 06}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // floor light + {0674, new ItemRemakeInfo(0674, 07135, 7, new byte[] {12, 00, 01, 02, 03, 04, 05, 06}, new byte[] {12, 00, 01, 02, 03, 04, 05, 06}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // floor light {0675, new ItemRemakeInfo(0675, 07136, 4, new byte[] {12, 10, 01, 05, 02, 14, 14, 14}, new byte[] {06, 06, 06, 06, 06, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // cat grass {0676, new ItemRemakeInfo(0676, 07148, 3, new byte[] {01, 04, 07, 00, 14, 14, 14, 14}, new byte[] {12, 12, 12, 12, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // life ring {0680, new ItemRemakeInfo(0680, 07189, 7, new byte[] {10, 10, 10, 10, 10, 10, 10, 10}, new byte[] {12, 07, 00, 01, 02, 03, 04, 06}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // studio spotlight @@ -616,7 +616,7 @@ public static class ItemRemakeInfoData {0843, new ItemRemakeInfo(0843, 05309, 7, new byte[] {02, 11, 10, 04, 06, 00, 01, 03}, new byte[] {12, 05, 12, 05, 06, 00, 01, 03}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // heart doorplate {0844, new ItemRemakeInfo(0844, 05716, 7, new byte[] {08, 05, 10, 00, 09, 12, 05, 02}, new byte[] {09, 00, 08, 08, 08, 00, 04, 09}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // paw-print doorplate {0845, new ItemRemakeInfo(0845, 04738, 1, new byte[] {08, 12, 14, 14, 14, 14, 14, 14}, new byte[] {09, 01, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // skull doorplate - {0846, new ItemRemakeInfo(0846, 05717, 5, new byte[] {11, 07, 11, 01, 11, 14, 14, 14}, new byte[] {10, 11, 04, 12, 06, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // crest doorplate + {0846, new ItemRemakeInfo(0846, 05717, 5, new byte[] {11, 07, 11, 01, 11, 13, 14, 14}, new byte[] {10, 11, 04, 12, 06, 12, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // crest doorplate {0847, new ItemRemakeInfo(0847, 05719, 1, new byte[] {11, 04, 14, 14, 14, 14, 14, 14}, new byte[] {08, 08, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // fossil doorplate {0848, new ItemRemakeInfo(0848, 04764, 7, new byte[] {05, 01, 00, 07, 06, 03, 10, 11}, new byte[] {12, 12, 12, 12, 12, 12, 12, 12}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // fish doorplate {0849, new ItemRemakeInfo(0849, 04752, 7, new byte[] {10, 12, 04, 11, 08, 07, 00, 09}, new byte[] {10, 12, 04, 11, 08, 07, 00, 09}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // iron doorplate @@ -661,7 +661,7 @@ public static class ItemRemakeInfoData {0895, new ItemRemakeInfo(0895, 12207, 2, new byte[] {01, 11, 04, 14, 14, 14, 14, 14}, new byte[] {11, 11, 11, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // arcade seat {0896, new ItemRemakeInfo(0896, 07259, 3, new byte[] {06, 07, 02, 04, 14, 14, 14, 14}, new byte[] {00, 06, 02, 04, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // palm-tree lamp {0897, new ItemRemakeInfo(0897, 11942, 1, new byte[] {13, 13, 14, 14, 14, 14, 14, 14}, new byte[] {01, 12, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // street piano - {0899, new ItemRemakeInfo(0899, 00667, 1, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // shaved-ice maker + {0899, new ItemRemakeInfo(0899, 00667, 1, new byte[] {05, 12, 14, 14, 14, 14, 14, 14}, new byte[] {04, 05, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // shaved-ice maker {0901, new ItemRemakeInfo(0901, 12332, 2, new byte[] {08, 00, 12, 14, 14, 14, 14, 14}, new byte[] {10, 06, 11, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // natural square table {0920, new ItemRemakeInfo(0920, 12373, 5, new byte[] {01, 06, 11, 13, 00, 10, 14, 14}, new byte[] {12, 07, 11, 12, 00, 01, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Pocket vintage camper {0921, new ItemRemakeInfo(0921, 12372, 5, new byte[] {07, 01, 06, 13, 11, 00, 14, 14}, new byte[] {01, 10, 06, 13, 13, 13, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Pocket modern camper @@ -705,10 +705,21 @@ public static class ItemRemakeInfoData {1295, new ItemRemakeInfo(1295, 13447, 1, new byte[] {12, 11, 14, 14, 14, 14, 14, 14}, new byte[] {12, 11, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day garden stand {1296, new ItemRemakeInfo(1296, 13448, 1, new byte[] {12, 11, 14, 14, 14, 14, 14, 14}, new byte[] {00, 06, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day hearth {1297, new ItemRemakeInfo(1297, 13453, 1, new byte[] {09, 08, 14, 14, 14, 14, 14, 14}, new byte[] {12, 06, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day table + {1495, new ItemRemakeInfo(1495, 13767, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 05, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale garland + {1496, new ItemRemakeInfo(1496, 13776, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 05, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale confetti machine + {1497, new ItemRemakeInfo(1497, 13775, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 12, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale stage + {1498, new ItemRemakeInfo(1498, 13774, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {09, 09, 12, 02, 08, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale drum + {1500, new ItemRemakeInfo(1500, 13770, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 05, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale parasol + {1501, new ItemRemakeInfo(1501, 13772, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 12, 02, 13, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale balloon lamp + {1504, new ItemRemakeInfo(1504, 13773, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 05, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale stall + {1505, new ItemRemakeInfo(1505, 13777, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {07, 00, 05, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale lamp + {1506, new ItemRemakeInfo(1506, 13778, 4, new byte[] {06, 01, 04, 03, 13, 14, 14, 14}, new byte[] {08, 09, 12, 02, 12, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Festivale flag {1508, new ItemRemakeInfo(1508, 13820, 1, new byte[] {00, 06, 14, 14, 14, 14, 14, 14}, new byte[] {12, 12, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day table setting {1509, new ItemRemakeInfo(1509, 13818, 1, new byte[] {12, 06, 14, 14, 14, 14, 14, 14}, new byte[] {00, 06, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day casserole {1510, new ItemRemakeInfo(1510, 13819, 1, new byte[] {09, 07, 14, 14, 14, 14, 14, 14}, new byte[] {08, 06, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // Turkey Day wheat decor + {1516, new ItemRemakeInfo(1516, 13488, 3, new byte[] {09, 10, 12, 02, 14, 14, 14, 14}, new byte[] {01, 07, 04, 05, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // chocolate heart {1524, new ItemRemakeInfo(1524, 13930, 5, new byte[] {13, 02, 04, 11, 01, 11, 14, 14}, new byte[] {13, 12, 05, 04, 12, 08, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // set of stockings + {1546, new ItemRemakeInfo(1546, 14029, 5, new byte[] {01, 02, 07, 03, 12, 10, 14, 14}, new byte[] {12, 12, 00, 12, 06, 11, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, new byte[] {14, 14, 14, 14, 14, 14, 14, 14}, false)}, // heart-shaped bouquet }; } } diff --git a/NHSE.Core/Structures/Item/Remake/ItemRemakeUtil.cs b/NHSE.Core/Structures/Item/Remake/ItemRemakeUtil.cs index d7eb3ca..78465d8 100644 --- a/NHSE.Core/Structures/Item/Remake/ItemRemakeUtil.cs +++ b/NHSE.Core/Structures/Item/Remake/ItemRemakeUtil.cs @@ -1109,11 +1109,22 @@ public static class ItemRemakeUtil {13449, 1293}, // Turkey Day decorations {13450, 1292}, // Turkey Day chair {13453, 1297}, // Turkey Day table + {13488, 1516}, // chocolate heart + {13767, 1495}, // Festivale garland + {13770, 1500}, // Festivale parasol + {13772, 1501}, // Festivale balloon lamp + {13773, 1504}, // Festivale stall + {13774, 1498}, // Festivale drum + {13775, 1497}, // Festivale stage + {13776, 1496}, // Festivale confetti machine + {13777, 1505}, // Festivale lamp + {13778, 1506}, // Festivale flag {13818, 1509}, // Turkey Day casserole {13819, 1510}, // Turkey Day wheat decor {13820, 1508}, // Turkey Day table setting {13862, 0652}, // Jingle's photo {13930, 1524}, // set of stockings + {14029, 1546}, // heart-shaped bouquet }; } } diff --git a/NHSE.Core/Structures/RecipeList.cs b/NHSE.Core/Structures/RecipeList.cs index cc0324e..cd00a7d 100644 --- a/NHSE.Core/Structures/RecipeList.cs +++ b/NHSE.Core/Structures/RecipeList.cs @@ -642,7 +642,9 @@ public static class RecipeList {0x2EE, 13244}, // gift pile {0x2EF, 13792}, // festive wrapping paper {0x2F0, 13603}, // falling-snow wall + {0x2F3, 03548}, // rainbow feather {0x2F5, 12217}, // summer-shell rug + {0x308, 14278}, // mermaid fence }; } } diff --git a/NHSE.Core/Structures/Records/EventFlagLand.cs b/NHSE.Core/Structures/Records/EventFlagLand.cs index 1286539..1f6d77e 100644 --- a/NHSE.Core/Structures/Records/EventFlagLand.cs +++ b/NHSE.Core/Structures/Records/EventFlagLand.cs @@ -323,7 +323,6 @@ public EventFlagLand(short init, short max, ushort index, string name) {0x160, new EventFlagLand(0 , 1 , 0352, "FoxPreVisitAlreadyBuyToday" )}, // つねきち|今日誰かが事前来訪中に美術品を買った {0x161, new EventFlagLand(0 , 1 , 0353, "RcoHasResolvedMoveKitBug" )}, // いせつキットバグを解消したか {0x162, new EventFlagLand(0 , 1 , 0354, "TapDreamEnable" )}, // ゆめみ|ゆめみ機能解禁か? - {0x164, new EventFlagLand(0 , 1 , 0356, "MyDesignPro2" )}, // 追加型マイデザインPro解禁 {0x165, new EventFlagLand(0 , 1 , 0357, "GulBVisitEnable" )}, // 海賊ジョニーが来訪する条件を満たしたか {0x167, new EventFlagLand(0 , 9999 , 0359, "FireworksAddBbsYear" )}, // 花火大会予告の掲示板書き込みをした年 {0x16A, new EventFlagLand(0 , 1 , 0362, "EnableMyDream" )}, // ゆめみ|現在、自分の島の夢を提供中か? @@ -361,12 +360,21 @@ public EventFlagLand(short init, short max, ushort index, string name) {0x18F, new EventFlagLand(0 , 1 , 0399, "TkkFirstLiveNow" )}, // とたけけ|初ライブステージか? {0x190, new EventFlagLand(0 , 1 , 0400, "ChristmasFtrFirstRound" )}, // クリスマス|おもちゃ家具の商品抽選が1巡したか {0x191, new EventFlagLand(0 , 9999 , 0401, "HarvestFestivalAddBbsYear" )}, // ハーベストフェスティバル予告の掲示板書き込みをした年 + {0x192, new EventFlagLand(0 , 1 , 0402, "GrowUpAfterPatch1_7" )}, // 1.7適用して成長処理をした {0x193, new EventFlagLand(0 , 9999 , 0403, "XmasEveAddBbsYear" )}, // クリスマス予告の掲示板書き込みをした年 {0x194, new EventFlagLand(0 , 1 , 0404, "BCAT_EventFlag_005" )}, // クリスマス準備期間解禁 {0x195, new EventFlagLand(0 , -1 , 0405, "RandomKey5" )}, // ランダムキーe {0x196, new EventFlagLand(0 , 1 , 0406, "ShopSocksFlag" )}, // かべかけソックス当選済み + {0x197, new EventFlagLand(0 , -1 , 0407, "ShopHeartChocoSelect" )}, // ハートのチョコレート抽選済みカラバリ + {0x198, new EventFlagLand(0 , -1 , 0408, "ShopHeartFlowerSelect" )}, // ハートのバラブーケ抽選済みカラバリ {0x199, new EventFlagLand(0 , -1 , 0409, "RandomKey6" )}, // ランダムキーf + {0x19A, new EventFlagLand(0 , -1 , 0410, "RandomKey7" )}, // ランダムキーg {0x19C, new EventFlagLand(0 , 1 , 0412, "BCAT_EventFlag_006" )}, // クリスマスイブ解禁 + {0x19D, new EventFlagLand(0 , 1 , 0413, "BCAT_EventFlag_007" )}, // カーニバル本番、バレンタイン本番解禁 + {0x1A3, new EventFlagLand(0 , 9999 , 0419, "ValentineAddBbsYear" )}, // バレンタイン予告の掲示板書き込みをした年 + {0x1A4, new EventFlagLand(0 , 9999 , 0420, "CarnivalAddBbsYear" )}, // カーニバル予告の掲示板書き込みをした年 + {0x1A5, new EventFlagLand(0 , 1 , 0421, "CarnivalNpcFeatherColorDecided" )}, // カーニバル|NPCが欲しがる羽の色決定済み + {0x1A6, new EventFlagLand(0 , 1 , 0422, "CarnivalEventPlazaNpcWander" )}, // カーニバル|広場行動NPCがぶらつくか }; private const string Unknown = "???"; diff --git a/NHSE.Core/Structures/Records/EventFlagPlayer.cs b/NHSE.Core/Structures/Records/EventFlagPlayer.cs index abb0657..2dd63aa 100644 --- a/NHSE.Core/Structures/Records/EventFlagPlayer.cs +++ b/NHSE.Core/Structures/Records/EventFlagPlayer.cs @@ -28,7 +28,6 @@ public EventFlagPlayer(short init, short max, ushort index, string name) {0x009, new EventFlagPlayer(0 , 1 , 0009, "JohnnyQuestFinishFlag" )}, // ジョニークエスト完了フラグ(海賊ジョニーは別フラグ) {0x00A, new EventFlagPlayer(0 , 8 , 0010, "JonnyTalkCount" )}, // 寝ているジョニーに話しかけた回数(海賊ジョニー共用) {0x00B, new EventFlagPlayer(0 , 1 , 0011, "HasPlayedTreasureHunt" )}, // 宝探しクエストをした事がある - {0x00C, new EventFlagPlayer(0 , 1 , 0012, "HasPlayedHideAndSeek" )}, // かくれんぼクエストをした事がある {0x00D, new EventFlagPlayer(0 , 1 , 0013, "ShizueCmnExplanationTowntune" )}, // しずえ|メロ説明聞いたか? {0x00E, new EventFlagPlayer(0 , 1 , 0014, "ShizueCmnExplanationTownflag" )}, // しずえ|村の旗説明聞いたか? {0x00F, new EventFlagPlayer(0 , 1 , 0015, "ShizueCmnExplanationComplaint" )}, // しずえ|『村民のこと』の説明聞いたか? @@ -946,7 +945,6 @@ public EventFlagPlayer(short init, short max, ushort index, string name) {0x47B, new EventFlagPlayer(0 , 1 , 1147, "NpcHalloweenTrickFlag" )}, // ハロウィン|今日イタズラされた? {0x47C, new EventFlagPlayer(0 , 1 , 1148, "TapFirstDreamIn" )}, // ゆめみ|夢の中に入ったことがある? {0x47D, new EventFlagPlayer(0 , 1 , 1149, "TapFirstCheckMydesingShowcase" )}, // ゆめみ|夢の中のマイデザインショーケース端末にアクセスしたことがある? - {0x47E, new EventFlagPlayer(0 , 1 , 1150, "SeekingQuestFirst" )}, // かくれんぼ|かくれんをした事がある {0x47F, new EventFlagPlayer(0 , 1 , 1151, "SpecialMakeChanged" )}, // プレイヤが特殊メイクに変更した {0x480, new EventFlagPlayer(0 , 1 , 1152, "TalkMakeTodayAnyone" )}, // 今日誰かにメイク会話を聞いたか {0x481, new EventFlagPlayer(0 , 1 , 1153, "RcmExplainCandyFlag" )}, // まめきち/ハロウィン|今年アメの説明聞いた? @@ -1032,6 +1030,7 @@ public EventFlagPlayer(short init, short max, ushort index, string name) {0x4E5, new EventFlagPlayer(0 , 10 , 1253, "SantaMissionNpcIndex3" )}, // クリスマス|サンタミッションでお返しをくれるNPC3 {0x4E6, new EventFlagPlayer(0 , 1 , 1254, "ReceiveLeaveTapLetter" )}, // ゆめみ|おまかせ解禁の手紙を受け取った {0x4E7, new EventFlagPlayer(0 , 1 , 1255, "RcmExplainToy" )}, // クリスマス|おもちゃ家具陳列の案内を受けた? + {0x4E8, new EventFlagPlayer(0 , 1 , 1256, "PckTalkTodayFlag" )}, // カーニバル/べルリーナ|今日すでに会話した? {0x4E9, new EventFlagPlayer(0 , 9999, 1257, "ChristmasWreathGetYear" )}, // クリスマス|クリスマスリースをNPCから貰った年 {0x4EA, new EventFlagPlayer(0 , 9 , 1258, "TukSecretRewardType" )}, // ハーベスト|フランクリンの隠し食材報酬抽選結果 {0x4EE, new EventFlagPlayer(0 , 1 , 1262, "TapUpdatedDreamFirstTalk" )}, // ゆめみ|初回会話を1.6.0以降に行った? @@ -1039,6 +1038,7 @@ public EventFlagPlayer(short init, short max, ushort index, string name) {0x4F1, new EventFlagPlayer(0 , 1 , 1265, "CheckHarvestFtrInStore" )}, // ハーベスト|ハーベスト家具をお店でチェックしたことがあるか? {0x4F2, new EventFlagPlayer(0 , 9999, 1266, "ChristmasPresentYear" )}, // クリスマス|自宅プレゼントをもらった年 {0x4F3, new EventFlagPlayer(0 , 2 , 1267, "InputApproachBanCount" )}, // アプローチ|入力系アプローチ禁止カウント + {0x4F4, new EventFlagPlayer(0 , 1 , 1268, "PckLookChatFlag" )}, // カーニバル/べルリーナ|カーニバル装備の雑談した? {0x4F5, new EventFlagPlayer(0 , 1 , 1269, "TukCancel1stRequest" )}, // ハーベスト|フランクリン1品目で「大変ですね」を選択した {0x4F6, new EventFlagPlayer(0 , 1 , 1270, "ChristmasWrappingGiftFlag" )}, // クリスマス|プレゼント交換初回でラッピングもらった? {0x4F8, new EventFlagPlayer(0 , 1 , 1272, "HarvestRefuseGiveHQFood1" )}, // ハーベスト|料理1の隠し食材を渡すのを拒んだ @@ -1050,18 +1050,46 @@ public EventFlagPlayer(short init, short max, ushort index, string name) {0x4FE, new EventFlagPlayer(0 , 2 , 1278, "HarvestGetHint3" )}, // ハーベスト|料理3の隠し食材ヒント聞いた {0x4FF, new EventFlagPlayer(0 , 2 , 1279, "HarvestGetHint4" )}, // ハーベスト|料理4の隠し食材ヒント聞いた {0x500, new EventFlagPlayer(0 , 1 , 1280, "ChristmasWrappingPresentFlag" )}, // クリスマス|ラッピングを誰かくれようとしたか? + {0x501, new EventFlagPlayer(0 , 1 , 1281, "PckRecipeFlag" )}, // カーニバル/べルリーナ|にじいろのはねのレシピもらった? {0x502, new EventFlagPlayer(0 , 1 , 1282, "RcoStorageExpansionReserved" )}, // たぬきち|収納の拡張を予約している {0x503, new EventFlagPlayer(0 , 1 , 1283, "RcoStorageExpansionLevel" )}, // たぬきち|収納の拡張段階 {0x504, new EventFlagPlayer(0 , 1 , 1284, "PlayerMovingReservedStorageExpand" )}, // たぬきち|収納の拡張申し込み当日にPだけ引越しした? {0x505, new EventFlagPlayer(0 , 1 , 1285, "MailSend_NoticeStorageExpansion" )}, // たぬきち|収納の拡張のお知らせ手紙の判定処理したか? {0x506, new EventFlagPlayer(0 , 1 , 1286, "RcoStandbyNoticeStorageExpansion" )}, // たぬきち|収納の拡張について強制会話するか? {0x507, new EventFlagPlayer(0 , 1 , 1287, "ChristmasWreathNoGetFlag" )}, // クリスマス|このNPCからリース受け取り損ねた + {0x508, new EventFlagPlayer(0 , 3 , 1288, "PckGetItem" )}, // カーニバル/べルリーナ|報酬アイテムは? + {0x50A, new EventFlagPlayer(0 , 1 , 1290, "GetCarnivalLight" )}, // カーニバル/べルリーナ|カーニバルなライトもらった? + {0x50B, new EventFlagPlayer(0 , 1 , 1291, "GetCarnivalFoodStand" )}, // カーニバル/べルリーナ|カーニバルな屋台もらった? + {0x50C, new EventFlagPlayer(0 , 1 , 1292, "GetCarnivalstage" )}, // カーニバル/べルリーナ|カーニバルなステージもらった? + {0x50D, new EventFlagPlayer(0 , 1 , 1293, "GetCarnivalConfettiMachine" )}, // カーニバル/べルリーナ|カーニバルなかみふぶきマシンもらった? + {0x50E, new EventFlagPlayer(0 , 1 , 1294, "GetCarnivalPercussion" )}, // カーニバル/べルリーナ|カーニバルなパーカッションもらった? + {0x50F, new EventFlagPlayer(0 , 1 , 1295, "GetCarnivalBalloonLight" )}, // カーニバル/べルリーナ|カーニバルバルーンライトもらった? + {0x510, new EventFlagPlayer(0 , 1 , 1296, "GetCarnivalUmbrellas" )}, // カーニバル/べルリーナ|カーニバルなパラソルもらった? + {0x511, new EventFlagPlayer(0 , 1 , 1297, "GetCarnivalFlag" )}, // カーニバル/べルリーナ|カーニバルなフラッグもらった? + {0x512, new EventFlagPlayer(0 , 1 , 1298, "GetCarnivalGarland" )}, // カーニバル/べルリーナ|カーニバルなガーランドもらった? + {0x513, new EventFlagPlayer(0 , 1 , 1299, "GetCarnivalFloat" )}, // カーニバル/べルリーナ|カーニバルなフロートもらった? + {0x515, new EventFlagPlayer(0 , 10 , 1301, "PckGiveFurnitureCount" )}, // カーニバル/べルリーナ|家具をもらった回数 {0x518, new EventFlagPlayer(0 , 1 , 1304, "SloExplainPumpkinColorFlag" )}, // レイジ|かぼちゃの苗説明聞いたことある? {0x519, new EventFlagPlayer(0 , 1 , 1305, "SloExplainAddPumpkinFlag" )}, // レイジ|かぼちゃの苗販売開始の説明聞いた? {0x51B, new EventFlagPlayer(0 , 1 , 1307, "TukSecretRewardType3rd" )}, // ハーベスト|フランクリンの隠し食材報酬抽選結果(3回目) + {0x51C, new EventFlagPlayer(0 , 1 , 1308, "PckTalkBeforeFlag" )}, // カーニバル/べルリーナ|面識ある? {0x51D, new EventFlagPlayer(0 , 1 , 1309, "SendNNPCConversationPlayReport" )}, // 会話のフリのプレイレポートがその日送信されたか {0x51F, new EventFlagPlayer(0 , 8 , 1311, "ChristmasPrevPresentItemType" )}, // クリスマス|直前にNPCにもらったアイテムの種類 + {0x520, new EventFlagPlayer(0 , 1 , 1312, "ValentineLetterFlag" )}, // バレンタイン|手紙送った? {0x522, new EventFlagPlayer(0 , 1 , 1314, "AnnounceChristmasEve" )}, // クリスマス|当日に島内放送で告知した? + {0x526, new EventFlagPlayer(0 , 511 , 1318, "GetCarnivalFurniture" )}, // カーニバル/ベルリーナ|どのカーニバル家具をもらったか? + {0x52C, new EventFlagPlayer(0 , 9999, 1324, "LastPlayValentineYear" )}, // バレンタイン|最後に遊んだバレンタインの年 + {0x52D, new EventFlagPlayer(0 , 1 , 1325, "PckRecipeTalkFlag" )}, // カーニバル/べルリーナ|にじいろのはねのレシピもらう会話した? + {0x530, new EventFlagPlayer(0 , 1 , 1328, "BuyReactionBook17" )}, // カーニバル用リアクション本を購入した + {0x531, new EventFlagPlayer(0 , 511 , 1329, "GetCarnivalFurnitureRed" )}, // カーニバル/ベルリーナ|どの赤色カーニバル家具をもらったか? + {0x532, new EventFlagPlayer(0 , 511 , 1330, "GetCarnivalFurnitureBlue" )}, // カーニバル/ベルリーナ|どの青色カーニバル家具をもらったか? + {0x533, new EventFlagPlayer(0 , 511 , 1331, "GetCarnivalFurnitureGreen" )}, // カーニバル/ベルリーナ|どの緑色カーニバル家具をもらったか? + {0x534, new EventFlagPlayer(0 , 511 , 1332, "GetCarnivalFurniturePurple" )}, // カーニバル/ベルリーナ|どの紫色カーニバル家具をもらったか? + {0x535, new EventFlagPlayer(0 , 511 , 1333, "GetCarnivalFurnitureRainbow" )}, // カーニバル/ベルリーナ|どの虹色カーニバル家具をもらったか? + {0x536, new EventFlagPlayer(0 , 1 , 1334, "PckColorLotteryFlag" )}, // カーニバル/ベルリーナ|色ごとにカーニバル家具の抽選をするか? + {0x537, new EventFlagPlayer(0 , 1 , 1335, "RcmChkReactionBook" )}, // カーニバル用リアクション本をチェックした + {0x538, new EventFlagPlayer(0 , 1 , 1336, "RcmChkCarnivalFtr" )}, // まめきち|カーニバル家具チェックした? + {0x539, new EventFlagPlayer(0 , 1 , 1337, "AnnounceCarnival" )}, // カーニバル|当日に島内放送で告知した? }; private const string Unknown = "???"; diff --git a/NHSE.Core/Structures/Records/EventFlagVillager.cs b/NHSE.Core/Structures/Records/EventFlagVillager.cs index 83a1a0e..09d2547 100644 --- a/NHSE.Core/Structures/Records/EventFlagVillager.cs +++ b/NHSE.Core/Structures/Records/EventFlagVillager.cs @@ -69,6 +69,11 @@ public EventFlagVillager(short v1, short v2, ushort index, string name) {0x039, new EventFlagVillager(0 , 2 , 0057, "HarvestGiveHint2" )}, // ハーベスト|2品目の隠し食材ヒント出したら1か2をセット {0x03A, new EventFlagVillager(0 , 2 , 0058, "HarvestGiveHint3" )}, // ハーベスト|3品目の隠し食材ヒント出したら1か2をセット {0x03B, new EventFlagVillager(0 , 2 , 0059, "HarvestGiveHint4" )}, // ハーベスト|4品目の隠し食材ヒント出したら1か2をセット + {0x03D, new EventFlagVillager(0 , 3 , 0061, "CarnivalFeatherColor" )}, // カーニバル|欲しがる羽の色 + {0x03E, new EventFlagVillager(0 , 10 , 0062, "DisplayValentinePresent" )}, // バレンタインデー|飾られるブーケの種類 + {0x040, new EventFlagVillager(0 , 1 , 0064, "HarvestDemoEndWait" )}, // ハーベスト|デモ終了待機中か? + {0x041, new EventFlagVillager(0 , 1 , 0065, "WoreNewYearHat" )}, // カウントダウン|ニューイヤーハットを被った + {0x042, new EventFlagVillager(0 , 1 , 0066, "HarvestDemoStateNow" )}, // ハーベスト|デモ参加状態か? }; private const string Unknown = "???"; diff --git a/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs b/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs index 1eb69d8..892036a 100644 --- a/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs +++ b/NHSE.Core/Structures/Records/EventFlagVillagerMemoryPlayer.cs @@ -31,7 +31,7 @@ public EventFlagVillagerMemoryPlayer(byte init, byte max, ushort index, string n {0x08, new EventFlagVillagerMemoryPlayer(0 , 7 , 008, "VisitCount" )}, // そのプレイヤーの家に行った回数 {0x09, new EventFlagVillagerMemoryPlayer(0 , 7 , 009, "VisitedCount" )}, // そのプレイヤーが家に来た回数 {0x0A, new EventFlagVillagerMemoryPlayer(25, 255, 010, "Friendship" )}, // 親密度 - {0x0B, new EventFlagVillagerMemoryPlayer(0 , 7 , 011, "TalkCountToday" )}, // 今日の会話回数(通算) + {0x0B, new EventFlagVillagerMemoryPlayer(0 , 9 , 011, "TalkCountToday" )}, // 今日の会話回数(通算) {0x0C, new EventFlagVillagerMemoryPlayer(0 , 7 , 012, "TalkCountInNpcHouseToday" )}, // 今日NPCの家での会話回数 {0x0D, new EventFlagVillagerMemoryPlayer(0 , 1 , 013, "HasAcquaintanceship" )}, // 面識ありか {0x0E, new EventFlagVillagerMemoryPlayer(0 , 1 , 014, "SitBenchFlag" )}, // NPCをベンチに座らせる @@ -151,7 +151,6 @@ public EventFlagVillagerMemoryPlayer(byte init, byte max, ushort index, string n {0x83, new EventFlagVillagerMemoryPlayer(0 , 1 , 131, "FireworksGetItemFlag" )}, // 花火大会|このNPCからリアクション会話で花火を受け取った {0x84, new EventFlagVillagerMemoryPlayer(0 , 1 , 132, "HaloweenTalkThisSceneFalg" )}, // ハロウィン|このシーンで会話した? {0x85, new EventFlagVillagerMemoryPlayer(0 , 1 , 133, "HaloweenGetCandyThisSceneFalg" )}, // ハロウィン|このシーンでアメもらった? - {0x86, new EventFlagVillagerMemoryPlayer(0 , 1 , 134, "FollowQuestAfter" )}, // 追従|追従クエスト後か? {0x88, new EventFlagVillagerMemoryPlayer(0 , 1 , 136, "HalloweenLastNotGetFlag" )}, // ハロウィン|最後の会話で報酬アイテムもらえなかった? {0x89, new EventFlagVillagerMemoryPlayer(0 , 16 , 137, "HalloweenLastNotGetItem" )}, // ハロウィン|最後の会話でもらえなかった報酬アイテム {0x8A, new EventFlagVillagerMemoryPlayer(0 , 1 , 138, "HarvestItemExchangeToday" )}, // ハーベスト|物々交換を1度でも行ったか? @@ -167,6 +166,11 @@ public EventFlagVillagerMemoryPlayer(byte init, byte max, ushort index, string n {0x94, new EventFlagVillagerMemoryPlayer(0 , 8 , 148, "ChristmasSantaPresentItemType" )}, // クリスマス|サンタミッションであげたプレゼントの種類 {0x95, new EventFlagVillagerMemoryPlayer(0 , 8 , 149, "ChristmasExchangeRemakeId" )}, // クリスマス|プレゼント交換であげたプレゼントのリメイクID {0x96, new EventFlagVillagerMemoryPlayer(0 , 8 , 150, "ChristmasSantaPresentRemakeId" )}, // クリスマス|サンタミッションであげたプレゼントのリメイクID + {0x97, new EventFlagVillagerMemoryPlayer(0 , 1 , 151, "CarnvalTalkWithoutFeatherFlag" )}, // 一般NPC/カーニバル|はねを持たずに会話した? + {0x98, new EventFlagVillagerMemoryPlayer(0 , 1 , 152, "CarnvalExchangeFeatherFlag" )}, // 一般NPC/カーニバル|このNPCとはね交換した? + {0x99, new EventFlagVillagerMemoryPlayer(0 , 1 , 153, "CarnvalTalkOutdoorFlag" )}, // 一般NPC/カーニバル|今日屋外で会話した? + {0x9A, new EventFlagVillagerMemoryPlayer(0 , 1 , 154, "CarnvalExchangeFeatherTalkFlag" )}, // 一般NPC/カーニバル|このNPCとはね交換の会話をした? + {0x9B, new EventFlagVillagerMemoryPlayer(0 , 1 , 155, "ValentinePresentFlag" )}, // バレンタイン|バレンタインのプレゼント渡した? }; private const string Unknown = "???"; diff --git a/NHSE.Core/Structures/SEADRandom.cs b/NHSE.Core/Structures/SEADRandom.cs deleted file mode 100644 index 1beee48..0000000 --- a/NHSE.Core/Structures/SEADRandom.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace NHSE.Core -{ - /// - /// SEAD likely stands for Software Entertainment Analysis & Development - /// - internal sealed class SEADRandom - { - private readonly uint[] state = new uint[4]; - - public SEADRandom(uint seedOne, uint seedTwo, uint seedThree, uint seedFour) - { - state[0] = seedOne; - state[1] = seedTwo; - state[2] = seedThree; - state[3] = seedFour; - } - - public SEADRandom(uint seed) - { - for (int i = 0; i < 4; i++) - { - state[i] = (uint)((0x6C078965 * (seed ^ (seed >> 30))) + i + 1); - seed = state[i]; - } - } - - public uint GetU32() - { - uint v1 = state[0] ^ (state[0] << 11); - - state[0] = state[1]; - state[1] = state[2]; - state[2] = state[3]; - return state[3] = v1 ^ (v1 >> 8) ^ state[3] ^ (state[3] >> 19); - } - - public ulong GetU64() - { - uint v1 = state[0] ^ (state[0] << 11); - uint v2 = state[1]; - uint v3 = v1 ^ (v1 >> 8) ^ state[3]; - - state[0] = state[2]; - state[1] = state[3]; - state[2] = v3 ^ (state[3] >> 19); - state[3] = v2 ^ (v2 << 11) ^ ((v2 ^ (v2 << 11)) >> 8) ^ state[2] ^ (v3 >> 19); - return ((ulong)state[2] << 32) | state[3]; - } - } -} diff --git a/NHSE.Core/Structures/XorShift128.cs b/NHSE.Core/Structures/XorShift128.cs new file mode 100644 index 0000000..300dfeb --- /dev/null +++ b/NHSE.Core/Structures/XorShift128.cs @@ -0,0 +1,38 @@ +namespace NHSE.Core +{ + /// + /// Xorshift128 RNG Implementation (xor128) + /// + /// + internal ref struct XorShift128 + { + private uint a, b, c, d; + private const int Mersenne = 0x6C078965; + + /// + /// Initialize the generator from a seed. + /// + /// Ticks, usually. + public XorShift128(uint seed) + { + // Unrolled Mersenne Twister initialization loop + a = (Mersenne * (seed ^ (seed >> 30))) + 1; + b = (Mersenne * ( a ^ ( a >> 30))) + 2; + c = (Mersenne * ( b ^ ( b >> 30))) + 3; + d = (Mersenne * ( c ^ ( c >> 30))) + 4; + } + + public uint GetU32() + { + uint t = a; + a = b; + b = c; + c = d; + t ^= t << 11; + t ^= t >> 8; + return d = t ^ d ^ (d >> 19); + } + + public ulong GetU64() => ((ulong)GetU32() << 32) | GetU32(); + } +} diff --git a/NHSE.Core/Util/FrameworkUtil.cs b/NHSE.Core/Util/FrameworkUtil.cs new file mode 100644 index 0000000..6cab66c --- /dev/null +++ b/NHSE.Core/Util/FrameworkUtil.cs @@ -0,0 +1,36 @@ +#if !NET5 +#pragma warning disable +// ReSharper disable once UnusedType.Global + +namespace System.Runtime.CompilerServices +{ + using Diagnostics; + using Diagnostics.CodeAnalysis; + + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit + { + } +} + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } +} +#pragma warning restore +#endif \ No newline at end of file diff --git a/NHSE.Parsing/GameMSBTDumper.cs b/NHSE.Parsing/GameMSBTDumper.cs index 27ba4ea..1e28705 100644 --- a/NHSE.Parsing/GameMSBTDumper.cs +++ b/NHSE.Parsing/GameMSBTDumper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -193,12 +194,14 @@ private static (string Label, string Text) GetCleanLabelText(MSBTLabel lbl, ILis var label = lbl.Name; var index = (int)lbl.Index; - var text = txt[index].ToString(Encoding.Unicode); - if (text.StartsWith("\u000e")) // string formatting present; discard formatting! + var bytes = txt[index]; + var text = bytes.ToString(Encoding.Unicode); + var raw = text.ToCharArray(); + int format = Array.FindIndex(raw, z => z == '\u000e'); + if (format == 0) // string formatting present; discard formatting! text = text.Substring(6); - const char germanJunk = '\u000e'; - int junk = text.IndexOf(germanJunk); + int junk = Array.FindIndex(text.ToCharArray(), z => z == '\u000e'); if (junk != -1) // string formatting present; discard formatting! (german) text = text.Substring(0, junk); diff --git a/NHSE.Parsing/GameMSBTDumperNHSE.cs b/NHSE.Parsing/GameMSBTDumperNHSE.cs index de8da6c..42317db 100644 --- a/NHSE.Parsing/GameMSBTDumperNHSE.cs +++ b/NHSE.Parsing/GameMSBTDumperNHSE.cs @@ -12,7 +12,7 @@ public static class GameMSBTDumperNHSE /// /// NHSE language code -> Game Language identifier /// - private static readonly IReadOnlyDictionary Languages = new Dictionary + public static readonly IReadOnlyDictionary Languages = new Dictionary { {"en", "USen"}, {"jp", "JPja"}, @@ -25,11 +25,7 @@ public static class GameMSBTDumperNHSE {"ko", "KRko"}, }; - public static void Dump( - string repoPath = @"C:\Users\Kurt\Documents\GitHub", - string messageStringPath = @"D:\Kurt\Desktop\v16\", - string unpackedMessageFormat = @"Message\String_{0}.sarc" - ) + public static void Dump(string repoPath, string messageStringPath, string unpackedMessageFormat) { string corePath = Path.Combine(repoPath, @"NHSE\NHSE.Core\Resources\text\"); string folder = Path.Combine(messageStringPath, unpackedMessageFormat); diff --git a/NHSE.Tests/DumpTests.cs b/NHSE.Tests/DumpTests.cs index 7c4b2e4..554b61b 100644 --- a/NHSE.Tests/DumpTests.cs +++ b/NHSE.Tests/DumpTests.cs @@ -1,38 +1,30 @@ -using System.Collections.Generic; -using System.IO; +using System.IO; using NHSE.Parsing; using Xunit; +using static NHSE.Parsing.GameMSBTDumperNHSE; namespace NHSE.Tests { public static class DumpTests { - private const string ver = "v16"; + private const string RepoPath = @"C:\Users\Kurt\Documents\GitHub"; + private const string PatchDumpPath = @"D:\Kurt\Desktop\" + PatchFolderName; + private const string PatchFolderName = "v17"; + private const string MessageDumpFormat = @"\Message\String_{0}"; [Fact] public static void DumpBCSV() { - var folder = $@"D:\Kurt\Desktop\{ver}\bcsv"; + const string folder = PatchDumpPath + @"\bcsv"; GameBCSVDumper.UpdateDumps(folder, folder, true); } [Fact] public static void DumpMSBT() { - const string cs = @"C:\Users\Kurt\Documents\GitHub\NHSE\NHSE.Core\Resources\text\"; - string folder = $@"D:\Kurt\Desktop\{ver}\Message\String_{{0}}.sarc"; - var langs = new Dictionary - { - {"en", "USen"}, - {"jp", "JPja"}, - {"zhs", "CNzh"}, - {"zht", "TWzh"}, - {"de", "EUde"}, - {"fr", "EUfr"}, - {"it", "EUit"}, - {"es", "EUes"}, - {"ko", "KRko"}, - }; + const string cs = RepoPath + @"\NHSE\NHSE.Core\Resources\text\"; + const string folder = PatchDumpPath + MessageDumpFormat; + var langs = Languages; foreach (var (code, langName) in langs) {