chaos-drop/converters.py

122 lines
3.9 KiB
Python

import fxconv
import re
def convert(input, output, params, target):
recognized = True
if params["custom-type"] == "level":
o = convert_level(input, params)
else:
recognized = False
if recognized:
fxconv.elf(o, output, "_" + params["name"], **target)
return 0
return 1
def num(value):
return fxconv.u32(value * 65536)
def convert_level(input, params):
RE_VALUES = {
"name": re.compile(r'.*'),
"finish": re.compile(r'\d+')
}
RE_ELEMENTS = {
"mirror": (True, re.compile(r'')),
"plane": (False, re.compile(r'([A-Z]+)\s+(\S+)')),
"text": (True, re.compile(r'\"([^"]+)\"')),
}
RE_POSITION = re.compile(r'(\d+)(?:-(\d+))?')
header = dict()
elements = []
with open(input, "r") as fp:
lines = [l for l in fp.read().splitlines() if l
and not l.startswith("#")]
for i, l in enumerate(lines):
if ":" not in l:
raise fxconv.FxconvError(f"invalid line '{l}': no ':'")
key, value = [x.strip() for x in l.split(":", 1)]
m_pos = RE_POSITION.fullmatch(key)
if m_pos:
start = int(m_pos[1])
end = int(m_pos[2]) if m_pos[2] else None
things = [x.strip() for x in value.strip().split(" ", 1)]
if len(things) == 1:
key, value = things[0], ""
else:
key, value = things
if key not in RE_ELEMENTS:
raise fxconv.FxconvError(f"unknown element '{key}'")
if RE_ELEMENTS[key][0] and end is None:
raise fxconv.FxconvError(f"'{key}' needs an end position")
if not RE_ELEMENTS[key][0] and end is not None:
raise fxconv.FxconvError(f"'{key}' needs no end position")
m = RE_ELEMENTS[key][1].fullmatch(value)
if not m:
raise fxconv.FxconvError(
f"invalid value for '{key}': '{value}")
elements.append((key, start, end, m))
else:
if key not in RE_VALUES:
raise fxconv.FxconvError(f"unknown key '{key}'")
if not RE_VALUES[key].fullmatch(value):
raise fxconv.FxconvError(
f"invalid value for '{key}': '{value}'")
header[key] = value
mirrors = fxconv.ObjectData()
planes = fxconv.ObjectData()
texts = fxconv.ObjectData()
mirror_count = 0
plane_count = 0
text_count = 0
for (key, start, end, m) in elements:
if key == "mirror":
mirrors += num(start * 32)
mirrors += num(end * 32)
mirror_count += 1
elif key == "plane":
kind = m[1]
shape = m[2]
assert shape.startswith("0b")
shape = "".join(c for c in shape[2:] if c != "'")
assert(len(shape) == 25)
kinds = {
"DAMAGE": 0,
"ARROW": 1,
}
assert kind in kinds
planes += num(start * 32)
planes += fxconv.u32(int(shape, 2))
planes += fxconv.u16(kinds[kind])
planes += fxconv.u16(0) # TODO: Arrow directions
plane_count += 1
elif key == "text":
string = m[1].replace("\\n", "\n").replace("\\\\", "\\")
texts += num(start * 32)
texts += num(end * 32)
texts += fxconv.string(string)
text_count += 1
o = fxconv.ObjectData()
o += fxconv.string(header.get("name", "<Untitled>"))
o += num(int(header["finish"]) * 32)
o += fxconv.u32(mirror_count)
o += fxconv.ptr(mirrors)
o += fxconv.u32(plane_count)
o += fxconv.ptr(planes)
o += fxconv.u32(text_count)
o += fxconv.ptr(texts)
return o