diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj
index 10403a3..737b096 100644
--- a/Benchmarks/Benchmarks.csproj
+++ b/Benchmarks/Benchmarks.csproj
@@ -4,7 +4,7 @@
Exe
enable
enable
- preview
+ 12
net6.0;net7.0;net8.0
diff --git a/Server/Server.csproj b/Server/Server.csproj
index 05c9822..f0d9ddb 100644
--- a/Server/Server.csproj
+++ b/Server/Server.csproj
@@ -6,7 +6,7 @@
warnings
Linux
Server
- default
+ 12
true
net8.0;net7.0
true
diff --git a/Server/Services/ItemObjectPoolService.cs b/Server/Services/ItemObjectPoolService.cs
new file mode 100644
index 0000000..3a8ac45
--- /dev/null
+++ b/Server/Services/ItemObjectPoolService.cs
@@ -0,0 +1,51 @@
+using System.Collections.Concurrent;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Wonderking.Game.Data;
+using Wonderking.Game.Reader;
+
+namespace Server.Services;
+
+public class ItemObjectPoolService : IHostedService
+{
+ readonly ConcurrentDictionary _itemObjectPool = new();
+ private readonly ItemReader _itemReader;
+
+ public ItemObjectPoolService(IConfiguration configuration)
+ {
+ _itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue("Path") ??
+ string.Empty);
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ var amountOfEntries = _itemReader.GetAmountOfEntries();
+ ParallelEnumerable.Range(0, (int)amountOfEntries).AsParallel().ForAll(i =>
+ {
+ var itemObject = _itemReader.GetEntry((uint)i);
+ _itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
+ });
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ _itemReader.Dispose();
+ return Task.CompletedTask;
+ }
+
+ public ItemObject GetItem(ushort itemId)
+ {
+ return _itemObjectPool[itemId];
+ }
+
+ public bool ContainsItem(ushort itemId)
+ {
+ return _itemObjectPool.ContainsKey(itemId);
+ }
+
+ public IQueryable QueryItems()
+ {
+ return _itemObjectPool.AsReadOnly().Values.AsQueryable();
+ }
+}
diff --git a/Wonderking/Game/Data/Item/ContainedItem.cs b/Wonderking/Game/Data/Item/ContainedItem.cs
new file mode 100644
index 0000000..3f819f4
--- /dev/null
+++ b/Wonderking/Game/Data/Item/ContainedItem.cs
@@ -0,0 +1,10 @@
+using System.Runtime.InteropServices;
+
+namespace Wonderking.Game.Data.Item;
+
+[StructLayout(LayoutKind.Sequential)]
+public struct ContainedItem
+{
+ public short ID { get; internal set; }
+ public float ObtainChance { get; internal set; }
+}
diff --git a/Wonderking/Game/Data/Item/CraftMaterial.cs b/Wonderking/Game/Data/Item/CraftMaterial.cs
new file mode 100644
index 0000000..b548e36
--- /dev/null
+++ b/Wonderking/Game/Data/Item/CraftMaterial.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Wonderking.Game.Data.Item;
+
+[StructLayout(LayoutKind.Sequential)]
+public struct CraftMaterial
+{
+ public uint ID;
+ public uint Amount;
+}
+
diff --git a/Wonderking/Game/Data/Item/ElementalStats.cs b/Wonderking/Game/Data/Item/ElementalStats.cs
new file mode 100644
index 0000000..1599a5f
--- /dev/null
+++ b/Wonderking/Game/Data/Item/ElementalStats.cs
@@ -0,0 +1,55 @@
+using System.Runtime.InteropServices;
+
+namespace Wonderking.GameData.Item;
+
+[StructLayout(LayoutKind.Explicit, Size = 64)]
+public struct ElementalStats
+{
+ [FieldOffset(0), MarshalAs(UnmanagedType.I4)]
+ public int MinimumFireDamage;
+
+ [FieldOffset(4), MarshalAs(UnmanagedType.I4)]
+ public int MinimumWaterDamage;
+
+ [FieldOffset(8), MarshalAs(UnmanagedType.I4)]
+ public int MinimumDarkDamage;
+
+ [FieldOffset(12), MarshalAs(UnmanagedType.I4)]
+ public int MinimumHolyDamage;
+
+ [FieldOffset(16), MarshalAs(UnmanagedType.I4)]
+ public int MaximumFireDamage;
+
+ [FieldOffset(20), MarshalAs(UnmanagedType.I4)]
+ public int MaximumWaterDamage;
+
+ [FieldOffset(24), MarshalAs(UnmanagedType.I4)]
+ public int MaximumDarkDamage;
+
+ [FieldOffset(28), MarshalAs(UnmanagedType.I4)]
+ public int MaximumHolyDamage;
+
+ [FieldOffset(32), MarshalAs(UnmanagedType.U4)]
+ public uint ElementFire;
+
+ [FieldOffset(36), MarshalAs(UnmanagedType.U4)]
+ public uint ElementWater;
+
+ [FieldOffset(40), MarshalAs(UnmanagedType.U4)]
+ public uint ElementDark;
+
+ [FieldOffset(44), MarshalAs(UnmanagedType.U4)]
+ public uint ElementHoly;
+
+ [FieldOffset(48), MarshalAs(UnmanagedType.I4)]
+ public int FireResistance;
+
+ [FieldOffset(52), MarshalAs(UnmanagedType.I4)]
+ public int WaterResistance;
+
+ [FieldOffset(56), MarshalAs(UnmanagedType.I4)]
+ public int DarkResistance;
+
+ [FieldOffset(60), MarshalAs(UnmanagedType.I4)]
+ public int HolyResistance;
+}
diff --git a/Wonderking/Game/Data/Item/ItemOptions.cs b/Wonderking/Game/Data/Item/ItemOptions.cs
new file mode 100644
index 0000000..3b52fb1
--- /dev/null
+++ b/Wonderking/Game/Data/Item/ItemOptions.cs
@@ -0,0 +1,7 @@
+namespace Wonderking.Game.Data.Item;
+
+public struct ItemOptions
+{
+ public ICollection OptionIDs { get; internal set; }
+ public bool OptionAvailable { get; internal set; }
+}
diff --git a/Wonderking/Game/Data/Item/Stats.cs b/Wonderking/Game/Data/Item/Stats.cs
new file mode 100644
index 0000000..ab60dd0
--- /dev/null
+++ b/Wonderking/Game/Data/Item/Stats.cs
@@ -0,0 +1,25 @@
+using System.Runtime.InteropServices;
+
+namespace Wonderking.GameData.Item;
+
+[StructLayout(LayoutKind.Explicit, Size = 24)]
+public struct Stats
+{
+ [FieldOffset(0), MarshalAs(UnmanagedType.I4)]
+ public int Strength;
+
+ [FieldOffset(4), MarshalAs(UnmanagedType.I4)]
+ public int Dexterity;
+
+ [FieldOffset(8), MarshalAs(UnmanagedType.I4)]
+ public int Intelligence;
+
+ [FieldOffset(12), MarshalAs(UnmanagedType.I4)]
+ public int Vitality;
+
+ [FieldOffset(16), MarshalAs(UnmanagedType.I4)]
+ public int Luck;
+
+ [FieldOffset(20), MarshalAs(UnmanagedType.I4)]
+ public int Wisdom;
+}
diff --git a/Wonderking/Game/Data/ItemObject.cs b/Wonderking/Game/Data/ItemObject.cs
new file mode 100644
index 0000000..2f1bf2e
--- /dev/null
+++ b/Wonderking/Game/Data/ItemObject.cs
@@ -0,0 +1,171 @@
+using System.Text.Json.Serialization;
+using Wonderking.Game.Data.Item;
+using Wonderking.GameData.Item;
+using Wonderking.Utils;
+
+namespace Wonderking.Game.Data;
+
+[GameDataMetadata(932, "baseitemdata.dat", 197)]
+public struct ItemObject
+{
+ public uint ItemID { get; set; }
+ public bool Disabled { get; set; }
+ public uint ItemType { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown2 { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown3 { get; set; }
+
+ public uint ClassNo1 { get; set; }
+ public uint ClassNo2 { get; set; }
+ public uint ClassNo3 { get; set; }
+ public uint ClassNo4 { get; set; }
+ public uint SlotNo1 { get; set; }
+ public uint SlotNo2 { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown4 { get; set; }
+
+ public uint IsCash { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown5 { get; set; }
+
+ public uint Price { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown7 { get; set; }
+
+ public uint MaxNumber { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown17 { get; set; }
+
+ public uint MaximumLevelRequirement { get; set; }
+ public uint SexNo { get; set; }
+ public uint WeaponSomething { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown8 { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R2C { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown9 { get; set; }
+
+ public Stats Stats { get; set; }
+ public ElementalStats ElementalStats { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R7C { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R8C { get; set; }
+
+ public float Speed { get; set; }
+
+ public float Jump { get; set; }
+ public int StatDefense { get; set; }
+ public uint MagicID { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown13 { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown14 { get; set; }
+
+ public int AdditionalHealthRecoveryVolume { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R9C_1 { get; set; }
+
+ public int AdditionalManaRecoveryVolume { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R9C_2 { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R10C { get; set; }
+
+ public int AdditionalHealthPoints { get; set; }
+ public int AdditionalManaPoints { get; set; }
+ public bool IsArrow { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown18 { get; set; }
+
+ public int AdditionalEvasionRate { get; set; }
+ public int HitRate { get; set; }
+
+ public int ChanceToHit { get; set; }
+ public int MagicalDamage { get; set; }
+ public int CriticalHitChance { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R12C { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown16 { get; set; }
+
+ public int MinimalAttackDamage { get; set; }
+ public int MaximalAttackDamage { get; set; }
+ public int PhysicalDamage { get; set; }
+ public CraftMaterial[] CraftMaterial { get; set; }
+ public uint CraftResultAmount { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R14C { get; set; }
+
+ public uint CraftResultItem { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R15C { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] R16C { get; set; }
+
+ public int InventoryX { get; set; }
+ public int InventoryY { get; set; }
+ public int InventoryWidth { get; set; }
+ public int InventoryHeight { get; set; }
+ public int SheetID { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown1 { get; set; }
+
+ public bool IsEnchantable { get; set; }
+
+ public uint SetID { get; set; }
+ public uint[] SetItems { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown1_2 { get; set; }
+
+ public ItemOptions Options { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown19 { get; set; }
+
+ public byte PetID { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown20 { get; set; }
+
+ public byte HitBoxScaling { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown20_2 { get; set; }
+
+ public ContainedItem[] ContainedItems { get; set; }
+
+ public bool IsQuestItem { get; set; }
+ public byte MinimumLevelRequirement { get; set; }
+
+ [JsonConverter(typeof(ByteArrayConverter))]
+ public byte[] Unknown21_2 { get; set; }
+}
diff --git a/Wonderking/Game/DataReader.cs b/Wonderking/Game/DataReader.cs
new file mode 100644
index 0000000..e67c8a4
--- /dev/null
+++ b/Wonderking/Game/DataReader.cs
@@ -0,0 +1,50 @@
+using System.Reflection;
+
+namespace Wonderking.Game;
+
+public abstract class DataReader
+{
+ protected DataReader(string path)
+ {
+ Path = path;
+ DatFileContent = new(GetDatFileContent(path).ToArray());
+ }
+
+ private protected string Path { get; init; }
+
+ public abstract uint GetAmountOfEntries();
+ public abstract T GetEntry(uint entryId);
+
+ protected ushort GetSizeOfEntry()
+ {
+ return typeof(T).GetCustomAttribute()?.DataEntrySize ??
+ throw new NotSupportedException("DataEntrySize is null");
+ }
+
+ private static string GetDatFileName()
+ {
+ return typeof(T).GetCustomAttribute()?.DatFileName ??
+ throw new NotSupportedException("DatFileName is null");
+ }
+
+ private static byte GetXorKey()
+ {
+ return typeof(T).GetCustomAttribute()?.XorKey ??
+ throw new NotSupportedException("XorKey is null");
+ }
+
+ protected MemoryStream DatFileContent { get; }
+
+ private static Span GetDatFileContent(string path)
+ {
+ var fileData = File.ReadAllBytes(path + GetDatFileName());
+ var data = new byte[fileData.Length];
+
+ for (var i = 0; i < fileData.Length; i++)
+ {
+ data[i] = (byte)(fileData[i] ^ GetXorKey());
+ }
+
+ return data;
+ }
+}
diff --git a/Wonderking/Game/GameDataMetadataAttribute.cs b/Wonderking/Game/GameDataMetadataAttribute.cs
new file mode 100644
index 0000000..b9db18c
--- /dev/null
+++ b/Wonderking/Game/GameDataMetadataAttribute.cs
@@ -0,0 +1,12 @@
+using JetBrains.Annotations;
+
+namespace Wonderking.Game;
+
+[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)]
+public class GameDataMetadataAttribute(ushort dataEntrySize, string datFileName, byte xorKey) : Attribute
+{
+ [UsedImplicitly] public byte XorKey { get; init; } = xorKey;
+ [UsedImplicitly] public ushort DataEntrySize { get; init; } = dataEntrySize;
+
+ [UsedImplicitly] internal string DatFileName { get; init; } = datFileName;
+}
diff --git a/Wonderking/Game/Reader/BinaryReader.cs b/Wonderking/Game/Reader/BinaryReader.cs
new file mode 100644
index 0000000..62c683d
--- /dev/null
+++ b/Wonderking/Game/Reader/BinaryReader.cs
@@ -0,0 +1,75 @@
+using System.Runtime.InteropServices;
+
+namespace Wonderking.Game.Reader;
+
+public static class BinaryReader where T : new()
+{
+ public static readonly Func Read;
+
+ static BinaryReader()
+ {
+ var type = typeof(T);
+
+ if (type == typeof(bool))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadBoolean());
+ }
+ else if (type == typeof(char))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadChar());
+ }
+ else if (type == typeof(string))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadString());
+ }
+ else if (type == typeof(sbyte))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadSByte());
+ }
+ else if (type == typeof(short))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadInt16());
+ }
+ else if (type == typeof(int))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadInt32());
+ }
+ else if (type == typeof(long))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadInt64());
+ }
+ else if (type == typeof(byte))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadByte());
+ }
+ else if (type == typeof(ushort))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadUInt16());
+ }
+ else if (type == typeof(uint))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadUInt32());
+ }
+ else if (type == typeof(ulong))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadUInt64());
+ }
+ else if (type == typeof(float))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadSingle());
+ }
+ else if (type == typeof(double))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadDouble());
+ }
+ else if (type == typeof(decimal))
+ {
+ Read = (Func)(Delegate)(Func)(p => p.ReadDecimal());
+ }
+ else
+ {
+ Read = (Func)(p =>
+ (T)(object)p.ReadBytes(Marshal.SizeOf(new T())));
+ }
+ }
+}
diff --git a/Wonderking/Game/Reader/GenericReaderExtensions.cs b/Wonderking/Game/Reader/GenericReaderExtensions.cs
new file mode 100644
index 0000000..17e888e
--- /dev/null
+++ b/Wonderking/Game/Reader/GenericReaderExtensions.cs
@@ -0,0 +1,37 @@
+
+/* Nicht gemergte Ă„nderung aus Projekt "Wonderking(net7.0)"
+Vor:
+using System.Runtime.InteropServices;
+using System.Text;
+Nach:
+using System.Text;
+*/
+
+using System.Text;
+
+namespace Wonderking.Game.Reader;
+
+public static class GenericReaderExtensions
+{
+ public static string ReadString(this BinaryReader reader, int length)
+ {
+ var ret = Encoding.ASCII.GetString(reader.ReadBytes(length)).Replace("\0", "");
+ return ret;
+ }
+
+ public static T[] ReadArray(this BinaryReader pReader, int pLength) where T : new()
+ {
+ var array = new T[pLength];
+ for (var index = 0; index < pLength; ++index)
+ {
+ array[index] = pReader.Read();
+ }
+
+ return array;
+ }
+
+ public static T Read(this BinaryReader br) where T : new()
+ {
+ return BinaryReader.Read(br);
+ }
+}
diff --git a/Wonderking/Game/Reader/ItemReader.cs b/Wonderking/Game/Reader/ItemReader.cs
new file mode 100644
index 0000000..38ae8a3
--- /dev/null
+++ b/Wonderking/Game/Reader/ItemReader.cs
@@ -0,0 +1,107 @@
+using Wonderking.Game.Data;
+using Wonderking.Game.Data.Item;
+
+namespace Wonderking.Game.Reader;
+
+public class ItemReader(string path) : DataReader(path), IDisposable
+{
+ public override uint GetAmountOfEntries()
+ {
+ return (uint)((this.DatFileContent.Length - 9) / this.GetSizeOfEntry());
+ }
+
+ public override ItemObject GetEntry(uint entryId)
+ {
+ var item = new ItemObject();
+ this.DatFileContent.Position = 9 + entryId * this.GetSizeOfEntry();
+ var reader = new BinaryReader(this.DatFileContent);
+ item.ItemID = reader.ReadUInt32(); //9
+ item.Disabled = reader.ReadUInt32() == 1; //13
+ item.ItemType = reader.ReadUInt32(); //17
+ item.Unknown2 = reader.ReadBytes(4); //21
+ item.Unknown3 = reader.ReadBytes(4); //25
+ item.ClassNo1 = reader.ReadUInt32(); //29
+ item.ClassNo2 = reader.ReadUInt32(); //33
+ item.ClassNo3 = reader.ReadUInt32(); //37
+ item.ClassNo4 = reader.ReadUInt32(); //41
+ item.SlotNo1 = reader.ReadUInt32(); //45
+ item.SlotNo2 = reader.ReadUInt32(); //49
+ item.Unknown4 = reader.ReadBytes(4); //53
+ item.IsCash = reader.ReadUInt32(); //57
+ item.Unknown5 = reader.ReadBytes(4); //61
+ item.Price = reader.ReadUInt32(); //65
+ item.Unknown7 = reader.ReadBytes(4); //69
+ item.MaxNumber = reader.ReadUInt32(); //73
+ item.Unknown17 = reader.ReadBytes(12); //77
+ item.MaximumLevelRequirement = reader.ReadUInt32(); //89
+ item.SexNo = reader.ReadUInt32(); //93
+ item.WeaponSomething = reader.ReadUInt32(); //97
+ item.Unknown8 = reader.ReadBytes(4); //101
+ item.R2C = reader.ReadBytes(16); //105
+ item.Unknown9 = reader.ReadBytes(4); //121
+ item.Stats = reader.ReadStats(); //125
+ item.ElementalStats = reader.ReadElementalStats(); //149
+ item.R7C = reader.ReadBytes(4); //213
+ item.R8C = reader.ReadBytes(8); //217
+ item.Speed = reader.ReadSingle(); //225
+ item.Jump = reader.ReadSingle(); //229
+ item.StatDefense = reader.ReadInt32(); //233
+ item.MagicID = reader.ReadUInt32(); //237
+ item.Unknown13 = reader.ReadBytes(4); //241
+ item.Unknown14 = reader.ReadBytes(4); //245
+ item.AdditionalHealthRecoveryVolume = reader.ReadInt32(); //249
+ item.R9C_1 = reader.ReadBytes(4); //253
+ item.AdditionalManaRecoveryVolume = reader.ReadInt32(); //257
+ item.R9C_2 = reader.ReadBytes(4); //261
+ item.R10C = reader.ReadBytes(8); //265
+ item.AdditionalHealthPoints = reader.ReadInt32(); //273
+ item.AdditionalManaPoints = reader.ReadInt32(); //277
+ item.IsArrow = reader.ReadBoolean(); //281
+ item.Unknown18 = reader.ReadBytes(7); //282
+ item.AdditionalEvasionRate = reader.ReadInt32(); //289
+ item.HitRate = reader.ReadInt32(); //293
+ item.ChanceToHit = reader.ReadInt32(); //297
+ item.MagicalDamage = reader.ReadInt32(); //301
+ item.CriticalHitChance = reader.ReadInt32(); //305
+ item.R12C = reader.ReadBytes(4); //309
+ item.Unknown16 = reader.ReadBytes(4); //313
+ item.MinimalAttackDamage = reader.ReadInt32(); //317
+ item.MaximalAttackDamage = reader.ReadInt32(); //321
+ item.PhysicalDamage = reader.ReadInt32(); //325
+ item.CraftMaterial = reader.ReadCraftMaterial(); //329
+ item.CraftResultAmount = reader.ReadUInt32(); //361
+ item.R14C = reader.ReadBytes(4); //365
+ item.CraftResultItem = reader.ReadUInt32(); //369
+ item.R15C = reader.ReadBytes(4); //373
+ item.R16C = reader.ReadBytes(20); //377
+ item.InventoryX = reader.ReadInt32(); //397
+ item.InventoryY = reader.ReadInt32(); //401
+ item.InventoryWidth = reader.ReadInt32(); //405
+ item.InventoryHeight = reader.ReadInt32(); //409
+ item.SheetID = reader.ReadInt32(); //413
+ item.Name = reader.ReadString(20); //417
+ item.Description = reader.ReadString(85); //427
+ item.Unknown1 = reader.ReadBytes(175); //493
+ item.IsEnchantable = reader.ReadUInt32() == 1; //687
+ item.Unknown1_2 = reader.ReadBytes(104); //687
+ item.SetItems = reader.ReadArray(5);
+ item.SetID = reader.ReadUInt32(); //691
+ item.Options = reader.ReadItemOptions(); //819
+ item.Unknown19 = reader.ReadBytes(23); //835
+ item.PetID = reader.ReadByte(); //858
+ item.Unknown20 = reader.ReadBytes(20); //859
+ item.HitBoxScaling = reader.ReadByte(); //879
+ item.Unknown20_2 = reader.ReadBytes(13); //880
+ item.ContainedItems = reader.ReadContainedItems(); //893
+ item.IsQuestItem = reader.ReadBoolean(); //923
+ item.MinimumLevelRequirement = reader.ReadByte(); //924
+ item.Unknown21_2 = reader.ReadBytes(6); //925
+ reader.Dispose(); //931
+ return item;
+ }
+
+ public void Dispose()
+ {
+ this.DatFileContent.Dispose();
+ }
+}
diff --git a/Wonderking/Game/Reader/ItemReaderExtensions.cs b/Wonderking/Game/Reader/ItemReaderExtensions.cs
new file mode 100644
index 0000000..df9d71f
--- /dev/null
+++ b/Wonderking/Game/Reader/ItemReaderExtensions.cs
@@ -0,0 +1,96 @@
+using Wonderking.GameData.Item;
+
+namespace Wonderking.Game.Data.Item;
+
+public static class ItemReaderExtensions
+{
+ public static Stats ReadStats(this BinaryReader reader)
+ {
+ return new Stats()
+ {
+ Strength = reader.ReadInt32(), //125
+ Dexterity = reader.ReadInt32(), //129
+ Intelligence = reader.ReadInt32(), //133
+ Vitality = reader.ReadInt32(), //137
+ Luck = reader.ReadInt32(), //141
+ Wisdom = reader.ReadInt32(), //145
+ };
+ }
+
+ public static ElementalStats ReadElementalStats(this BinaryReader reader)
+ {
+ return new ElementalStats()
+ {
+ MinimumFireDamage = reader.ReadInt32(), //149
+ MinimumWaterDamage = reader.ReadInt32(), //153
+ MinimumDarkDamage = reader.ReadInt32(), //157
+ MinimumHolyDamage = reader.ReadInt32(), //161
+ MaximumFireDamage = reader.ReadInt32(), //165
+ MaximumWaterDamage = reader.ReadInt32(), //169
+ MaximumDarkDamage = reader.ReadInt32(), //173
+ MaximumHolyDamage = reader.ReadInt32(), //177
+ ElementFire = reader.ReadUInt32(), //181
+ ElementWater = reader.ReadUInt32(), //185
+ ElementDark = reader.ReadUInt32(), //189
+ ElementHoly = reader.ReadUInt32(), //193
+ FireResistance = reader.ReadInt32(), //197
+ WaterResistance = reader.ReadInt32(), //201
+ DarkResistance = reader.ReadInt32(), //205
+ HolyResistance = reader.ReadInt32(), //209
+ };
+ }
+
+ public static ContainedItem[] ReadContainedItems(this BinaryReader reader)
+ {
+ var list = new ContainedItem[5];
+ //893
+ for (var i = 0; i < 5; i++)
+ {
+ list[i].ID = reader.ReadInt16();
+ }
+
+ //903
+ for (var i = 0; i < 5; i++)
+ {
+ list[i].ObtainChance = reader.ReadSingle();
+ }
+
+ return list;
+ }
+
+ public static CraftMaterial[] ReadCraftMaterial(this BinaryReader reader)
+ {
+ var mats = new CraftMaterial[4];
+ //329
+ for (var i = 0; i < 4; ++i)
+ {
+ mats[i].ID = reader.ReadUInt32();
+ }
+
+ //345
+ for (var i = 0; i < 4; ++i)
+ {
+ mats[i].ID = reader.ReadUInt32();
+ }
+
+ return mats;
+ }
+
+ public static ItemOptions ReadItemOptions(this BinaryReader reader)
+ {
+ var options = new ItemOptions();
+
+ options.OptionAvailable = reader.ReadInt32() == 1; //819
+
+ var optionIDs = new List(4);
+ //823
+ for (var i = 0; i < 3; i++)
+ {
+ optionIDs.Add((byte)reader.ReadUInt32());
+ }
+
+ options.OptionIDs = optionIDs;
+
+ return options;
+ }
+}
diff --git a/Wonderking/Utils/ByteArrayConverter.cs b/Wonderking/Utils/ByteArrayConverter.cs
new file mode 100644
index 0000000..76259f5
--- /dev/null
+++ b/Wonderking/Utils/ByteArrayConverter.cs
@@ -0,0 +1,36 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Wonderking.Utils;
+
+public class ByteArrayConverter : JsonConverter
+{
+ public override byte[] Read(
+ ref Utf8JsonReader reader,
+ Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ var hexData = reader.GetString();
+ if (hexData != null)
+ {
+ return hexData.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
+ }
+ throw new JsonException("Hex string is null.");
+ }
+
+ public override void Write(
+ Utf8JsonWriter writer,
+ byte[]? value,
+ JsonSerializerOptions options)
+ {
+ if (value == null)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ var hexData = BitConverter.ToString(value).Replace("-", string.Empty);
+ writer.WriteStringValue(hexData);
+ }
+ }
+}
diff --git a/Wonderking/Wonderking.csproj b/Wonderking/Wonderking.csproj
index 78f67e5..f5b8158 100644
--- a/Wonderking/Wonderking.csproj
+++ b/Wonderking/Wonderking.csproj
@@ -5,6 +5,7 @@
enable
net8.0;net7.0
strict
+ 12
@@ -30,5 +31,13 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
+ True
+ \
+
+
+
+
+