dreamcast/x/parse.py
2025-02-17 23:09:47 -06:00

371 lines
9.2 KiB
Python

from pprint import pprint
import sys
import lex
import templates
with open(sys.argv[1], "rb") as f:
buf = f.read()
mem = memoryview(buf)
class TokenReader:
def __init__(self, mem):
self.tokens = list(lex.lex_all(mem, 0))
self.ix = 0
def consume(self, o):
assert self.tokens[self.ix] == o, (self.tokens[self.ix], o)
self.ix += 1
def consume_type(self, t):
assert type(self.tokens[self.ix]) == t, (t,
self.tokens[self.ix],
type(self.tokens[self.ix]))
self.ix += 1
return self.tokens[self.ix-1]
def match(self, o):
if self.tokens[self.ix] == o:
self.ix += 1
return self.tokens[self.ix-1]
else:
return False
def match_type(self, t):
if type(self.tokens[self.ix]) == t:
self.ix += 1
return self.tokens[self.ix-1]
else:
return False
def peek(self):
return self.tokens[self.ix]
def eof(self):
return self.ix >= len(self.tokens)
def parse_int_raw(r):
i = r.consume_type(int)
return i
def parse_float_raw(r):
i = r.consume_type(float)
return i
def parse_int(r):
i = r.consume_type(int)
r.consume(lex.TOKEN_SEMICOLON)
return i
def parse_float(r):
f = r.consume_type(float)
r.consume(lex.TOKEN_SEMICOLON)
return f
def parse_vector(r):
x = parse_float(r)
y = parse_float(r)
z = parse_float(r)
return templates.Vector(x, y, z)
def parse_string(r):
s = r.consume_type(str)
r.consume(lex.TOKEN_SEMICOLON)
return s
def parse_color_rgba(r):
red = r.consume_type(float)
r.consume(lex.TOKEN_COMMA)
green = r.consume_type(float)
r.consume(lex.TOKEN_COMMA)
blue = r.consume_type(float)
r.consume(lex.TOKEN_COMMA)
alpha = r.consume_type(float)
r.consume(lex.TOKEN_SEMICOLON)
r.consume(lex.TOKEN_SEMICOLON)
return templates.ColorRGBA(
red, green, blue, alpha
)
def parse_color_rgb(r):
red = r.consume_type(float)
r.consume(lex.TOKEN_COMMA)
green = r.consume_type(float)
r.consume(lex.TOKEN_COMMA)
blue = r.consume_type(float)
r.consume(lex.TOKEN_SEMICOLON)
r.consume(lex.TOKEN_SEMICOLON)
return templates.ColorRGB(
red, green, blue
)
def parse_list(r, n, parse, *, delim=lex.TOKEN_COMMA):
l = []
for _ in range(n - 1):
l.append(parse(r))
if delim is not None:
r.consume(delim)
l.append(parse(r))
r.consume(lex.TOKEN_SEMICOLON)
return l
def parse_matrix4x4(r):
v = []
for i in range(15):
v.append(r.consume_type(float))
r.consume(lex.TOKEN_COMMA)
v.append(r.consume_type(float))
r.consume(lex.TOKEN_SEMICOLON)
r.consume(lex.TOKEN_SEMICOLON)
return templates.Matrix4x4(
v
)
def parse_header(r):
r.consume(b"Header")
r.consume(lex.TOKEN_LBRACKET)
major = parse_int(r)
minor = parse_int(r)
flags = parse_int(r)
r.consume(lex.TOKEN_RBRACKET)
return templates.Header(
major, minor, flags
)
def parse_material(r):
r.consume(b"Material")
name = r.consume_type(bytes)
r.consume(lex.TOKEN_LBRACKET)
faceColor = parse_color_rgba(r)
power = parse_float(r)
specularColor = parse_color_rgb(r)
emissiveColor = parse_color_rgb(r)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r))
return name, templates.Material(
faceColor,
power,
specularColor,
emissiveColor,
objects
)
def parse_texture_filename(r):
r.consume(b"TextureFilename")
r.consume(lex.TOKEN_LBRACKET)
filename = parse_string(r)
r.consume(lex.TOKEN_RBRACKET)
return templates.TextureFilename(
filename
)
def parse_frame(r):
r.consume(b"Frame")
name = r.consume_type(bytes)
r.consume(lex.TOKEN_LBRACKET)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r))
return name, templates.Frame(
objects
)
def parse_frame_transform_matrix(r):
r.consume(b"FrameTransformMatrix")
r.consume(lex.TOKEN_LBRACKET)
frameMatrix = parse_matrix4x4(r)
r.consume(lex.TOKEN_RBRACKET)
return templates.FrameTransformMatrix(
frameMatrix
)
def parse_mesh_face(r):
nFaceVertexIndices = parse_int(r)
faceVertexIndices = parse_list(r, nFaceVertexIndices, parse_int_raw)
return templates.MeshFace(
nFaceVertexIndices,
faceVertexIndices,
)
def parse_mesh(r):
r.consume(b"Mesh")
name = r.consume_type(bytes)
r.consume(lex.TOKEN_LBRACKET)
nVertices = parse_int(r)
vertices = parse_list(r, nVertices, parse_vector)
nFaces = parse_int(r)
faces = parse_list(r, nFaces, parse_mesh_face)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r))
return name, templates.Mesh(
nVertices,
vertices,
nFaces,
faces,
objects
)
def parse_mesh_material_list(r):
r.consume(b"MeshMaterialList")
r.consume(lex.TOKEN_LBRACKET)
nMaterials = parse_int(r)
nFaceIndices = parse_int(r)
faceIndices = parse_list(r, nFaceIndices, parse_int, delim=None)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r, "Material"))
return templates.MeshMaterialList(
nMaterials,
nFaceIndices,
faceIndices,
objects
)
def parse_mesh_normals(r):
r.consume(b"MeshNormals")
r.consume(lex.TOKEN_LBRACKET)
nNormals = parse_int(r)
normals = parse_list(r, nNormals, parse_vector)
nFaceNormals = parse_int(r)
faceNormals = parse_list(r, nFaceNormals, parse_mesh_face)
r.consume(lex.TOKEN_RBRACKET)
return templates.MeshNormals(
nNormals,
normals,
nFaceNormals,
faceNormals
)
def parse_coords2d(r):
u = parse_float(r)
v = parse_float(r)
return templates.Coords2D(u, v)
def parse_mesh_texture_coords(r):
r.consume(b"MeshTextureCoords")
r.consume(lex.TOKEN_LBRACKET)
nTextureCoords = parse_int(r)
textureCoords = parse_list(r, nTextureCoords, parse_coords2d)
r.consume(lex.TOKEN_RBRACKET)
return templates.MeshTextureCoords(
nTextureCoords,
textureCoords
)
def parse_animation_set(r):
r.consume(b"AnimationSet")
name = r.consume_type(bytes)
r.consume(lex.TOKEN_LBRACKET)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r, b"Animation"))
return name, templates.AnimationSet(
objects
)
def parse_animation(r):
r.consume(b"Animation")
name = r.consume_type(bytes)
r.consume(lex.TOKEN_LBRACKET)
objects = []
while not r.match(lex.TOKEN_RBRACKET):
objects.append(parse_one_ref(r, {b"AnimationKey", b"AnimationOptions"}))
return name, templates.Animation(
objects
)
def parse_float_keys(r):
nValues = parse_int(r)
values = parse_list(r, nValues, parse_float_raw)
r.consume(lex.TOKEN_SEMICOLON) # FIXME: is this correct for nKeys>1?
return templates.TimedFloatKeys(
nValues,
values,
)
def parse_timed_float_keys(r):
time = parse_int(r)
tfkeys = parse_float_keys(r)
return templates.TimedFloatKeys(
time,
tfkeys,
)
def parse_animation_key(r):
r.consume(b"AnimationKey")
r.consume(lex.TOKEN_LBRACKET)
keyType = parse_int(r)
nKeys = parse_int(r)
keys = parse_list(r, nKeys, parse_timed_float_keys)
r.consume(lex.TOKEN_RBRACKET)
return templates.AnimationKey(
keyType,
nKeys,
keys,
)
def parse_one(r, peek_token=None):
token = r.peek()
if peek_token != None:
if type(peek_token) is set:
assert token in peek_token, (token, peek_token)
else:
assert token == peek_token, (token, peek_token)
if token == b"Header":
return parse_header(r)
elif token == b"Material":
return parse_material(r)
elif token == b"TextureFilename":
return parse_texture_filename(r)
elif token == b"Frame":
return parse_frame(r)
elif token == b"FrameTransformMatrix":
return parse_frame_transform_matrix(r)
elif token == b"Mesh":
return parse_mesh(r)
elif token == b"MeshMaterialList":
return parse_mesh_material_list(r)
elif token == b"MeshNormals":
return parse_mesh_normals(r)
elif token == b"MeshTextureCoords":
return parse_mesh_texture_coords(r)
elif token == b"AnimationSet":
return parse_animation_set(r)
elif token == b"Animation":
return parse_animation(r)
elif token == b"AnimationKey":
return parse_animation_key(r)
else:
assert False, token
def parse_one_ref(r, peek_token=None):
if r.match(lex.TOKEN_LBRACKET):
name = r.consume_type(bytes)
r.consume(lex.TOKEN_RBRACKET)
return templates.Reference(
name
)
else:
token = r.peek()
return parse_one(r, peek_token)
def parse_all(r):
while not r.eof():
yield parse_one_ref(r)
r = TokenReader(mem)
for i in parse_all(r):
pprint(i)