Add player & villager house dump/load

share your house, but not your toothbrush!
This commit is contained in:
Kurt
2020-04-19 16:23:59 -07:00
parent 5d51cf65df
commit d21b99978a
14 changed files with 386 additions and 30 deletions

View File

@@ -44,6 +44,34 @@ public IReadOnlyList<Building> Buildings
set => Building.SetArray(value).CopyTo(Data, Offsets.Buildings);
}
public PlayerHouse GetPlayerHouse(int index)
{
if ((uint)index >= MainSaveOffsets.PlayerCount)
throw new ArgumentOutOfRangeException(nameof(index));
return Data.Slice(Offsets.PlayerHouseList + (index * PlayerHouse.SIZE), PlayerHouse.SIZE).ToClass<PlayerHouse>();
}
public void SetPlayerHouse(PlayerHouse h, int index)
{
if ((uint)index >= MainSaveOffsets.PlayerCount)
throw new ArgumentOutOfRangeException(nameof(index));
h.ToBytesClass().CopyTo(Data, Offsets.PlayerHouseList + (index * PlayerHouse.SIZE));
}
public VillagerHouse GetVillagerHouse(int index)
{
if ((uint)index >= MainSaveOffsets.VillagerCount)
throw new ArgumentOutOfRangeException(nameof(index));
return Data.Slice(Offsets.NpcHouseList + (index * VillagerHouse.SIZE), VillagerHouse.SIZE).ToClass<VillagerHouse>();
}
public void SetVillagerHouse(VillagerHouse h, int index)
{
if ((uint)index >= MainSaveOffsets.VillagerCount)
throw new ArgumentOutOfRangeException(nameof(index));
h.ToBytesClass().CopyTo(Data, Offsets.NpcHouseList + (index * VillagerHouse.SIZE));
}
public TurnipStonk Turnips
{
get => Data.Slice(Offsets.TurnipExchange, TurnipStonk.SIZE).ToClass<TurnipStonk>();

View File

@@ -7,6 +7,8 @@ namespace NHSE.Core
/// </summary>
public abstract class MainSaveOffsets
{
public const int PlayerCount = 8;
public abstract int Villager { get; }
public const int VillagerSize = 0x12AB0;
public const int VillagerCount = 10;
@@ -22,9 +24,12 @@ public abstract class MainSaveOffsets
public abstract int TurnipExchange { get; }
public abstract int EventFlagLand { get; }
public abstract int FieldItem { get; }
public abstract int Acres { get; }
public abstract int Terrain { get; }
public abstract int PlayerHouseList { get; }
public abstract int NpcHouseList { get; }
public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
{

View File

@@ -7,10 +7,16 @@ public class MainSaveOffsets10 : MainSaveOffsets
{
public override int Villager => 0x110;
public override int Patterns => 0x1D72F0;
public override int EventFlagLand => FieldItem - 0x800;
public override int FieldItem => 0x2018FC;
public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts???
public override int Terrain => Buildings - 0x24C00;
public override int Buildings => 0x2D0EFC;
public override int Acres => 0x2D1294;
public override int PlayerHouseList => FieldItem + 0xDAA2C;
public override int NpcHouseList => PlayerHouseList + (PlayerCount * PlayerHouse.SIZE);
public override int TurnipExchange => 0x4118C0;
public override int RecycleBin => 0xABC000; // yep.
}

View File

@@ -7,10 +7,18 @@ public class MainSaveOffsets11 : MainSaveOffsets
{
public override int Villager => 0x120;
public override int Patterns => 0x1D7310; // +0x20 from v1.0
public override int FieldItem => 0x20191C; // +0x20 from v1.0
public override int Terrain => Buildings - 0x24C00; // dunno where exactly it starts??? // +0x20 from v1.0
public override int EventFlagLand => FieldItem - 0x800;
// GSaveMainField
public override int FieldItem => 0x20191C; // +0x20 from v1.0 // Layer 1&2, and 2*0x1500
public override int Terrain => Buildings - 0x24C00;// +0x20 from v1.0 // LandMakingMap
public override int Buildings => 0x2D0F1C; // +0x20 from v1.0
public override int Acres => 0x2D12B4; // +0x20 from v1.0
public override int PlayerHouseList => FieldItem + 0xDAA2C;
public override int NpcHouseList => PlayerHouseList + (PlayerCount * PlayerHouse.SIZE);
public override int TurnipExchange => 0x412060; // +0x7A0 from v1.0
public override int RecycleBin => 0xABDE70; // +0x1E70 from v1.0
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
namespace NHSE.Core
@@ -16,7 +17,7 @@ public static class GameFileDumper
/// <param name="path">Path to dump to</param>
public static void Dump(this HorizonSave sav, string path)
{
sav.Main.Dump(path);
sav.Main.DumpVillagerHouses(path);
foreach (var p in sav.Players)
{
var dir = Path.Combine(path, p.DirectoryName);
@@ -53,7 +54,54 @@ private static void Dump(string path, byte[] data, string name)
}
/// <summary>
/// Dumps all villagers in their decrypted state to the requested <see cref="path"/>.
/// Dumps all villager houses to the requested <see cref="path"/>.
/// </summary>
/// <param name="sav">Save Data to dump from</param>
/// <param name="path">Path to dump to</param>
public static void DumpPlayerHouses(this HorizonSave sav, string path)
{
var count = Math.Min(sav.Players.Length, MainSaveOffsets.PlayerCount);
for (int i = 0; i < count; i++)
{
var p = sav.Players[i];
var h = sav.Main.GetPlayerHouse(i);
h.Dump(path, p.Personal);
}
}
private static void Dump(this PlayerHouse h, string path, Personal p)
{
var name = GameInfo.Strings.GetVillager(p.PlayerName);
var dest = Path.Combine(path, $"{name}.nhph");
var data = h.ToBytesClass();
File.WriteAllBytes(dest, data);
}
/// <summary>
/// Dumps all villager houses to the requested <see cref="path"/>.
/// </summary>
/// <param name="sav">Save Data to dump from</param>
/// <param name="path">Path to dump to</param>
public static void DumpVillagerHouses(this MainSave sav, string path)
{
for (int i = 0; i < MainSaveOffsets.VillagerCount; i++)
{
var v = sav.GetVillager(i);
var h = sav.GetVillagerHouse(i);
h.Dump(path, v);
}
}
private static void Dump(this VillagerHouse h, string path, Villager v)
{
var name = GameInfo.Strings.GetVillager(v.InternalName);
var dest = Path.Combine(path, $"{name}.nhvh");
var data = h.ToBytesClass();
File.WriteAllBytes(dest, data);
}
/// <summary>
/// Dumps all villagers to the requested <see cref="path"/>.
/// </summary>
/// <param name="villagers">Data to dump from</param>
/// <param name="path">Path to dump to</param>
@@ -71,7 +119,7 @@ private static void DumpVillager(this Villager v, string path)
}
/// <summary>
/// Dumps all villagers in their decrypted state to the requested <see cref="path"/>.
/// Dumps all designs to the requested <see cref="path"/>.
/// </summary>
/// <param name="sav">Save Data to dump from</param>
/// <param name="path">Path to dump to</param>

View File

@@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
namespace NHSE.Core
{
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 1)]
public struct GSaveItemName
{
public const int SIZE = 0x08;
[field: FieldOffset(0x00)] public ushort UniqueID { get; set; }
[field: FieldOffset(0x02)] public byte SystemParam { get; set; }
[field: FieldOffset(0x03)] public byte AdditionalParam { get; set; }
[field: FieldOffset(0x04)] public uint FreeParam { get; set; }
// ReSharper disable once NonReadonlyMemberInGetHashCode
public override int GetHashCode() => UniqueID;
public override bool Equals(object obj) => obj is GSaveItemName i && i.Equals(this);
public bool Equals(GSaveItemName obj) => obj.UniqueID == UniqueID && obj.SystemParam == SystemParam && obj.AdditionalParam == AdditionalParam;
public static bool operator ==(GSaveItemName left, GSaveItemName right) => left.Equals(right);
public static bool operator !=(GSaveItemName left, GSaveItemName right) => !(left == right);
}
}

View File

@@ -0,0 +1,29 @@
namespace NHSE.Core
{
public interface IHouseInfo
{
uint HouseLevel { get; set; }
ushort WallUniqueID { get; set; }
ushort RoofUniqueID { get; set; }
ushort DoorUniqueID { get; set; }
ushort OrderWallUniqueID { get; set; }
ushort OrderRoofUniqueID { get; set; }
ushort OrderDoorUniqueID { get; set; }
GSaveItemName DoorDecoItemName { get; set; }
}
public static class HouseInfoExtensions
{
public static void CopyFrom(this IHouseInfo dest, IHouseInfo src)
{
dest.HouseLevel = src.HouseLevel;
dest.WallUniqueID = src.WallUniqueID;
dest.RoofUniqueID = src.RoofUniqueID;
dest.DoorUniqueID = src.DoorUniqueID;
dest.OrderWallUniqueID = src.OrderWallUniqueID;
dest.OrderRoofUniqueID = src.OrderRoofUniqueID;
dest.OrderDoorUniqueID = src.OrderDoorUniqueID;
dest.DoorDecoItemName = src.DoorDecoItemName;
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Runtime.InteropServices;
namespace NHSE.Core
{
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 4)]
public class PlayerHouse : IHouseInfo
{
public const int SIZE = 0x26400;
[field: FieldOffset(0x000)] public uint HouseLevel { get; set; }
[field: FieldOffset(0x004)] public ushort WallUniqueID { get; set; }
[field: FieldOffset(0x006)] public ushort RoofUniqueID { get; set; }
[field: FieldOffset(0x008)] public ushort DoorUniqueID { get; set; }
[field: FieldOffset(0x00A)] public ushort OrderWallUniqueID { get; set; }
[field: FieldOffset(0x00C)] public ushort OrderRoofUniqueID { get; set; }
[field: FieldOffset(0x00E)] public ushort OrderDoorUniqueID { get; set; }
[field: FieldOffset(0x263D4)] public GSaveItemName DoorDecoItemName { get; set; }
[field: FieldOffset(0x263E0)] public GSaveItemName PostItemName { get; set; }
[field: FieldOffset(0x263E8)] public GSaveItemName OrderPostItemName { get; set; }
}
}

View File

@@ -3,8 +3,8 @@
namespace NHSE.Core
{
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 4)]
public class VillagerHouse
{
public class VillagerHouse : IHouseInfo
{
public const int SIZE = 0x1D4;
public const int ItemCount = 36;
@@ -23,21 +23,4 @@ public class VillagerHouse
[field: FieldOffset(0x1C8)] public GSaveItemName DoorDecoItemName { get; set; }
[field: FieldOffset(0x1D0)] public sbyte BuildPlayer { get; set; }
}
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 1)]
public struct GSaveItemName
{
public const int SIZE = 0x08;
[field: FieldOffset(0x00)] public ushort UniqueID { get; set; }
[field: FieldOffset(0x02)] public byte SystemParam { get; set; }
[field: FieldOffset(0x03)] public byte AdditionalParam { get; set; }
[field: FieldOffset(0x04)] public uint FreeParam { get; set; }
// ReSharper disable once NonReadonlyMemberInGetHashCode
public override int GetHashCode() => UniqueID;
public override bool Equals(object obj) => obj is GSaveItemName i && i.Equals(this);
public bool Equals(GSaveItemName obj) => obj.UniqueID == UniqueID && obj.SystemParam == SystemParam && obj.AdditionalParam == AdditionalParam;
public static bool operator ==(GSaveItemName left, GSaveItemName right) => left.Equals(right);
public static bool operator !=(GSaveItemName left, GSaveItemName right) => !(left == right);
}
}

