128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""MAP-Chunk von Level-WADs parsen/schreiben (Lama/Croc-2-Engine, PC).
|
|
|
|
Nachgebaut aus groove.exe FUN_0042ac50 (MAP-Lader). Hängt ab von
|
|
WFPC-Flags (u32 im WFPC-Chunk, Bits 0x10000, 0x10000000, 0x200000, 0x4000000, 0x10)
|
|
TRAK-Modell-Vertexzahlen (Vertexfarben-Block).
|
|
Siehe notes/objects.md.
|
|
"""
|
|
import struct
|
|
|
|
|
|
class R:
|
|
def __init__(s, d, o=0):
|
|
s.d, s.o = d, o
|
|
|
|
def u32(s):
|
|
v = struct.unpack_from('<I', s.d, s.o)[0]; s.o += 4; return v
|
|
|
|
def i32(s):
|
|
v = struct.unpack_from('<i', s.d, s.o)[0]; s.o += 4; return v
|
|
|
|
def u16(s):
|
|
v = struct.unpack_from('<H', s.d, s.o)[0]; s.o += 2; return v
|
|
|
|
def f(s, fmt):
|
|
v = struct.unpack_from('<' + fmt, s.d, s.o); s.o += struct.calcsize('<' + fmt); return v
|
|
|
|
|
|
def trak_vertcounts(trak):
|
|
"""TRAK: u32 n, n x 0x84-Header (u16 @0x6c = Vertexzahl), dann Daten."""
|
|
n = struct.unpack_from('<I', trak, 0)[0]
|
|
return [struct.unpack_from('<H', trak, 4 + i * 0x84 + 0x6c)[0] for i in range(n)]
|
|
|
|
|
|
# Feldnamen
|
|
TILE = ('x', 'y', 'z', 'rot', 'b', 'alt') # 3 f32 + 3 u32
|
|
LIGHT = '11I3IH4I2IHHI' # 90 Byte
|
|
PATHNODE = 'IIHHH5I' # 34 Byte
|
|
STRAT = '3H3iIIi4Iiih' + 'H' # 58 Byte (s.u.)
|
|
|
|
|
|
def parse(d, wfpc, vcounts):
|
|
r = R(d)
|
|
m = {}
|
|
nt, W, H = r.f('3I')
|
|
m['W'], m['H'] = W, H
|
|
m['tiles'] = [r.f('3f3I') for _ in range(nt)]
|
|
n = r.u32(); m['list1'] = list(r.f('%dI' % n)) # von Strats per Index referenziert
|
|
n = r.u32(); m['lights'] = [r.f(LIGHT) for _ in range(n)]
|
|
n = r.u32(); m['nodes'] = [r.f(PATHNODE) for _ in range(n)]
|
|
m['tab32'] = r.f('64I') # 32 x (u32,u32) -> DAT_006da350
|
|
m['grid'] = r.f('%dI' % (W * H))
|
|
m['tiles2'] = [r.f('6I') for _ in range(nt)]
|
|
m['tilemodel'] = list(r.f('%dI' % nt))
|
|
alts = []
|
|
if wfpc & 0x10000:
|
|
n = r.u32(); alts = [r.f('5I') for _ in range(n)]
|
|
m['alts'] = alts
|
|
# Vertexfarben: je Tile nv u32 (Byte3 = Frames), ggf. weitere Frames; dann Alt-Modell
|
|
altidx = [i for i, t in enumerate(m['tiles']) if t[5]]
|
|
altmap = dict(zip(altidx, alts))
|
|
start = r.o
|
|
split = [] # Vertexfarben-Bytes je Tile (inkl. Alt-Modell)
|
|
for i in range(nt):
|
|
t0 = r.o
|
|
for mdl in [m['tilemodel'][i]] + ([altmap[i][4]] if i in altmap else []):
|
|
nv = vcounts[mdl]
|
|
cols = r.f('%dI' % nv)
|
|
fr = max([c >> 24 for c in cols] + [0])
|
|
if fr > 1:
|
|
r.o += 4 * nv * (fr - 1)
|
|
split.append(d[t0:r.o])
|
|
m['vcol'] = d[start:r.o]
|
|
m['vcol_split'] = split
|
|
ns, x = r.f('2I')
|
|
m['strat_x'] = x
|
|
m['strats'] = [r.f(STRAT) for _ in range(ns)]
|
|
paths = []
|
|
if wfpc & 0x200000:
|
|
np_ = r.u32()
|
|
for _ in range(np_):
|
|
nsub = r.u32(); subs = []
|
|
for _ in range(nsub):
|
|
cnt, fl = r.f('2H'); pts = []
|
|
for _ in range(cnt):
|
|
a = r.f('2I')
|
|
c = r.f('I') if wfpc & 0x4000000 else ()
|
|
pts.append(a + c + r.f('2H'))
|
|
subs.append((fl, pts))
|
|
paths.append(subs)
|
|
m['paths'] = paths
|
|
m['x200'] = r.u32() if wfpc & 0x10 else None
|
|
m['last'] = r.u16()
|
|
m['rest'] = d[r.o:]
|
|
return m
|
|
|
|
|
|
def build(m, wfpc):
|
|
"""Gegenstück zu parse(); m['vcol'] muss zu tilemodel/alts passen (Rohbytes)."""
|
|
P = struct.pack
|
|
o = [P('<3I', len(m['tiles']), m['W'], m['H'])]
|
|
o += [P('<3f3I', *t) for t in m['tiles']]
|
|
o.append(P('<I', len(m['list1'])) + P('<%dI' % len(m['list1']), *m['list1']))
|
|
o.append(P('<I', len(m['lights'])) + b''.join(P('<' + LIGHT, *x) for x in m['lights']))
|
|
o.append(P('<I', len(m['nodes'])) + b''.join(P('<' + PATHNODE, *x) for x in m['nodes']))
|
|
o.append(P('<64I', *m['tab32']))
|
|
o.append(P('<%dI' % len(m['grid']), *m['grid']))
|
|
o += [P('<6I', *t) for t in m['tiles2']]
|
|
o.append(P('<%dI' % len(m['tilemodel']), *m['tilemodel']))
|
|
if wfpc & 0x10000:
|
|
o.append(P('<I', len(m['alts'])) + b''.join(P('<5I', *a) for a in m['alts']))
|
|
o.append(m['vcol'])
|
|
o.append(P('<2I', len(m['strats']), m['strat_x']))
|
|
o += [P('<' + STRAT, *s) for s in m['strats']]
|
|
if wfpc & 0x200000:
|
|
o.append(P('<I', len(m['paths'])))
|
|
for subs in m['paths']:
|
|
o.append(P('<I', len(subs)))
|
|
for fl, pts in subs:
|
|
o.append(P('<2H', len(pts), fl))
|
|
for p in pts:
|
|
o.append(P('<2I', *p[:2]) + (P('<I', p[2]) if wfpc & 0x4000000 else b'') + P('<2H', *p[-2:]))
|
|
if wfpc & 0x10:
|
|
o.append(P('<I', m['x200']))
|
|
o.append(P('<H', m['last']))
|
|
o.append(m['rest'])
|
|
return b''.join(o)
|