mirror of
https://github.com/kwsch/NHSE.git
synced 2026-09-10 04:05:38 -05:00
Add design pattern read/write
Looks like they might be indexing into the palette differently...
This commit is contained in:
@@ -12,6 +12,8 @@ public sealed class MainSave : EncryptedFilePair
|
||||
|
||||
public Villager GetVillager(int index) => Offsets.ReadVillager(Data, index);
|
||||
public void SetVillager(Villager value, int index) => Offsets.WriteVillager(value, Data, index);
|
||||
public DesignPattern GetDesign(int index) => Offsets.ReadPattern(Data, index);
|
||||
public void SetDesign(DesignPattern value, int index) => Offsets.WritePattern(value, Data, index);
|
||||
|
||||
public IReadOnlyList<Item> RecycleBin
|
||||
{
|
||||
|
||||
@@ -9,6 +9,10 @@ public abstract class MainSaveOffsets
|
||||
{
|
||||
public abstract int Villager { get; }
|
||||
public const int VillagerSize = 0x12AB0;
|
||||
public const int VillagerCount = 10;
|
||||
|
||||
public abstract int Patterns { get; }
|
||||
public const int PatternCount = 50;
|
||||
|
||||
public abstract int RecycleBin { get; }
|
||||
public const int RecycleBinCount = 40;
|
||||
@@ -25,9 +29,33 @@ public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
|
||||
};
|
||||
}
|
||||
|
||||
public abstract Villager ReadVillager(byte[] data, int index);
|
||||
public DesignPattern ReadPattern(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var v = data.Slice(Patterns + (index * DesignPattern.SIZE), DesignPattern.SIZE);
|
||||
return new DesignPattern(v);
|
||||
}
|
||||
|
||||
public void WritePattern(DesignPattern p, byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
p.Data.CopyTo(data, Patterns + (index * DesignPattern.SIZE));
|
||||
}
|
||||
|
||||
public Villager ReadVillager(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var v = data.Slice(Villager + (index * VillagerSize), VillagerSize);
|
||||
return new Villager(v);
|
||||
}
|
||||
|
||||
public void WriteVillager(Villager v, byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
v.Data.CopyTo(data, Villager + (index * VillagerSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
public class MainSaveOffsets10 : MainSaveOffsets
|
||||
{
|
||||
public override int Villager => 0x110;
|
||||
public override int Patterns => 0x1D72F0;
|
||||
public override int RecycleBin => 0xABC000; // yep.
|
||||
|
||||
public override Villager ReadVillager(byte[] data, int index)
|
||||
{
|
||||
var v = data.Slice(Villager + (index * VillagerSize), VillagerSize);
|
||||
return new Villager(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,7 @@
|
||||
public class MainSaveOffsets11 : MainSaveOffsets
|
||||
{
|
||||
public override int Villager => 0x120;
|
||||
public override int Patterns => 0x1D7310; // +0x20 from v1.0
|
||||
public override int RecycleBin => 0xABDE70; // +0x1E70 from v1.0
|
||||
|
||||
public override Villager ReadVillager(byte[] data, int index)
|
||||
{
|
||||
var v = data.Slice(Villager + (index * VillagerSize), VillagerSize);
|
||||
return new Villager(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
NHSE.Core/Structures/Designs/DesignPattern.cs
Normal file
118
NHSE.Core/Structures/Designs/DesignPattern.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
public class DesignPattern : IVillagerOrigin
|
||||
{
|
||||
public const int Width = 32;
|
||||
public const int Height = 32;
|
||||
|
||||
public const int SIZE = 0x2A8; // 3 bytes unused at end
|
||||
private const int PersonalOffset = 0x38;
|
||||
private const int PaletteDataStart = 0x78;
|
||||
public const int PaletteColorCount = 15; // y not 16???
|
||||
private const int PaletteColorSize = 3; // R, G, B
|
||||
private const int PixelDataOffset = PaletteDataStart + (PaletteColorCount * PaletteColorSize); // 0xA5
|
||||
private const int PixelCount = 0x400; // Width * Height
|
||||
//private const int PixelDataSize = PixelCount / 2; // 4bit|4bit pixel packing
|
||||
|
||||
public readonly byte[] Data;
|
||||
|
||||
public DesignPattern(byte[] data) => Data = data;
|
||||
|
||||
public string DesignName
|
||||
{
|
||||
get => StringUtil.GetString(Data, 0x10, 20);
|
||||
set => StringUtil.GetBytes(value, 20).CopyTo(Data, 0x10);
|
||||
}
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x04);
|
||||
}
|
||||
|
||||
public byte[] GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset + 0x1C);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset + 0x1C);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x20);
|
||||
}
|
||||
|
||||
public byte[] GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20);
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the color choice (1-15) for the pixel at the given <see cref="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Pixel index</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
return (index & 1) == 0 ? (val & 0x0F) : (val >> 4);
|
||||
}
|
||||
set
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
var update = ((index & 1) == 0)
|
||||
? (val & 0xF0) | (value & 0xF)
|
||||
: (value & 0xF) << 4 | (val & 0xF);
|
||||
Data[ofs] = (byte)update;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetPixelIndex(int x, int y) => (y * Height) + x;
|
||||
|
||||
public int GetPixel(int x, int y)
|
||||
{
|
||||
if ((uint)x >= Width)
|
||||
throw new ArgumentException($"Argument out of range (0-{Width})", nameof(x));
|
||||
if ((uint)y >= Height)
|
||||
throw new ArgumentException($"Argument out of range (0-{Height})", nameof(y));
|
||||
|
||||
var index = GetPixelIndex(x, y);
|
||||
return this[index];
|
||||
}
|
||||
|
||||
public static int GetColorOffset(int index)
|
||||
{
|
||||
if ((uint)index >= PaletteColorCount)
|
||||
throw new ArgumentException($"Argument out of range (0-{PaletteColorCount})", nameof(index));
|
||||
return PaletteDataStart + (index * PaletteColorSize);
|
||||
}
|
||||
|
||||
public byte[] GetBitmap()
|
||||
{
|
||||
byte[] data = new byte[4 * Width * Height];
|
||||
for (int i = 0; i < PixelCount; i++)
|
||||
{
|
||||
var choice = this[i];
|
||||
if (choice == PaletteColorCount)
|
||||
continue; // transparent?
|
||||
var palette = GetColorOffset(choice);
|
||||
var ofs = i * 4;
|
||||
Array.Copy(Data, palette, data, ofs, 4);
|
||||
data[ofs + 3] = 0xFF; // opaque
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] GetPaletteBitmap() => Data.Slice(PaletteDataStart, PaletteColorCount * PaletteColorSize);
|
||||
}
|
||||
}
|
||||
@@ -66,5 +66,22 @@ public static void DumpVillagers(this MainSave sav, string path)
|
||||
File.WriteAllBytes(dest, v.Data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villagers in their decrypted state 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 DumpDesigns(this MainSave sav, string path)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var dp = sav.GetDesign(i);
|
||||
var name = dp.DesignName;
|
||||
var fn = StringUtil.CleanFileName($"{name}.nhd");
|
||||
var dest = Path.Combine(path, fn);
|
||||
File.WriteAllBytes(dest, dp.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user