initial commit: llamamod 0.2.0 – reverse-engineered level formats (wad, map, trak, text, stpc), python tools and notes, java mod manager (iso install, mod pipeline with exe patching, save editor, wine/windows launcher) with mods (test world, debug wireframe, mirror, randomizer, fog, cheats, intro skip, practice, free camera, boot to level, level projects) and the 3d level editor (geometry, objects, cross-world import, environment, textures, play-test, manual)
This commit is contained in:
commit
b36a366240
826 files changed
+105304
No files matched your search
@@ -0,0 +1,127 @@
|
||||
#!/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)
|
||||
Reference in new issue
Block a user