dreamcast/regs/gen/ta_parameter_format.py
Zack Buhman de76c0bed2 ta_parameter_format: generate TA parameter formats from csv
This replaces all handwritten TA parameter format structures with
automatically generated structures.

All valid TA parameter formats are now declared.
2023-12-30 18:55:09 +08:00

198 lines
5.2 KiB
Python

from dataclasses import dataclass
from pprint import pprint
import sys
import csv
from generate import renderer
_field_types = {
"parameter_control_word": "uint32_t",
"user_clip_": "uint32_t",
"object_pointer": "uint32_t",
"bounding_box_": "uint32_t",
"isp_tsp_instruction_word": "uint32_t",
"tsp_instruction_word": "uint32_t",
"texture_control_word": "uint32_t",
"data_size_for_sort_dma": "uint32_t",
"next_address_for_sort_dma": "uint32_t",
"face_color_": "float",
"face_offset_color_": "float",
"x": "float",
"y": "float",
"z": "float",
"base_color_": "float",
"base_intensity_": "float",
"u": "float",
"v": "float",
"u_v": "uint32_t",
"base_color": "uint32_t",
"offset_color": "uint32_t",
"offset_color_": "float",
"base_intensity": "float",
"offset_intensity": "float",
"a_": "float",
"b_": "float",
"c_": "float",
"d_": "float",
"a_u_a_v": "uint32_t",
"b_u_b_v": "uint32_t",
"c_u_c_v": "uint32_t",
"_res": "uint32_t"
}
def get_type(field_name: str):
match = None
match_len = 0
for name, type in _field_types.items():
if field_name.startswith(name) and len(name) >= match_len:
match = type
assert match_len != len(name), (name, match)
match_len = len(name)
assert match != None, field_name
return match
class EndOfInput(Exception):
pass
def next_row(ix, rows, advance):
if ix >= len(rows):
raise EndOfInput
if advance:
while rows[ix][0] == "":
ix += 1
if ix >= len(rows):
raise EndOfInput
row = rows[ix]
ix += 1
return ix, row
@dataclass
class FieldDeclaration:
offset: int
name: str
@dataclass
class StructDeclaration:
name: str
fields: list[FieldDeclaration]
size: int
def parse_type_declaration(ix, rows):
ix, row = next_row(ix, rows, advance=True)
assert len(row) == 2, row
struct_name, empty = row
assert empty == "", row
fields = []
last_offset = -4
res_ix = 0
def terminate():
size = last_offset + 4
assert size == 32 or size == 64, size
return ix, StructDeclaration(
struct_name,
fields,
size
)
while True:
try:
ix, row = next_row(ix, rows, advance=False)
except EndOfInput:
return terminate()
if row[0] == "":
return terminate()
else:
assert len(row) == 2, row
_offset, name = row
offset = int(_offset, 16)
assert offset == last_offset + 4, (hex(offset), hex(last_offset))
last_offset = offset
if name == "":
name = f"_res{res_ix}"
res_ix += 1
fields.append(FieldDeclaration(offset, name))
def parse(rows):
ix = 0
declarations = []
while True:
try:
ix, declaration = parse_type_declaration(ix, rows)
except EndOfInput:
break
declarations.append(declaration)
return declarations
def render_initializer(declaration):
initializer = f"{declaration.name}("
padding = " " * len(initializer)
def start(i):
if i == 0:
return initializer
else:
return padding
nonres_fields = [f for f in declaration.fields if not f.name.startswith('_res')]
for i, field in enumerate(nonres_fields):
s = start(i)
type = get_type(field.name)
comma = ',' if i + 1 < len(nonres_fields) else ''
yield s + f"const {type} {field.name}" + comma
yield padding + ')'
for i, field in enumerate(declaration.fields):
value = field.name if not field.name.startswith('_res') else '0'
s = ':' if i == 0 else ','
yield " " + s + f" {field.name}({value})"
yield "{ }"
def render_static_assertions(declaration):
yield f"static_assert((sizeof ({declaration.name})) == {declaration.size});"
for field in declaration.fields:
yield f"static_assert((offsetof (struct {declaration.name}, {field.name})) == 0x{field.offset:02x});"
def render_declaration(declaration):
yield f"struct {declaration.name} {{"
for field in declaration.fields:
type = get_type(field.name)
yield f"{type} {field.name};"
yield ""
yield from render_initializer(declaration)
yield "};"
yield from render_static_assertions(declaration)
def render_declarations(namespace, declarations):
yield f"namespace {namespace} {{"
for declaration in declarations:
yield from render_declaration(declaration)
yield ""
yield "}"
def headers():
yield "#pragma once"
yield ""
yield "#include <cstdint>"
yield ""
def read_input(filename):
with open(filename) as f:
reader = csv.reader(f, delimiter=",", quotechar='"')
rows = [
[s.strip() for s in row]
for row in reader
]
return rows
if __name__ == "__main__":
rows = read_input(sys.argv[1])
namespace = sys.argv[2]
declarations = parse(rows)
render, out = renderer()
render(headers())
render(render_declarations(namespace, declarations))
print(out.getvalue())