diff --git a/NHSE.Core/NHSE.Core.csproj b/NHSE.Core/NHSE.Core.csproj index ec5607b..801a179 100644 --- a/NHSE.Core/NHSE.Core.csproj +++ b/NHSE.Core/NHSE.Core.csproj @@ -8,6 +8,7 @@ + @@ -39,6 +40,7 @@ + diff --git a/NHSE.Core/Resources/byte/item_size.bin b/NHSE.Core/Resources/byte/item_size.bin new file mode 100644 index 0000000..1e7a8bd Binary files /dev/null and b/NHSE.Core/Resources/byte/item_size.bin differ diff --git a/NHSE.Core/Structures/Item/ItemInfo.cs b/NHSE.Core/Structures/Item/ItemInfo.cs index c12e994..18d416f 100644 --- a/NHSE.Core/Structures/Item/ItemInfo.cs +++ b/NHSE.Core/Structures/Item/ItemInfo.cs @@ -3,6 +3,7 @@ public static class ItemInfo { private static readonly byte[] ItemKinds = ResourceUtil.GetBinaryResource("item_kind.bin"); + private static readonly byte[] ItemSizes = ResourceUtil.GetBinaryResource("item_size.bin"); public static ItemKind GetItemKind(IHeldItem item) => GetItemKind(item.ItemId); public static ItemKind GetItemKind(FieldItem item) => GetItemKind(item.DisplayItemId); @@ -13,5 +14,15 @@ public static ItemKind GetItemKind(ushort id) return ItemKind.Unknown; return (ItemKind) ItemKinds[id]; } + + public static ItemSizeType GetItemSize(IHeldItem item) => GetItemSize(item.ItemId); + public static ItemSizeType GetItemSize(FieldItem item) => GetItemSize(item.DisplayItemId); + + public static ItemSizeType GetItemSize(ushort id) + { + if (id > ItemSizes.Length) + return ItemSizeType.Unknown; + return (ItemSizeType)ItemSizes[id]; + } } -} \ No newline at end of file +} diff --git a/NHSE.Core/Structures/Item/ItemSize.cs b/NHSE.Core/Structures/Item/ItemSize.cs new file mode 100644 index 0000000..c0e838a --- /dev/null +++ b/NHSE.Core/Structures/Item/ItemSize.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; + +namespace NHSE.Core +{ +#pragma warning disable CA1027 // Mark enums with FlagsAttribute + public enum ItemSizeType : byte +#pragma warning restore CA1027 // Mark enums with FlagsAttribute + { + S_0_5x1_0_Wall, + S_1_0x0_5, + S_1_0x0_5_Wall, + S_1_0x1_0, + S_1_0x1_0_Rug, + S_1_0x1_0_Wall, + S_1_0x1_5_Wall, + S_1_0x2_0_Wall, + S_1_5x1_5, + S_2_0x0_5, + S_2_0x1_0, + S_2_0x1_0_Rug, + S_2_0x1_0_Wall, + S_2_0x1_5_Wall, + S_2_0x2_0, + S_2_0x2_0_Rug, + S_2_0x2_0_Wall, + S_3_0x1_0, + S_3_0x2_0, + S_3_0x2_0_Rug, + S_3_0x3_0, + S_3_0x3_0_Rug, + S_4_0x3_0_Rug, + S_4_0x4_0_Rug, + S_5_0x5_0_Rug, + + S_5_0x4_0_Rug, // not used by any items; manually added since it's in ItemSize + Unknown = byte.MaxValue, + } + + public class ItemSize + { + public readonly int Width; + public readonly int Height; + + public ItemSize(int w, int h) + { + Width = w; Height = h; + } + } + + public static class ItemSizeExtensions + { + public const string EnumPrefix = "S_"; + + private static readonly Dictionary Dictionary = new Dictionary + { + {ItemSizeType.S_1_0x1_0 , new ItemSize( 2, 2)}, // 1x1 + {ItemSizeType.S_2_0x1_0 , new ItemSize( 4, 2)}, // 2x1 + {ItemSizeType.S_2_0x2_0 , new ItemSize( 4, 4)}, // 2x2 + {ItemSizeType.S_3_0x1_0 , new ItemSize( 6, 2)}, // 3x1 + {ItemSizeType.S_3_0x2_0 , new ItemSize( 6, 4)}, // 3x2 + {ItemSizeType.S_3_0x3_0 , new ItemSize( 6, 6)}, // 3x3 + {ItemSizeType.S_1_0x0_5 , new ItemSize( 2, 1)}, // 1x0.5 + {ItemSizeType.S_2_0x0_5 , new ItemSize( 4, 1)}, // 2x0.5 + {ItemSizeType.S_1_5x1_5 , new ItemSize( 3, 3)}, // 1.5x1.5 + {ItemSizeType.S_1_0x0_5_Wall, new ItemSize( 2, 1)}, // 1x0.5(壁) + {ItemSizeType.S_0_5x1_0_Wall, new ItemSize( 1, 2)}, // 0.5x1(壁) + {ItemSizeType.S_1_0x1_0_Wall, new ItemSize( 2, 2)}, // 1x1(壁) + {ItemSizeType.S_1_0x1_5_Wall, new ItemSize( 2, 3)}, // 1x1.5 (壁) + {ItemSizeType.S_1_0x2_0_Wall, new ItemSize( 2, 4)}, // 1x2  (壁) + {ItemSizeType.S_2_0x1_0_Wall, new ItemSize( 4, 2)}, // 2x1(壁) + {ItemSizeType.S_2_0x1_5_Wall, new ItemSize( 4, 3)}, // 2x1.5 (壁) + {ItemSizeType.S_2_0x2_0_Wall, new ItemSize( 4, 4)}, // 2x2(壁) + {ItemSizeType.S_1_0x1_0_Rug , new ItemSize( 2, 2)}, // 1x1(ラグ) + {ItemSizeType.S_2_0x1_0_Rug , new ItemSize( 4, 2)}, // 2x1(ラグ) + {ItemSizeType.S_2_0x2_0_Rug , new ItemSize( 4, 4)}, // 2x2(ラグ) + {ItemSizeType.S_3_0x2_0_Rug , new ItemSize( 6, 4)}, // 3x2(ラグ) + {ItemSizeType.S_3_0x3_0_Rug , new ItemSize( 6, 6)}, // 3x3(ラグ) + {ItemSizeType.S_4_0x3_0_Rug , new ItemSize( 8, 6)}, // 4x3(ラグ) + {ItemSizeType.S_4_0x4_0_Rug , new ItemSize( 8, 8)}, // 4x4(ラグ) + {ItemSizeType.S_5_0x4_0_Rug , new ItemSize(10, 8)}, // 5x4(ラグ) + {ItemSizeType.S_5_0x5_0_Rug , new ItemSize(10, 10)}, // 5x5(ラグ) + + }; + + public static int GetWidth(this ItemSizeType s) => Dictionary.TryGetValue(s, out var val) ? val.Width : 1; + public static int GetHeight(this ItemSizeType s) => Dictionary.TryGetValue(s, out var val) ? val.Height : 1; + } +} diff --git a/NHSE.Parsing/GameBCSVDumper.cs b/NHSE.Parsing/GameBCSVDumper.cs index a3332a3..0511ea2 100644 --- a/NHSE.Parsing/GameBCSVDumper.cs +++ b/NHSE.Parsing/GameBCSVDumper.cs @@ -47,6 +47,7 @@ void DumpB(string fn, byte[] bytes) DumpS("eventFlagLand.txt", GetLandEventFlagNames(pathBCSV)); DumpS("ItemKind.txt", GetPossibleEnum(pathBCSV, "ItemParam.bcsv", 0xFC275E86)); + DumpS("ItemSize.txt", GetPossibleEnum(pathBCSV, "ItemParam.bcsv", 0xE06FB090)); DumpS("PlantKind.txt", GetPossibleEnum(pathBCSV, "FgMainParam.bcsv", 0x48EF0398)); DumpS("TerrainKind.txt", GetNumberedEnumValues(pathBCSV, "FieldLandMakingUnitModelParam.bcsv", 0x39B5A93D, 0x54706054)); DumpS("BridgeKind.txt", GetNumberedEnumValues(pathBCSV, "StructureBridgeParam.bcsv", 0x39B5A93D, 0x54706054)); @@ -57,7 +58,9 @@ void DumpB(string fn, byte[] bytes) DumpS("WallKind.txt", GetNumberedEnumValues(pathBCSV, "StructureHouseWallParam.bcsv", 0x39B5A93D, 0x54706054)); DumpB("item_kind.bin", GetItemKindArray(pathBCSV)); + DumpB("item_size.bin", GetItemSizeArray(pathBCSV)); DumpS("plants.txt", GetPlantedNames(pathBCSV)); + DumpS("item_size_dictionary.txt", GetItemSizeDictionary(pathBCSV)); if (csv) BCSVConverter.DumpAll(pathBCSV, dest, delim); @@ -157,6 +160,69 @@ public static byte[] GetItemKindArray(string pathBCSV, string fn = "ItemParam.bc return result; } + public static byte[] GetItemSizeArray(string pathBCSV, string fn = "ItemParam.bcsv") + { + var path = Path.Combine(pathBCSV, fn); + var data = File.ReadAllBytes(path); + var bcsv = new BCSV(data); + + var dict = bcsv.GetFieldDictionary(); + var fType = dict[0xE06FB090]; + var fID = dict[0x54706054]; + + var types = new Dictionary(); + ushort max = 0; + for (int i = 0; i < bcsv.EntryCount; i++) + { + var id = bcsv.ReadValue(i, fID); + var ival = ushort.Parse(id); + var type = bcsv.ReadValue(i, fType).TrimEnd('\0'); + type = ItemSizeExtensions.EnumPrefix + type; // can't start with numbers + + if (!Enum.TryParse(type, out var k)) + throw new InvalidEnumArgumentException($"{type} is not a known enum value @ index {i}. Update the enum index first."); + types.Add(ival, k); + + if (ival > max) + max = ival; + } + + byte[] result = new byte[max + 1]; + foreach (var kvp in types) + result[kvp.Key] = (byte)kvp.Value; + + return result; + } + + public static string[] GetItemSizeDictionary(string pathBCSV, string fn = "ItemSize.bcsv") + { + var path = Path.Combine(pathBCSV, fn); + var data = File.ReadAllBytes(path); + var bcsv = new BCSV(data); + + var dict = bcsv.GetFieldDictionary(); + + var fw = dict[0x16B8F524]; + var fh = dict[0xBCB13DAF]; + var fs = dict[0x87BF00E8]; + var fc = dict[0x977ADFCE]; + + string[] result = new string[bcsv.EntryCount]; + + const string prefix = nameof(ItemSizeType) + "." + ItemSizeExtensions.EnumPrefix; + for (int i = 0; i < bcsv.EntryCount; i++) + { + var w = bcsv.ReadValue(i, fw).TrimEnd('\0'); + var h = bcsv.ReadValue(i, fh).TrimEnd('\0'); + var s = bcsv.ReadValue(i, fs).TrimEnd('\0'); + var c = bcsv.ReadValue(i, fc).TrimEnd('\0'); + + result[i] = $"{{{prefix}{s,-12}, new ItemSize({w,2}, {h,2})}}, // {c}"; + } + + return result; + } + public static List GetRecipeList(string pathBCSV, string fn = "RecipeCraftParam.bcsv") { var bcsv = BCSVConverter.GetBCSV(pathBCSV, fn);