View File

@@ -12,6 +12,7 @@ public class MarshalTests
[Fact] public void MarshalTurnip() => MarshalTest<TurnipStonk>(TurnipStonk.SIZE);
[Fact] public void MarshalBuilding() => MarshalTest<Building>(Building.SIZE);
[Fact] public void MarshalVillagerHouse() => MarshalTest<VillagerHouse>(VillagerHouse.SIZE);
[Fact] public void MarshalPlayerHouse() => MarshalTest<PlayerHouse>(PlayerHouse.SIZE);
[Fact] public void MarshalVillagerHouseItem() => MarshalTestS<VillagerHouseItem>(VillagerHouseItem.SIZE);
[Fact] public void MarshalGSaveItemName() => MarshalTestS<GSaveItemName>(GSaveItemName.SIZE);

View File

@@ -46,6 +46,8 @@ private void InitializeComponent()
this.PB_Villager = new System.Windows.Forms.PictureBox();
this.L_VillagerID = new System.Windows.Forms.Label();
this.NUD_Villager = new System.Windows.Forms.NumericUpDown();
this.B_LoadHouse = new System.Windows.Forms.Button();
this.B_DumpHouse = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.NUD_Variant)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.NUD_Species)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.PB_Villager)).BeginInit();
@@ -54,7 +56,7 @@ private void InitializeComponent()
//
// B_EditVillagerFlags
//
this.B_EditVillagerFlags.Location = new System.Drawing.Point(297, 165);
this.B_EditVillagerFlags.Location = new System.Drawing.Point(297, 119);
this.B_EditVillagerFlags.Name = "B_EditVillagerFlags";
this.B_EditVillagerFlags.Size = new System.Drawing.Size(92, 40);
this.B_EditVillagerFlags.TabIndex = 46;
@@ -74,7 +76,7 @@ private void InitializeComponent()
//
// B_EditFurniture
//
this.B_EditFurniture.Location = new System.Drawing.Point(199, 165);
this.B_EditFurniture.Location = new System.Drawing.Point(199, 119);
this.B_EditFurniture.Name = "B_EditFurniture";
this.B_EditFurniture.Size = new System.Drawing.Size(92, 40);
this.B_EditFurniture.TabIndex = 44;
@@ -242,10 +244,32 @@ private void InitializeComponent()
0});
this.NUD_Villager.ValueChanged += new System.EventHandler(this.LoadVillager);
//
// B_LoadHouse
//
this.B_LoadHouse.Location = new System.Drawing.Point(297, 165);
this.B_LoadHouse.Name = "B_LoadHouse";
this.B_LoadHouse.Size = new System.Drawing.Size(92, 40);
this.B_LoadHouse.TabIndex = 48;
this.B_LoadHouse.Text = "Load House";
this.B_LoadHouse.UseVisualStyleBackColor = true;
this.B_LoadHouse.Click += new System.EventHandler(this.B_LoadHouse_Click);
//
// B_DumpHouse
//
this.B_DumpHouse.Location = new System.Drawing.Point(199, 165);
this.B_DumpHouse.Name = "B_DumpHouse";
this.B_DumpHouse.Size = new System.Drawing.Size(92, 40);
this.B_DumpHouse.TabIndex = 47;
this.B_DumpHouse.Text = "Dump House";
this.B_DumpHouse.UseVisualStyleBackColor = true;
this.B_DumpHouse.Click += new System.EventHandler(this.B_DumpHouse_Click);
//
// VillagerEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.B_LoadHouse);
this.Controls.Add(this.B_DumpHouse);
this.Controls.Add(this.B_EditVillagerFlags);
this.Controls.Add(this.CHK_VillagerMovingOut);
this.Controls.Add(this.B_EditFurniture);
@@ -295,5 +319,7 @@ private void InitializeComponent()
private System.Windows.Forms.PictureBox PB_Villager;
private System.Windows.Forms.Label L_VillagerID;
private System.Windows.Forms.NumericUpDown NUD_Villager;
private System.Windows.Forms.Button B_LoadHouse;
private System.Windows.Forms.Button B_DumpHouse;
}
}

