From 161ec806fd15750697acff041a88e5a1427061eb Mon Sep 17 00:00:00 2001 From: Michael Burbea Date: Tue, 15 Sep 2020 20:30:32 -0400 Subject: [PATCH] Better handling of stash gems (#60) Now, the item in question includes the sockets, so the delete removes it and it's gem. --- KoAR.Core/Item.cs | 2 +- KoAR.Core/RemasterStashItem.cs | 4 +-- KoAR.Core/Stash.cs | 39 ++++++++++++++++++--------- KoAR.Core/StashItem.ItemBuffMemory.cs | 2 +- KoAR.Core/StashItem.cs | 25 +++++++---------- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/KoAR.Core/Item.cs b/KoAR.Core/Item.cs index 4f138768..7fecfd26 100644 --- a/KoAR.Core/Item.cs +++ b/KoAR.Core/Item.cs @@ -217,7 +217,7 @@ public IEnumerable GetSockets() { 0 => Definition.GetSockets(), 1 when Definition.SocketTypes.Length == 1 => new[] { new Socket(Definition.SocketTypes[0], ItemSockets.Gems[0]) }, // trivial case. - _ => Inner(Definition.SocketTypes, ItemSockets.Gems.AsSpan().ToArray()) + _ => Inner(Definition.SocketTypes, ItemSockets.Gems.ToArray()) }; static IEnumerable Inner(string sockets, Gem[] gems) diff --git a/KoAR.Core/RemasterStashItem.cs b/KoAR.Core/RemasterStashItem.cs index 0e99f105..f9d9a71b 100644 --- a/KoAR.Core/RemasterStashItem.cs +++ b/KoAR.Core/RemasterStashItem.cs @@ -4,13 +4,13 @@ namespace KoAR.Core { public class RemasterStashItem : StashItem { - public RemasterStashItem(GameSave gameSave, int offset, int dataLength) : base(gameSave, offset, dataLength) + public RemasterStashItem(GameSave gameSave, int offset, int dataLength, Gem[] gems) : base(gameSave, offset, dataLength, gems) { } private ref InventoryState State => ref Unsafe.As(ref Bytes[Offsets.IsStolen]); - public override bool HasCustomName => (Bytes[Offsets.HasCustomName] & 16) != 0; + public override bool HasCustomName => (Bytes[Offsets.HasCustomName] & 16) == 16; public override bool IsStolen => (State & InventoryState.Stolen) == InventoryState.Stolen; } diff --git a/KoAR.Core/Stash.cs b/KoAR.Core/Stash.cs index 3a4c3bb2..527886f7 100644 --- a/KoAR.Core/Stash.cs +++ b/KoAR.Core/Stash.cs @@ -46,30 +46,43 @@ public Stash(GameSave gameSave, int offset) { if (Amalur.ItemDefinitions.ContainsKey(MemoryUtilities.Read(_gameSave.Body, _offset + indices[i]))) { - var item = CreateStashItem(gameSave, _offset + indices[i], indices[i + 1] - indices[i]); - Items.Add(item); - for(uint j = 0; j < item.GemCount; j++) + var itemStart = indices[i]; + var gems = Array.Empty(); + if (_gameSave.Body[_offset + indices[i + 1] - 1] != 0xFF) { - i++; - if (Amalur.GemDefinitions.ContainsKey(MemoryUtilities.Read(_gameSave.Body, _offset + indices[i]))) + var gemList = new List(); + var ix = _offset + indices[i + 1] - 4; + uint handle; + while ((handle = MemoryUtilities.Read(_gameSave.Body, ix)) > 4u) + { + ix -= 4; + } + for (uint j = 0; j < handle; j++) { - item.Gems.Add(new Gem(_gameSave, _offset + indices[i])); + i++; + if (Amalur.GemDefinitions.ContainsKey(MemoryUtilities.Read(_gameSave.Body, _offset + indices[i]))) + { + gemList.Add(new Gem(_gameSave, _offset + indices[i])); + } } + gems = gemList.ToArray(); } + var item = CreateStashItem(gameSave, _offset + itemStart, (i + 1 == indices.Count ? DataLength : indices[i + 1]) - itemStart, gems); + Items.Add(item); } - + } // ok we might read this twice, who cares. if (Amalur.ItemDefinitions.ContainsKey(MemoryUtilities.Read(_gameSave.Body, _offset + indices[^1]))) { - Items.Add(CreateStashItem(gameSave, _offset + indices[^1], DataLength - indices[^1])); + Items.Add(CreateStashItem(gameSave, _offset + indices[^1], DataLength - indices[^1], Array.Empty())); } } } - static StashItem CreateStashItem(GameSave gameSave, int offset, int datalength) => gameSave.IsRemaster - ? new RemasterStashItem(gameSave, offset, datalength) - : new StashItem(gameSave, offset, datalength); + static StashItem CreateStashItem(GameSave gameSave, int offset, int datalength, Gem[] gems) => gameSave.IsRemaster + ? new RemasterStashItem(gameSave, offset, datalength, gems) + : new StashItem(gameSave, offset, datalength, gems); public int DataLength { @@ -96,7 +109,7 @@ public StashItem AddItem(ItemDefinition type) // 2. We blow away everything when we do this operation anyway. // 3. We rely on the fact that the game will regenerate the ItemBuff section when the stash spawns the item. (Primarily to avoid thinking about instanceIds...) Span temp = stackalloc byte[25 + type.PlayerBuffs.Length * 8]; - var sectionHeader = _gameSave.IsRemaster ? 0x04_0Aul : 0x03_0Aul; + var sectionHeader = _gameSave.IsRemaster ? 0x04_0Aul : 0x03_0Aul; MemoryUtilities.Write(temp, 0, type.TypeId | sectionHeader << 32); MemoryUtilities.Write(temp, 10, type.MaxDurability); temp[14] = 1; @@ -117,7 +130,7 @@ public StashItem AddItem(ItemDefinition type) _gameSave.Body = MemoryUtilities.ReplaceBytes(_gameSave.Body, offset, 0, temp); DataLength += temp.Length; Count++; - Items.Add(CreateStashItem(_gameSave, offset, temp.Length)); + Items.Add(CreateStashItem(_gameSave, offset, temp.Length, Array.Empty())); _gameSave.UpdateOffsets(offset, temp.Length); _gameSave.UpdateDataLengths(offset, temp.Length); return Items[^1]; diff --git a/KoAR.Core/StashItem.ItemBuffMemory.cs b/KoAR.Core/StashItem.ItemBuffMemory.cs index afae3610..eee4421f 100644 --- a/KoAR.Core/StashItem.ItemBuffMemory.cs +++ b/KoAR.Core/StashItem.ItemBuffMemory.cs @@ -17,7 +17,7 @@ public ItemBuffMemory(StashItem stashItem, int endOfSection) (_stashItem, _endOfSection) = (stashItem, endOfSection); int count = Count; var firstBuff = Offsets.FirstItemBuff; - for(int i = 0; i < count; i++) + for (int i = 0; i < count; i++) { var buffId = MemoryUtilities.Read(Bytes, firstBuff + (i * 16) + 4); List.Add(Amalur.GetBuff(buffId)); diff --git a/KoAR.Core/StashItem.cs b/KoAR.Core/StashItem.cs index dd45de4d..bf608877 100644 --- a/KoAR.Core/StashItem.cs +++ b/KoAR.Core/StashItem.cs @@ -10,10 +10,9 @@ public partial class StashItem : IItem protected byte[] Bytes { get; } public List PlayerBuffs { get; } = new List(); - public uint GemCount { get; } - public List Gems { get; } = new List(); + public Gem[] Gems { get; } - public StashItem(GameSave gameSave, int offset, int dataLength) + public StashItem(GameSave gameSave, int offset, int dataLength, Gem[] gems) { ItemOffset = offset; Bytes = gameSave.Body.AsSpan(offset, dataLength).ToArray(); @@ -27,18 +26,12 @@ public StashItem(GameSave gameSave, int offset, int dataLength) { ItemName = Encoding.Default.GetString(Bytes, Offsets.Name, NameLength); } - int socketsStart = Bytes.Length - 1; - if (Bytes[^1] != 0xFF) - { - int i = Bytes.Length - 4; - uint handle; - while ((handle = MemoryUtilities.Read(Bytes, i)) > 4) - { - i -= 4; - } - GemCount = handle; - socketsStart = i - 2; - } + Gems = gems; + // socket section is either FF + // or 20 02, followed by int32 count, and int32 handle per gem. + int socketsStart = gems.Length == 0 + ? Bytes.Length - 1 + : gems[0].ItemOffset - offset - (4 * (1 + gems.Length)) - 2; ItemBuffs = Bytes[Offsets.HasItemBuffs] == 0x14 ? new ItemBuffMemory(this, socketsStart) : Definition.ItemBuffs; } @@ -78,7 +71,7 @@ public StashItem(GameSave gameSave, int offset, int dataLength) public IEnumerable GetSockets() { - return GemCount switch + return Gems.Length switch { 0 => Definition.GetSockets(), 1 when Definition.SocketTypes.Length == 1 => new[] { new Socket(Definition.SocketTypes[0], Gems[0]) }, // trivial case.