diff options
| -rw-r--r-- | ethyl.py | 100 | ||||
| -rw-r--r-- | parse-brlyt.py | 15 | ||||
| -rw-r--r-- | write-brlyt.py | 71 |
3 files changed, 145 insertions, 41 deletions
@@ -3,14 +3,14 @@ formats = { 'pane': ''' u8 flags_1 // 8 u8 flags_2 // 9 - u8 alpha_1 // a + u8 alpha // a u8 alpha_2 // b char name[0x18] // c float x // 24 float y // 28 float z // 2c - float pane_unk1 // 30 - float pane_unk2 // 34 + float pane_unk1 = 0 // 30 + float pane_unk2 = 0 // 34 float angle // 38 float xmag // 3c float ymag // 40 @@ -19,29 +19,26 @@ formats = { etc! ''', 'text': ''' - u16 unk1 // 4c - u16 unk2 // 4e - u16 mat_off // 50 - u16 unk3 // 52 - u8 unk4 // 54 - u8 unk5[3] // 55 - u32 name_offs // 58 - u32 unk6 // 5c - u32 unk7 // 60 - float unk8 // 64 - float unk9 // 68 - float unka // 6c - float unkb // 70 + u16 len1 // 4c + u16 len2 // 4e + u16 mat_off // 50 + u16 unk3 // 52 + u8 unk4 // 54 + u8 pad[3] = [0, 0, 0]// 55 + u32 name_offs // 58 + u32 unk6 // 5c + u32 unk7 // 60 + float font_size_x // 64 + float font_size_y // 68 + float char_space // 6c + float line_space // 70 etc! ''', 'pic': ''' - u32 unk1 // 4c - u32 unk2 // 50 - u32 unk3 // 54 - u32 unk4 // 58 - u16 mat_off // 5c - u8 num_texcoords// 5e, should be < 8 - u8 padding // 5f + u32 vtx_colors[4] = [0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff] // 4c - what are these? + u16 mat_off // 5c + u8 num_texcoords // 5e, should be < 8 + u8 padding = 0 // 5f etc! ''', 'group': ''' @@ -57,9 +54,10 @@ formats = { etc! ''', 'lytheader': ''' - u32 a - u32 b - u32 c + u8 a + u8 pad[3] = [0, 0, 0] + float width + float height ''', } @@ -78,22 +76,34 @@ for (dname, definition) in formats.items(): if i == 'etc!': etcn = True continue - typ, name = [j.strip() for j in i.split()] + x = [j.strip() for j in i.split()] + typ, name = x[:2] + try: + assert x[2] == '=' + default = eval(' '.join(x[3:])) + except: + default = None m = re.match('^([^\[]+)\[([a-zA-Z0-9]+)\]$', name) if m: name = m.group(1) size = eval(m.group(2)) - df.append((typ, (name, size))) + df.append((typ, (name, size), default)) else: - df.append((typ, name)) + df.append((typ, name, default)) formats[dname] = df, etc, etcn - - +def my_repr(b): + if type(b) in (int, long): + rep = hex(b) + elif type(b) in (list, tuple): + rep = '[' + ', '.join(map(my_repr, b)) + ']' + else: + rep = repr(b) + return rep def pr_dict(data, start=''): for a in sorted(data.keys()): b = data[a] - print '%s%s: %s' % (start, str(a), hex(b) if type(b) in (int, long) else repr(b)) + print '%s%s: %s' % (start, str(a), my_repr(b)) types = { 'u8': 'B', 'u16': 'H', @@ -123,6 +133,15 @@ def nullterm(str_plus): return str_plus[:z] else: return str_plus +def unnullterm(var, size): + if len(var) > size: + raise Exception('unnullterm: "%s" too long' % var) + return var.ljust(size, '\0') +def untv(name): + assert '\0' not in name + lex = len(name) + 16 + lex -= lex % 16 + return name.ljust(lex, '\0') def parse_data(chunk, definition, prefix=None, start=0): df, etc, etcn = formats[definition] #print df, repr(definition) @@ -130,7 +149,7 @@ def parse_data(chunk, definition, prefix=None, start=0): pos = start ret = {} - for typ, name in df: + for typ, name, _ in df: try: name, size = name except: @@ -172,20 +191,27 @@ def unparse_var(typ, var): def unparse_data(vars, definition, prefix=None): df, etc, etcn = formats[definition] ret = '' - for typ, name in df: + for typ, name, default in df: size = None try: name, size = name except: pass if prefix is not None: - name = re.sub('^' + re.quote(prefix) + '\.', '', name) - var = vars[name] + name = prefix + '.' + name + try: + var = vars[name] + except KeyError: + if default is None: + raise + else: + var = default + if size is None: ret += unparse_var(typ, var) else: if typ == 'char': - ret += var.ljust(size, '\0') + ret += unnullterm(var, size) else: assert len(var) == size for v in var: diff --git a/parse-brlyt.py b/parse-brlyt.py index c737970..ea998ca 100644 --- a/parse-brlyt.py +++ b/parse-brlyt.py @@ -6,8 +6,11 @@ rlyt = f.read() f.close() assert rlyt[0:8] == 'RLYT\xfe\xff\x00\x08' -offs, = struct.unpack('>h', rlyt[0xc:0xe]) +offs, nel = struct.unpack('>hh', rlyt[0xc:0x10]) ch = iff_to_chunks(rlyt[offs:]) + +assert len(ch) == nel + indent = 0 for typ, chunk in ch: print ' ' * indent + typ + ' ' + hex(len(chunk)) @@ -17,13 +20,15 @@ for typ, chunk in ch: assert chunk[:pos] == unparse_data(vars, 'pane') # for testing if typ == 'txt1': vars2, pos = parse_data(chunk[pos:], 'text', '~text') - - txt = chunk[vars2['~text.name_offs'] - 8:] + os = vars2['~text.name_offs'] - 8 + txt = chunk[os:os + vars2['~text.len2']] vars2['~text.text'] = unicode(txt, 'utf_16_be').rstrip('\x00') + vars2['~text.material'] = materials[vars2['~text.mat_off']] vars.update(vars2) + #print vars['~text.cnt2'], len(vars2['~text.text']), len(txt), repr(txt) elif typ == 'pic1': vars2, pos = parse_data(chunk, 'pic', prefix='~pic', start=pos) - + vars2['~pic.material'] = materials[vars2['~pic.mat_off']] vars2['~pic.texcoords'] = [] for n in xrange(vars2['~pic.num_texcoords']): vars2['~pic.texcoords'].append(struct.unpack('>ffffffff', chunk[pos:pos+0x20])) @@ -51,6 +56,7 @@ for typ, chunk in ch: else: offset, unk = struct.unpack('>II', chunk[pos:pos+8]) pos += 8 + name = nullterm(chunk[offset - 8:]) vars[things].append(name if typ == 'mat1' else {'name': name, 'unk': unk}) if typ == 'mat1': @@ -60,6 +66,7 @@ for typ, chunk in ch: elif typ in ('gre1', 'pae1'): indent -= 1 else: + print 'Unhandled type %s' % typ print repr(chunk) if vars: pr_dict(vars, ' ' * indent + ' ') #print hex(len(ch)) diff --git a/write-brlyt.py b/write-brlyt.py new file mode 100644 index 0000000..b73b23a --- /dev/null +++ b/write-brlyt.py @@ -0,0 +1,71 @@ +import sys, struct, re +from ethyl import * + +f = open(sys.argv[1], 'r') +chunks = eval(f.read()) +f.close() + + +rlyt = 'RLYT\xfe\xff\x00\x08\x00\x00\x00\x00' # size later +rlyt += struct.pack('>hh', 0x10, len(chunks)) + +materials = None + +for c in chunks: + if isinstance(c, basestring): + typ = c + chunk = '' + else: + typ, vars = c + if typ == 'lyt1': + chunk = unparse_data(vars, 'lytheader') + elif typ == 'grp1': + vars['numsubs'] = len(vars['subs']) + chunk = unparse_data(vars, 'group') + for sub in vars['subs']: + chunk += unnullterm(sub, 16) + elif typ in ('txl1', 'fnl1', 'mat1'): + things = {'txl1': 'textures', 'fnl1': 'files', 'mat1': 'materials'}[typ] + ls = vars[things] + chunk = struct.pack('>hh', len(ls), 0) + strings = '' + offss = [] + for thing in ls: + name = untv(thing['name'] if typ != 'mat1' else thing) + offss.append(len(strings)) + strings += name + for n, thing in enumerate(ls): + chunk += struct.pack('>I', offss[n] + 12 + (4 if typ == 'mat1' else 8) * len(ls)) + if typ != 'mat1': + chunk += struct.pack('>I', thing['unk']) + chunk += strings + if typ == 'mat1': + materials = ls + elif typ in ('pic1', 'pan1', 'bnd1', 'wnd1', 'txt1'): + chunk = unparse_data(vars, 'pane') + if typ == 'txt1': + vars['~text.name_offs'] = 0x74 + vars['~text.mat_off'] = materials.index(vars['~text.material']) + blob = unicode(vars['~text.text']).encode('utf_16_be') + vars['~text.len1'] = vars['~text.len2'] = len(blob) + chunk += unparse_data(vars, 'text', prefix='~text') + chunk += blob + elif typ == 'pic1': + vars['~pic.mat_off'] = materials.index(vars['~pic.material']) + vars['~pic.num_texcoords'] = len(vars['~pic.texcoords']) + chunk += unparse_data(vars, 'pic', prefix='~pic') + for tc in vars['~pic.texcoords']: + chunk += struct.pack('>ffffffff', *tc) + elif typ != 'pan1': + failz + else: + raise Exception('Unhandled type %s' % typ) + rlyt += typ + struct.pack('>I', len(chunk) + 8) + chunk + + +rlyt = rlyt[:8] + struct.pack('>I', len(rlyt)) + rlyt[0xc:] + + +g = open(sys.argv[2], 'wb') +g.write(rlyt) +g.close()
\ No newline at end of file |
