Tfe

Ongi etorri tfe-ren webgunera...

Blog/jokoak_euskaraz/stray/locres.py

(Deskargatu)
import struct

MAGIC = bytes.fromhex('0e147475674a03fc4a15909dc3377f1b')


def read_fstring(data, off):
    l = struct.unpack('<i', data[off:off+4])[0]
    off += 4
    if l == 0:
        return '', off
    if l < 0:
        raw = data[off:off+(-l)*2]
        s = raw.decode('utf-16-le', errors='replace')
        return s.rstrip('\x00'), off + (-l)*2
    s = data[off:off+l]
    return s[:-1].decode('utf-8', errors='replace'), off + l


def parse(data):
    assert data[:16] == MAGIC, 'not a locres v3 file'
    off = 16
    version = data[off]
    off += 1
    lut_off = struct.unpack('<q', data[off:off+8])[0]
    off += 8
    entries_count = struct.unpack('<I', data[off:off+4])[0]
    off += 4
    ns_count = struct.unpack('<I', data[off:off+4])[0]
    off += 4
    namespaces = []
    for _ in range(ns_count):
        ns_hash = struct.unpack('<I', data[off:off+4])[0]
        off += 4
        ns, off = read_fstring(data, off)
        key_count = struct.unpack('<I', data[off:off+4])[0]
        off += 4
        keys = []
        for _ in range(key_count):
            key_hash = struct.unpack('<I', data[off:off+4])[0]
            off += 4
            key, off = read_fstring(data, off)
            src_hash = struct.unpack('<I', data[off:off+4])[0]
            off += 4
            lut_index = struct.unpack('<i', data[off:off+4])[0]
            off += 4
            keys.append((key, key_hash, src_hash, lut_index))
        namespaces.append((ns_hash, ns, keys))
    end_of_entries = off
    assert lut_off == end_of_entries or lut_off >= end_of_entries, (lut_off, end_of_entries)
    off = lut_off
    lut_count = struct.unpack('<I', data[off:off+4])[0]
    off += 4
    lut = []
    for _ in range(lut_count):
        s, off = read_fstring(data, off)
        ref = struct.unpack('<i', data[off:off+4])[0]
        off += 4
        lut.append(s)
    return {
        'version': version,
        'namespaces': namespaces,
        'lut': lut,
        'entries_count': entries_count,
    }

def write_fstring_bytes(s):
    if not s:
        return b'\x00\x00\x00\x00'
    try:
        b = s.encode('ascii')
        return struct.pack('<i', len(b) + 1) + b + b'\x00'
    except UnicodeEncodeError:
        b = s.encode('utf-16-le')
        return struct.pack('<i', -(len(b)//2 + 1)) + b + b'\x00\x00'


def build(namespaces, lut, ns_hashes=None):
    body = bytearray()
    body += MAGIC
    body += b'\x03'  # version
    body += b'\x00' * 8  # lut offset placeholder
    body += struct.pack('<I', sum(len(ks) for _, ks in namespaces))
    body += struct.pack('<I', len(namespaces))
    for i, (ns, keys) in enumerate(namespaces):
        ns_hash = (ns_hashes[i] if ns_hashes else 0)
        body += struct.pack('<I', ns_hash)
        body += write_fstring_bytes(ns)
        body += struct.pack('<I', len(keys))
        for key, key_hash, src_hash, lut_index in keys:
            body += struct.pack('<I', key_hash)
            body += write_fstring_bytes(key)
            body += struct.pack('<I', src_hash)
            body += struct.pack('<i', lut_index)
    lut_body = bytearray()
    lut_body += struct.pack('<I', len(lut))
    for s in lut:
        lut_body += write_fstring_bytes(s)
        lut_body += struct.pack('<i', 1)  # refcount
    body += lut_body
    lut_off = len(body) - len(lut_body)
    body[17:25] = struct.pack('<q', lut_off)
    return bytes(body)


if __name__ == '__main__':
    import sys
    for path in sys.argv[1:]:
        data = open(path, 'rb').read()
        r = parse(data)
        print(path)
        print('  version:', r['version'], 'entries:', r['entries_count'], 'lut:', len(r['lut']))
        for ns_hash, ns, keys in r['namespaces']:
            print('  ns:', repr(ns), 'keys:', len(keys))