View File

@@ -10,14 +10,19 @@ public partial class VillagerEditor : UserControl
{
public Villager[] Villagers;
public IVillagerOrigin Origin;
private readonly MainSave SAV;
private int VillagerIndex = -1;
public VillagerEditor(Villager[] villagers, IVillagerOrigin origin)
public VillagerEditor(Villager[] villagers, IVillagerOrigin origin, MainSave sav, bool hasHouses)
{
Villagers = villagers;
Origin = origin;
SAV = sav;
InitializeComponent();
LoadVillagers();
if (!hasHouses)
B_DumpHouse.Visible = B_LoadHouse.Visible = false;
}
public void Save() => SaveVillager(VillagerIndex);
@@ -161,5 +166,60 @@ private void ChangeVillager()
L_ExternalName.Text = GameInfo.Strings.GetVillager(name);
PB_Villager.Image = VillagerSprite.GetVillagerSprite(name);
}
private void B_DumpHouse_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
{
using var fbd = new FolderBrowserDialog();
if (fbd.ShowDialog() != DialogResult.OK)
return;
var dir = Path.GetDirectoryName(fbd.SelectedPath);
if (dir == null || !Directory.Exists(dir))
return;
SAV.DumpVillagerHouses(fbd.SelectedPath);
return;
}
var name = L_ExternalName.Text;
using var sfd = new SaveFileDialog
{
Filter = "New Horizons Villager House (*.nhvh)|*.nhvh|All files (*.*)|*.*",
FileName = $"{name}.nhvh",
};
if (sfd.ShowDialog() != DialogResult.OK)
return;
var h = SAV.GetVillagerHouse(VillagerIndex);
var data = h.ToBytesClass();
File.WriteAllBytes(sfd.FileName, data);
}
private void B_LoadHouse_Click(object sender, EventArgs e)
{
var name = L_ExternalName.Text;
using var ofd = new OpenFileDialog
{
Filter = "New Horizons Villager House (*.nhvh)|*.nhvh|All files (*.*)|*.*",
FileName = $"{name}.nhvh",
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var file = ofd.FileName;
var fi = new FileInfo(file);
const int expectLength = VillagerHouse.SIZE;
if (fi.Length != expectLength)
{
var msg = $"Imported villager house's data length (0x{fi.Length:X}) does not match the required length (0x{expectLength:X}).";
WinFormsUtil.Error("Cancelling:", msg);
return;
}
var data = File.ReadAllBytes(file);
var h = data.ToClass<VillagerHouse>();
SAV.SetVillagerHouse(h, VillagerIndex);
}
}
}

