aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ethyl.py100
-rw-r--r--parse-brlyt.py15
-rw-r--r--write-brlyt.py71
3 files changed, 145 insertions, 41 deletions
diff --git a/ethyl.py b/ethyl.py
index 2d0d205..5cd9274 100644
--- a/ethyl.py
+++ b/ethyl.py
@@ -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