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) DISTANCE_FACTOR = 24 def advance(position, coordinate): silent = False if coordinate.startswith("@"): silent = True coordinate = coordinate[1:] if coordinate.startswith("="): value = int(coordinate) else: value = position + int(coordinate[1:]) if silent: return position, value else: return value, value def convert_level(input, params): RE_VALUES = { "name": re.compile(r'.*'), } RE_ELEMENTS = { "mirror": (True, re.compile(r'')), "plane": (False, re.compile(r'(\S+)\s+(\S+)')), "text": (True, re.compile(r'\"([^"]*)\"')), "vfx": (True, re.compile(r'([a-z]+)')), } RE_POSITION = re.compile(r'^\s*(@)?\s*(?:(\d+)/)?\s*(\d+)?') header = dict() elements = [] pos = 0 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): # Key/value metadata if ":" in l: key, value = [x.strip() for x in l.split(":", 1)] 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 continue # Elements m_pos = RE_POSITION.match(l) if not m_pos: raise fxconv.FxconvError(f"invalid position: '{l}'") start = pos + int(m_pos[2]) if m_pos[2] else pos end = start + int(m_pos[3]) if m_pos[3] else start if not m_pos[1]: pos = end l = l[len(m_pos[0]):] things = [x.strip() for x in l.strip().split(" ", 1)] if len(things) == 1: elname, data = things[0], "" else: elname, data = things if RE_ELEMENTS[elname][0] and end == start: raise fxconv.FxconvError(f"'{elname}' needs a size") if not RE_ELEMENTS[elname][0] and end != start: raise fxconv.FxconvError(f"'{elname}' needs no size") m = RE_ELEMENTS[elname][1].fullmatch(data) if not m: raise fxconv.FxconvError(f"bad data for '{elname}': '{data}'") elements.append((elname, start, end, m)) mirrors = fxconv.ObjectData() planes = fxconv.ObjectData() texts = fxconv.ObjectData() vfxs = fxconv.ObjectData() mirror_count = 0 plane_count = 0 text_count = 0 vfx_count = 0 for (key, start, end, m) in elements: if key == "mirror": mirrors += num(start * DISTANCE_FACTOR) mirrors += num(end * DISTANCE_FACTOR) mirror_count += 1 elif key == "plane": kind = m[1] shape_str = m[2] predef_shapes = { "VLINES_1_5": 0b1000110001100011000110001, "VLINES_1_3_5": 0b1010110101101011010110101, "VLINES_2_4": 0b0101001010010100101001010, "HLINES_1_5": 0b1111100000000000000011111, "HLINES_1_3_5": 0b1111100000111110000011111, "HLINES_2_4": 0b0000011111000001111100000, "HASH": 0b0101011111010101111101010, "BOX": 0b1111110001100011000111111, "BOX_DOT": 0b1111110001101011000111111, "BIGDOT": 0b0000001110011100111000000, "DOT": 0b0000000000001000000000000, "RING": 0b0000001110010100111000000, "CORNERS": 0b1101111011000001101111011, "DIAGONAL+": 0b0001100111011101110011000, "DIAGONAL-": 0b1100011100011100011100011, "ARROW_+X": 0b0010000010111110001000100, "ARROW_-X": 0b0010001000111110100000100, "ARROW_+Z": 0b0010001110101010010000100, "ARROW_-Z": 0b0010000100101010111000100, "FULL": 0b1111111111111111111111111, } if shape_str in predef_shapes: shape = predef_shapes[shape_str] else: assert shape_str.startswith("0b") shape = "".join(c for c in shape_str[2:] if c != "'") assert(len(shape) == 25) shape = int(shape, 2) kinds = { "DAMAGE": 0, "ARROW": 1, "INVIS": 2, "DEATH": 3, } assert kind in kinds data = 0 if kind == "ARROW": directions = ["ARROW_-Z", "ARROW_+X", "ARROW_+Z", "ARROW_-X"] assert shape_str in directions data = directions.index(shape_str) planes += num(start * DISTANCE_FACTOR) planes += fxconv.u32(shape) planes += fxconv.u16(kinds[kind]) planes += fxconv.u16(data) plane_count += 1 elif key == "text": string = m[1].replace("\\n", "\n").replace("\\\\", "\\") texts += num(start * DISTANCE_FACTOR) texts += num(end * DISTANCE_FACTOR) texts += fxconv.string(string) text_count += 1 elif key == "vfx": effects = { "darken": 0, "whiten": 1, "invert": 2, } if m[1] not in effects: raise fxconv.FxconvError(f"invalid vfx '{m[1]}'") vfxs += num(start * DISTANCE_FACTOR) vfxs += num(end * DISTANCE_FACTOR) vfxs += fxconv.u32(effects[m[1]]) vfx_count += 1 o = fxconv.ObjectData() o += fxconv.string(header.get("name", "")) o += num(int(header.get("finish", pos+2)) * DISTANCE_FACTOR) 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) o += fxconv.u32(vfx_count) o += fxconv.ptr(vfxs) return o