mirror of
https://github.com/kwsch/NHSE.git
synced 2026-07-02 00:11:55 -05:00
Replace player/town name on edit
Replaces a byte sequence with the updated sequence of data, using the player/town ID as a prefix to ensure that short names can be changed correctly without affecting other game data (hopefully).
This commit is contained in:
parent
786aad69f0
commit
65fca3cbba
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
|||
/// </remarks>
|
||||
public IEnumerable<FileHashRegion> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// <summary>
|
||||
/// Stores references for all files in the Villager (<see cref="DirectoryName"/>) folder.
|
||||
/// </summary>
|
||||
public sealed class Player
|
||||
public sealed class Player : IEnumerable<EncryptedFilePair>
|
||||
{
|
||||
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<EncryptedFilePair> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the data using the provided crypto <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="seed">Seed to initialize the RNG with when encrypting the files.</param>
|
||||
public void Save(uint seed)
|
||||
{
|
||||
Personal.Save(seed);
|
||||
Photo.Save(seed);
|
||||
PostBox.Save(seed);
|
||||
Profile.Save(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all hashes of the Player's files.
|
||||
/// </summary>
|
||||
public void Hash()
|
||||
{
|
||||
Personal.Hash();
|
||||
Photo.Hash();
|
||||
PostBox.Hash();
|
||||
Profile.Hash();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets every <see cref="FileHashRegion"/> that is deemed invalid.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public IEnumerable<FileHashRegion> InvalidHashes()
|
||||
{
|
||||
return Personal.InvalidHashes()
|
||||
.Concat(Photo.InvalidHashes())
|
||||
.Concat(PostBox.InvalidHashes())
|
||||
.Concat(Profile.InvalidHashes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -178,5 +178,52 @@ internal static T[] ConcatAll<T>(params T[][] arr)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a provided <see cref="pattern"/> within the supplied <see cref="array"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">Array to look in</param>
|
||||
/// <param name="pattern">Pattern to look for</param>
|
||||
/// <param name="startIndex">Starting offset to look from</param>
|
||||
/// <param name="length">Amount of entries to look through</param>
|
||||
/// <returns>Index the pattern occurrs at; if not found, returns -1.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user