using System; using static System.Buffers.Binary.BinaryPrimitives; namespace NHSE.Core; /// /// Simple design pattern /// public sealed class DesignPattern(Memory Raw) : 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 UsageCompatibilityOffset = 0x70; 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 Span Data => Raw.Span; public uint Hash { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); } public uint Version { get => ReadUInt32LittleEndian(Data[0x04..]); set => WriteUInt32LittleEndian(Data[0x04..], value); } public string DesignName { get => StringUtil.GetString(Data, 0x10, 20); set => StringUtil.GetBytes(value, 20).CopyTo(Data[0x10..]); } public uint TownID { get => ReadUInt32LittleEndian(Data[PersonalOffset..]); set => WriteUInt32LittleEndian(Data[PersonalOffset..], value); } public string TownName { get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10); set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x04)..]); } public Span GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20); public uint PlayerID { get => ReadUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..]); set => WriteUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..], value); } public string PlayerName { get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10); set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x20)..]); } public uint UsageCompatibility { get => ReadUInt32LittleEndian(Data[UsageCompatibilityOffset..]); set => WriteUInt32LittleEndian(Data[UsageCompatibilityOffset..], value); } public Span GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20); /// /// Gets/Sets the color choice (1-15) for the pixel at the given . /// /// Pixel index 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); } /// /// Builds a new array with unpacked 32bit pixel data. /// public byte[] GetBitmap() { var result = new byte[4 * Width * Height]; LoadBitmap(result); return result; } public void LoadBitmap(Span data) { for (int i = 0; i < PixelCount; i++) { var choice = this[i]; if (choice == PaletteColorCount) continue; // transparent? var palette = GetColorOffset(choice); var ofs = i * 4; data[ofs + 2] = Data[palette + 0]; data[ofs + 1] = Data[palette + 1]; data[ofs + 0] = Data[palette + 2]; data[ofs + 3] = 0xFF; // opaque } } /// /// Returns a raw slice of data containing the 24bit color pixels. /// public byte[] GetPaletteBitmap() { var result = new byte[3 * PaletteColorCount]; LoadPaletteBitmap(result); return result; } public void LoadPaletteBitmap(Span result) { for (int i = 0; i < PaletteColorCount; i++) { var ofs = PaletteDataStart + (i * 3); result[(i * 3) + 2] = Data[ofs + 0]; result[(i * 3) + 1] = Data[ofs + 1]; result[(i * 3) + 0] = Data[ofs + 2]; } } }