diff --git a/NHSE.Core/Save/Files/Personal.cs b/NHSE.Core/Save/Files/Personal.cs index aef5156..bc6e00a 100644 --- a/NHSE.Core/Save/Files/Personal.cs +++ b/NHSE.Core/Save/Files/Personal.cs @@ -10,7 +10,7 @@ public sealed class Personal : EncryptedFilePair { public readonly PersonalOffsets Offsets; public Personal(string folder) : base(folder, "personal") => Offsets = PersonalOffsets.GetOffsets(Info); - public override string ToString() => Name; + public override string ToString() => PlayerName; public uint TownID { @@ -24,18 +24,22 @@ public string TownName set => GetBytes(value, 10).CopyTo(Data, Offsets.PersonalId + 0x04); } + public byte[] GetTownIdentity() => Data.Slice(Offsets.PersonalId + 0x00, 4 + 20); + public uint PlayerID { get => BitConverter.ToUInt32(Data, Offsets.PersonalId + 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.PersonalId + 0x1C); } - public string Name + public string PlayerName { get => GetString(Offsets.PersonalId + 0x20, 10); set => GetBytes(value, 10).CopyTo(Data, Offsets.PersonalId + 0x20); } + public byte[] GetPlayerIdentity() => Data.Slice(Offsets.PersonalId + 0x1C, 4 + 20); + public EncryptedInt32 Wallet { get => EncryptedInt32.Read(Data, Offsets.Wallet); diff --git a/NHSE.Core/Save/Meta/HorizonSave.cs b/NHSE.Core/Save/Meta/HorizonSave.cs index d7b9855..50492e3 100644 --- a/NHSE.Core/Save/Meta/HorizonSave.cs +++ b/NHSE.Core/Save/Meta/HorizonSave.cs @@ -28,8 +28,11 @@ public void Save(uint seed) Main.Save(seed); foreach (var player in Players) { - player.Hash(); - player.Save(seed); + foreach (var pair in player) + { + pair.Hash(); + pair.Save(seed); + } } } @@ -42,7 +45,17 @@ public void Save(uint seed) /// public IEnumerable GetInvalidHashes() { - return Main.InvalidHashes().Concat(Players.SelectMany(z => z.InvalidHashes())); + foreach (var hash in Main.InvalidHashes()) + yield return hash; + foreach (var hash in Players.SelectMany(z => z).SelectMany(z => z.InvalidHashes())) + yield return hash; + } + + public void ChangeIdentity(byte[] original, byte[] updated) + { + Main.Data.ReplaceOccurrences(original, updated); + foreach (var pair in Players.SelectMany(z => z)) + pair.Data.ReplaceOccurrences(original, updated); } } } diff --git a/NHSE.Core/Save/Meta/Player.cs b/NHSE.Core/Save/Meta/Player.cs index f6a6a6b..a6e27d5 100644 --- a/NHSE.Core/Save/Meta/Player.cs +++ b/NHSE.Core/Save/Meta/Player.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,7 +8,7 @@ namespace NHSE.Core /// /// Stores references for all files in the Villager () folder. /// - public sealed class Player + public sealed class Player : IEnumerable { public readonly Personal Personal; public readonly PhotoStudioIsland Photo; @@ -15,7 +16,10 @@ public sealed class Player public readonly Profile Profile; public readonly string DirectoryName; - public override string ToString() => Personal.Name; + public IEnumerator GetEnumerator() => new EncryptedFilePair[] {Personal, Photo, PostBox, Profile}.AsEnumerable().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public override string ToString() => Personal.PlayerName; public static Player[] ReadMany(string folder) { @@ -35,43 +39,5 @@ private Player(string folder) PostBox = new PostBox(folder); Profile = new Profile(folder); } - - /// - /// Saves the data using the provided crypto . - /// - /// Seed to initialize the RNG with when encrypting the files. - public void Save(uint seed) - { - Personal.Save(seed); - Photo.Save(seed); - PostBox.Save(seed); - Profile.Save(seed); - } - - /// - /// Updates all hashes of the Player's files. - /// - public void Hash() - { - Personal.Hash(); - Photo.Hash(); - PostBox.Hash(); - Profile.Hash(); - } - - /// - /// Gets every that is deemed invalid. - /// - /// - /// Doesn't return any metadata about which file the hashes were bad for. - /// Just check what's returned with what's implemented; the offsets are unique enough. - /// - public IEnumerable InvalidHashes() - { - return Personal.InvalidHashes() - .Concat(Photo.InvalidHashes()) - .Concat(PostBox.InvalidHashes()) - .Concat(Profile.InvalidHashes()); - } } } \ No newline at end of file diff --git a/NHSE.Core/Structures/Villager/Villager.cs b/NHSE.Core/Structures/Villager/Villager.cs index 3a710f7..b22a8ab 100644 --- a/NHSE.Core/Structures/Villager/Villager.cs +++ b/NHSE.Core/Structures/Villager/Villager.cs @@ -1,4 +1,5 @@ -using System.Text; +using System; +using System.Text; namespace NHSE.Core { @@ -25,6 +26,36 @@ public VillagerPersonality Personality set => Data[2] = (byte)value; } + public uint TownID + { + get => BitConverter.ToUInt32(Data, 0x04); + set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); + } + + public string TownName + { + get => GetString(0x08, 10); + set => GetBytes(value, 10).CopyTo(Data, 0x08); + } + + public uint PlayerID + { + get => BitConverter.ToUInt32(Data, 0x20); + set => BitConverter.GetBytes(value).CopyTo(Data, 0x20); + } + + public string PlayerName + { + get => GetString(0x24, 10); + set => GetBytes(value, 10).CopyTo(Data, 0x24); + } + + public string TownName2 + { + get => GetString(0x5CC, 10); + set => GetBytes(value, 10).CopyTo(Data, 0x5CC); + } + public string CatchPhrase { get => GetString(0x10014, 2 * 12); diff --git a/NHSE.Core/Util/ArrayUtil.cs b/NHSE.Core/Util/ArrayUtil.cs index 7e2ba0a..3618446 100644 --- a/NHSE.Core/Util/ArrayUtil.cs +++ b/NHSE.Core/Util/ArrayUtil.cs @@ -178,5 +178,52 @@ internal static T[] ConcatAll(params T[][] arr) return result; } + + /// + /// Finds a provided within the supplied . + /// + /// Array to look in + /// Pattern to look for + /// Starting offset to look from + /// Amount of entries to look through + /// Index the pattern occurrs at; if not found, returns -1. + public static int IndexOfBytes(byte[] array, byte[] pattern, int startIndex = 0, int length = -1) + { + int len = pattern.Length; + int endIndex = length > 0 + ? startIndex + length + : array.Length - len - startIndex; + + endIndex = Math.Min(array.Length - pattern.Length, endIndex); + + int i = startIndex; + int j = 0; + while (true) + { + if (pattern[j] != array[i + j]) + { + if (++i == endIndex) + return -1; + j = 0; + } + else if (++j == len) + { + return i; + } + } + } + + public static int ReplaceOccurrences(this byte[] array, byte[] pattern, byte[] swap) + { + int count = 0; + while (true) + { + int ofs = IndexOfBytes(array, pattern); + if (ofs == -1) + return count; + swap.CopyTo(array, ofs); + ++count; + } + } } } diff --git a/NHSE.WinForms/Editor.cs b/NHSE.WinForms/Editor.cs index e308d61..f08e787 100644 --- a/NHSE.WinForms/Editor.cs +++ b/NHSE.WinForms/Editor.cs @@ -150,7 +150,7 @@ private void LoadPlayer(int index) var player = SAV.Players[index]; var pers = player.Personal; - TB_Name.Text = pers.Name; + TB_Name.Text = pers.PlayerName; TB_TownName.Text = pers.TownName; NUD_BankBells.Value = pers.Bank.Value; NUD_NookMiles.Value = pers.NookMiles.Value; @@ -170,8 +170,21 @@ private void SavePlayer(int index) var player = SAV.Players[index]; var pers = player.Personal; - pers.Name = TB_Name.Text; - pers.TownName = TB_TownName.Text; + + if (pers.PlayerName != TB_Name.Text) + { + var orig = pers.GetPlayerIdentity(); + pers.PlayerName = TB_Name.Text; + var updated = pers.GetPlayerIdentity(); + SAV.ChangeIdentity(orig, updated); + } + if (pers.TownName != TB_Name.Text) + { + var orig = pers.GetTownIdentity(); + pers.TownName = TB_TownName.Text; + var updated = pers.GetTownIdentity(); + SAV.ChangeIdentity(orig, updated); + } var bank = pers.Bank; bank.Value = (uint)NUD_BankBells.Value; @@ -189,7 +202,6 @@ private void SavePlayer(int index) #region Villager Editing - private Villager? villager; private int VillagerIndex = -1; private void LoadVillager(int index) @@ -208,12 +220,11 @@ private void LoadVillager(int index) TB_Catchphrase.Text = v.CatchPhrase; VillagerIndex = index; - villager = v; } private void SaveVillager(int index) { - var v = villager; + var v = SAV.Main.GetVillager(index); if (v is null) return; @@ -248,7 +259,7 @@ private void Menu_SavePNG_Click(object sender, EventArgs e) string name; if (pb == PB_Player) - name = SAV.Players[PlayerIndex].Personal.Name; + name = SAV.Players[PlayerIndex].Personal.PlayerName; else if (pb == PB_Villager) name = L_ExternalName.Text; else