View File

@@ -77,6 +77,10 @@ private void InitializeComponent()
this.B_EditTurnipExchange = new System.Windows.Forms.Button();
this.B_EditBuildings = new System.Windows.Forms.Button();
this.B_RecycleBin = new System.Windows.Forms.Button();
this.B_LoadHouse = new System.Windows.Forms.Button();
this.B_DumpHouse = new System.Windows.Forms.Button();
this.NUD_PlayerHouse = new System.Windows.Forms.NumericUpDown();
this.L_PlayerHouse = new System.Windows.Forms.Label();
this.Menu_Editor.SuspendLayout();
this.TC_Editors.SuspendLayout();
this.Tab_Players.SuspendLayout();
@@ -90,6 +94,7 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)(this.NUD_PatternIndex)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.PB_Pattern)).BeginInit();
this.Tab_Map.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.NUD_PlayerHouse)).BeginInit();
this.SuspendLayout();
//
// Menu_Editor
@@ -518,6 +523,10 @@ private void InitializeComponent()
//
// Tab_Map
//
this.Tab_Map.Controls.Add(this.L_PlayerHouse);
this.Tab_Map.Controls.Add(this.NUD_PlayerHouse);
this.Tab_Map.Controls.Add(this.B_LoadHouse);
this.Tab_Map.Controls.Add(this.B_DumpHouse);
this.Tab_Map.Controls.Add(this.B_EditFieldItems);
this.Tab_Map.Controls.Add(this.B_EditTerrain);
this.Tab_Map.Controls.Add(this.B_EditAcres);
@@ -592,6 +601,52 @@ private void InitializeComponent()
this.B_RecycleBin.UseVisualStyleBackColor = true;
this.B_RecycleBin.Click += new System.EventHandler(this.B_RecycleBin_Click);
//
// B_LoadHouse
//
this.B_LoadHouse.Location = new System.Drawing.Point(106, 29);
this.B_LoadHouse.Name = "B_LoadHouse";
this.B_LoadHouse.Size = new System.Drawing.Size(92, 40);
this.B_LoadHouse.TabIndex = 50;
this.B_LoadHouse.Text = "Load House";
this.B_LoadHouse.UseVisualStyleBackColor = true;
this.B_LoadHouse.Click += new System.EventHandler(this.B_LoadHouse_Click);
//
// B_DumpHouse
//
this.B_DumpHouse.Location = new System.Drawing.Point(8, 29);
this.B_DumpHouse.Name = "B_DumpHouse";
this.B_DumpHouse.Size = new System.Drawing.Size(92, 40);
this.B_DumpHouse.TabIndex = 49;
this.B_DumpHouse.Text = "Dump House";
this.B_DumpHouse.UseVisualStyleBackColor = true;
this.B_DumpHouse.Click += new System.EventHandler(this.B_DumpHouse_Click);
//
// numericUpDown1
//
this.NUD_PlayerHouse.Location = new System.Drawing.Point(106, 6);
this.NUD_PlayerHouse.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.NUD_PlayerHouse.Name = "NUD_PlayerHouse";
this.NUD_PlayerHouse.Size = new System.Drawing.Size(45, 20);
this.NUD_PlayerHouse.TabIndex = 51;
this.NUD_PlayerHouse.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// L_PlayerHouse
//
this.L_PlayerHouse.Location = new System.Drawing.Point(16, 6);
this.L_PlayerHouse.Name = "L_PlayerHouse";
this.L_PlayerHouse.Size = new System.Drawing.Size(84, 20);
this.L_PlayerHouse.TabIndex = 52;
this.L_PlayerHouse.Text = "Player House:";
this.L_PlayerHouse.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// Editor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -621,6 +676,7 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)(this.NUD_PatternIndex)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.PB_Pattern)).EndInit();
this.Tab_Map.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.NUD_PlayerHouse)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -675,6 +731,10 @@ private void InitializeComponent()
private System.Windows.Forms.Button B_EditTerrain;
private System.Windows.Forms.Button B_EditPlayerFlags;
private System.Windows.Forms.Button B_EditFieldItems;
private System.Windows.Forms.NumericUpDown NUD_PlayerHouse;
private System.Windows.Forms.Button B_LoadHouse;
private System.Windows.Forms.Button B_DumpHouse;
private System.Windows.Forms.Label L_PlayerHouse;
}
}

