forked from omicron/d2warehouse
Add socketed items parsing
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from typing import Optional
|
||||||
from bitarray import bitarray
|
from bitarray import bitarray
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -108,7 +109,7 @@ class Item:
|
|||||||
defense: int | None = None
|
defense: int | None = None
|
||||||
durability: int | None = None
|
durability: int | None = None
|
||||||
max_durability: int | None = None
|
max_durability: int | None = None
|
||||||
sockets: int | None = None
|
sockets: list[Optional["Item"]] | None = None
|
||||||
quantity: int | None = None
|
quantity: int | None = None
|
||||||
stats: list[Stat] | None = None
|
stats: list[Stat] | None = None
|
||||||
|
|
||||||
@@ -157,8 +158,13 @@ class Item:
|
|||||||
" " * indent,
|
" " * indent,
|
||||||
f"Durability: {self.durability} out of {self.max_durability}",
|
f"Durability: {self.durability} out of {self.max_durability}",
|
||||||
)
|
)
|
||||||
if self.sockets:
|
if self.is_socketed:
|
||||||
print(" " * indent, f"Num Sockets: {self.sockets}")
|
print(" " * indent, f"{len(self.sockets)} sockets:")
|
||||||
|
for socket in self.sockets:
|
||||||
|
if socket:
|
||||||
|
socket.print(indent + 4)
|
||||||
|
else:
|
||||||
|
print(" " * (indent + 4), "Empty")
|
||||||
if self.quantity:
|
if self.quantity:
|
||||||
print(" " * indent, f"Quantity: {self.quantity}")
|
print(" " * indent, f"Quantity: {self.quantity}")
|
||||||
if self.stats:
|
if self.stats:
|
||||||
|
|||||||
@@ -128,8 +128,7 @@ def parse_item(data: bytes) -> tuple[bytes, Item]:
|
|||||||
code_end += 53
|
code_end += 53
|
||||||
# TODO: verify that this socket thing is really 1 bit for simple items...?
|
# TODO: verify that this socket thing is really 1 bit for simple items...?
|
||||||
sockets_end = code_end + 1 if is_simple else code_end + 3
|
sockets_end = code_end + 1 if is_simple else code_end + 3
|
||||||
sockets_count = ba2int(bits[code_end:sockets_end])
|
filled_sockets = ba2int(bits[code_end:sockets_end])
|
||||||
print("sockets", sockets_count)
|
|
||||||
if is_ear:
|
if is_ear:
|
||||||
raise UnsupportedItemError("Ear items are not supported")
|
raise UnsupportedItemError("Ear items are not supported")
|
||||||
|
|
||||||
@@ -180,13 +179,28 @@ def parse_item(data: bytes) -> tuple[bytes, Item]:
|
|||||||
item, itembase_end = parse_basetype_data(bits[personalized_end:], item)
|
item, itembase_end = parse_basetype_data(bits[personalized_end:], item)
|
||||||
itembase_end += personalized_end
|
itembase_end += personalized_end
|
||||||
|
|
||||||
item, enchantments_end = parse_enchantments(bits[itembase_end:], item)
|
if item.is_socketed:
|
||||||
|
sockets_count = ba2int(bits[itembase_end : itembase_end + 4])
|
||||||
|
sockets_end = itembase_end + 4
|
||||||
|
else:
|
||||||
|
sockets_end = itembase_end
|
||||||
|
|
||||||
|
item, enchantments_end = parse_enchantments(bits[sockets_end:], item)
|
||||||
enchantments_end += itembase_end
|
enchantments_end += itembase_end
|
||||||
|
|
||||||
extended_byte_size = int((enchantments_end + 7) / 8)
|
extended_byte_size = int((enchantments_end + 7) / 8)
|
||||||
|
|
||||||
item.raw_data = data[:extended_byte_size]
|
item.raw_data = data[:extended_byte_size]
|
||||||
return data[extended_byte_size:], item
|
remaining_data = data[extended_byte_size:]
|
||||||
|
|
||||||
|
# Parse out sockets if any exist on the item
|
||||||
|
if item.is_socketed:
|
||||||
|
item.sockets = [None] * sockets_count
|
||||||
|
for i in range(0, filled_sockets):
|
||||||
|
remaining_data, socket = parse_item(remaining_data)
|
||||||
|
item.sockets[i] = socket
|
||||||
|
|
||||||
|
return remaining_data, item
|
||||||
|
|
||||||
|
|
||||||
def parse_item_graphic(bits: bitarray) -> tuple[int | None, int]:
|
def parse_item_graphic(bits: bitarray) -> tuple[int | None, int]:
|
||||||
@@ -319,10 +333,6 @@ def parse_basetype_data(bits: bitarray, item: Item) -> tuple[Item, int]:
|
|||||||
item.quantity = ba2int(bits[ptr : ptr + 9])
|
item.quantity = ba2int(bits[ptr : ptr + 9])
|
||||||
ptr += 9
|
ptr += 9
|
||||||
|
|
||||||
if item.is_socketed:
|
|
||||||
item.sockets = ba2int(bits[ptr : ptr + 4])
|
|
||||||
ptr += 4
|
|
||||||
|
|
||||||
return item, ptr
|
return item, ptr
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class ParseItemTest(unittest.TestCase):
|
|||||||
self.assertEqual(data, b"")
|
self.assertEqual(data, b"")
|
||||||
self.assertEqual(item.quality, Quality.HIGH)
|
self.assertEqual(item.quality, Quality.HIGH)
|
||||||
self.assertEqual(len(item.stats), 2)
|
self.assertEqual(len(item.stats), 2)
|
||||||
self.assertEqual(item.sockets, 2)
|
self.assertEqual(len(item.sockets), 2)
|
||||||
|
|
||||||
def test_ed_max(self):
|
def test_ed_max(self):
|
||||||
# test bugfix for https://gitlab.com/omicron-oss/d2warehouse/-/issues/1
|
# test bugfix for https://gitlab.com/omicron-oss/d2warehouse/-/issues/1
|
||||||
@@ -81,21 +81,24 @@ class ParseItemTest(unittest.TestCase):
|
|||||||
self.assertEqual(str(item.stats[0]), "+13% Enhanced Damage")
|
self.assertEqual(str(item.stats[0]), "+13% Enhanced Damage")
|
||||||
self.assertEqual(str(item.stats[2]), "+2 to Maximum Damage")
|
self.assertEqual(str(item.stats[2]), "+2 to Maximum Damage")
|
||||||
|
|
||||||
def test_runeworld_lore(self):
|
def test_runeword_lore(self):
|
||||||
# Lore: Ort Sol
|
# Lore: Ort Sol
|
||||||
data = bytes.fromhex(
|
data = bytes.fromhex(
|
||||||
"1008800405c055c637f1073af4558697412981070881506049e87f005516fb134582ff1000a0003500e07cbb001000a0003504e07c9800"
|
"1008800405c055c637f1073af4558697412981070881506049e87f005516fb134582ff1000a0003500e07cbb001000a0003504e07c9800"
|
||||||
)
|
)
|
||||||
data, item = parse_item(data)
|
data, item = parse_item(data)
|
||||||
# TODO: requires socket parsing
|
self.assertEqual(data, b"")
|
||||||
# self.assertEqual(data, b"")
|
|
||||||
self.assertTrue(item.is_runeword)
|
self.assertTrue(item.is_runeword)
|
||||||
self.assertEqual(item.sockets, 2)
|
self.assertEqual(len(item.sockets), 2)
|
||||||
|
item.sockets[0].print()
|
||||||
|
item.sockets[1].print()
|
||||||
|
self.assertEqual(item.sockets[0].code, "r09")
|
||||||
|
self.assertEqual(item.sockets[1].code, "r12")
|
||||||
rw = lookup_runeword(item.runeword_id)
|
rw = lookup_runeword(item.runeword_id)
|
||||||
self.assertEqual(rw["name"], "Lore")
|
self.assertEqual(rw["name"], "Lore")
|
||||||
for stat in item.stats:
|
for stat in item.stats:
|
||||||
print(str(stat))
|
print(str(stat))
|
||||||
self.assertEqual(str(item.stats[4]), "+1 to All Skills") # runeword stat
|
self.assertEqual(str(item.stats[4]), "+1 to All Skills") # runeword stat
|
||||||
self.assertEqual(str(item.stats[2]), "+10 to Energy") # runeword stat
|
self.assertEqual(str(item.stats[2]), "+10 to Energy") # runeword stat
|
||||||
# TODO: requires socket parsing
|
# TODO: sol rune stat -- should it be in representation?
|
||||||
# self.assertEqual(str(item.stats[1]), "Lightning Resist 30%") # sol rune stat
|
# self.assertEqual(str(item.sockets[1].stats[0]), "Lightning Resist 30%")
|
||||||
|
|||||||
Reference in New Issue
Block a user