"""Extrae ItemSparse.db2 (WDC4, tabla sparse) usando las definiciones oficiales de WoWDBDefs para el layout_hash del propio fichero. Por qué hace falta el DBD: en una tabla sparse los locstring van INLINE y terminados en \0, así que desplazan todo lo que va detrás. Los offsets de field_storage_info asumen 4 bytes por string y NO valen; hay que recorrer los campos en orden. El fichero no dice qué campos son strings: eso está en el DBD. """ import re import struct import sys def parse_dbd(path, layout_hash): txt = open(path, encoding='utf-8').read() # Tipos por columna (COLUMNS ... hasta línea en blanco) cols = {} block = txt.split('COLUMNS', 1)[1].split('\n\n', 1)[0] for line in block.strip().split('\n'): parts = line.split() if len(parts) >= 2: cols[parts[1].split('<')[0].rstrip('?')] = parts[0].split('<')[0] # Bloque del layout pedido blocks = txt.split('\n\n') for b in blocks: if f'LAYOUT {layout_hash}' in b: fields = [] for line in b.strip().split('\n'): if line.startswith(('LAYOUT', 'BUILD', 'COMMENT')): continue m = re.match(r'^(\$[^$]*\$)?([A-Za-z_0-9]+)(<[^>]*>)?(\[(\d+)\])?', line) if not m: continue ann, name, size, _, arr = m.groups() fields.append(dict(name=name, noninline='noninline' in (ann or ''), array=int(arr) if arr else 1, type=cols.get(name, 'int'))) return fields raise SystemExit(f'layout {layout_hash} no encontrado') class ItemSparse: def __init__(self, db2_path, dbd_path): d = self.d = open(db2_path, 'rb').read() hdr = struct.unpack_from(' 320 bits), no el de un elemento. nbytes = self.storage[fi]['size_bits'] // 8 if not nbytes: continue if f['array'] == 1: out[f['name']] = int.from_bytes(rec[pos:pos + nbytes], 'little') pos += nbytes out['__consumed'] = pos out['__len'] = len(rec) return out