View File

@@ -124,7 +124,7 @@ private VillagerEditor LoadVillagers()
{
var p0 = SAV.Players[0].Personal;
var villagers = SAV.Main.GetVillagers();
var v = new VillagerEditor(villagers, p0) {Dock = DockStyle.Fill};
var v = new VillagerEditor(villagers, p0, SAV.Main, true) {Dock = DockStyle.Fill};
Tab_Villagers.Controls.Add(v);
return v;
}
@@ -146,6 +146,7 @@ private void LoadPlayers()
PlayerIndex = -1;
CB_Players.SelectedIndex = 0;
NUD_PlayerHouse.Maximum = CB_Players.Items.Count;
}
private void LoadMain()
@@ -439,5 +440,62 @@ private void B_EditFieldItems_Click(object sender, EventArgs e)
using var editor = new FieldItemEditor(SAV.Main);
editor.ShowDialog();
}
private void B_DumpHouse_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Shift)
{
using var fbd = new FolderBrowserDialog();
if (fbd.ShowDialog() != DialogResult.OK)
return;
var dir = Path.GetDirectoryName(fbd.SelectedPath);
if (dir == null || !Directory.Exists(dir))
return;
SAV.DumpPlayerHouses(fbd.SelectedPath);
return;
}
var index = (int)(NUD_PlayerHouse.Value - 1);
var name = CB_Players.Items[index].ToString();
using var sfd = new SaveFileDialog
{
Filter = "New Horizons Player House (*.nhph)|*.nhph|All files (*.*)|*.*",
FileName = $"{name}.nhph",
};
if (sfd.ShowDialog() != DialogResult.OK)
return;
var h = SAV.Main.GetPlayerHouse(index);
var data = h.ToBytesClass();
File.WriteAllBytes(sfd.FileName, data);
}
private void B_LoadHouse_Click(object sender, EventArgs e)
{
var index = (int)(NUD_PlayerHouse.Value - 1);
var name = CB_Players.Items[index].ToString();
using var ofd = new OpenFileDialog
{
Filter = "New Horizons Player House (*.nhph)|*.nhph|All files (*.*)|*.*",
FileName = $"{name}.nhph",
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var file = ofd.FileName;
var fi = new FileInfo(file);
const int expectLength = PlayerHouse.SIZE;
if (fi.Length != expectLength)
{
var msg = $"Imported player house's data length (0x{fi.Length:X}) does not match the required length (0x{expectLength:X}).";
WinFormsUtil.Error("Cancelling:", msg);
return;
}
var data = File.ReadAllBytes(file);
var h = data.ToClass<PlayerHouse>();
SAV.Main.SetPlayerHouse(h, index);
}
}
}