162 lines
4.5 KiB
Python
162 lines
4.5 KiB
Python
# https://gitlab.com/bztsrc/mtsedit/blob/master/docs/mts_format.md
|
|
|
|
import argparse
|
|
import json
|
|
import pathlib
|
|
|
|
import lupa
|
|
|
|
from stream import StreamReader, StreamWriter
|
|
|
|
lua = lupa.LuaRuntime(unpack_returned_tuples=True)
|
|
|
|
|
|
def is_schem(file: pathlib.Path):
|
|
return file.suffix == '.mts'
|
|
|
|
|
|
def convert_schem(child, alias_map):
|
|
print(f'processing {child}')
|
|
with child.open('rb') as fh:
|
|
contents = fh.read()
|
|
|
|
reader = StreamReader(contents)
|
|
magic = reader.bytes(4)
|
|
if magic != b'MTSM':
|
|
raise RuntimeError(f'invalid magic number {magic}')
|
|
version = reader.u16()
|
|
if version != 4:
|
|
raise RuntimeError(f'unexpected version {version}')
|
|
x = reader.u16()
|
|
y = reader.u16()
|
|
z = reader.u16()
|
|
layer_probability_values = reader.bytes(y)
|
|
name_id_length = reader.u16()
|
|
names = []
|
|
any_changed = False
|
|
for _ in range(name_id_length):
|
|
name = reader.bytes(reader.u16())
|
|
alias = alias_map.get(name.decode())
|
|
if alias:
|
|
any_changed = True
|
|
names.append(alias.encode())
|
|
else:
|
|
names.append(name)
|
|
|
|
if any_changed:
|
|
print('writing changes...')
|
|
rest = reader.rest()
|
|
with child.open('wb') as fh:
|
|
writer = StreamWriter(fh)
|
|
writer.bytes(b'MTSM')
|
|
writer.u16(4)
|
|
writer.u16(x)
|
|
writer.u16(y)
|
|
writer.u16(z)
|
|
writer.bytes(layer_probability_values)
|
|
writer.u16(name_id_length)
|
|
for name in names:
|
|
writer.u16(len(name))
|
|
writer.bytes(name)
|
|
writer.bytes(rest)
|
|
|
|
|
|
def is_we(file: pathlib.Path):
|
|
return file.suffix == '.we'
|
|
|
|
|
|
def lua_dump(value):
|
|
if type(value) is str:
|
|
return repr(value)
|
|
elif type(value) in {int, float}:
|
|
return str(value)
|
|
elif type(value) in {list, tuple}:
|
|
return f'{{{", ".join(map(lua_dump, value))}}}'
|
|
elif type(value) is dict:
|
|
return '{' + ', '.join(f'[{lua_dump(k)}] = {lua_dump(v)}' for k, v in value.items()) + '}'
|
|
elif value is None:
|
|
return 'nil'
|
|
elif value is True:
|
|
return 'true'
|
|
elif value is False:
|
|
return 'false'
|
|
elif lupa.lua_type(value) == 'table':
|
|
return lua_dump(dict(value.items()))
|
|
else:
|
|
raise RuntimeError(f'value {value!r} w/ unexpected type {type(value)}')
|
|
|
|
|
|
def convert_we(child, alias_map):
|
|
print(f'processing {child}')
|
|
with child.open('r') as fh:
|
|
contents = fh.read()
|
|
|
|
assert(contents[:9] == '5:return ')
|
|
table = lua.eval(contents[9:])
|
|
data = tuple(map(dict, table.values()))
|
|
any_changed = False
|
|
for point in data:
|
|
alias = alias_map.get(point['name'])
|
|
if alias:
|
|
point['name'] = alias
|
|
any_changed = True
|
|
|
|
if any_changed:
|
|
print('writing changes...')
|
|
output = f'5:return {lua_dump(data)}'
|
|
with child.open('w') as fh:
|
|
fh.write(output)
|
|
|
|
|
|
def create_alias_map(stairsplus_dump: pathlib.Path):
|
|
print('reading aliases from dump')
|
|
aliases = {}
|
|
with stairsplus_dump.open() as fh:
|
|
data = json.load(fh)
|
|
|
|
for alias, shaped_node in data['aliases'].items():
|
|
aliases[alias] = shaped_node
|
|
|
|
return aliases
|
|
|
|
|
|
def main(args):
|
|
alias_map = create_alias_map(args.stairsplus_dump)
|
|
|
|
for child in args.schems.iterdir():
|
|
if child.is_file():
|
|
if is_schem(child):
|
|
convert_schem(child, alias_map)
|
|
elif is_we(child):
|
|
convert_we(child, alias_map)
|
|
else:
|
|
print(f'unknown file type {child.suffix}')
|
|
|
|
|
|
def existing_file(path: str) -> pathlib.Path:
|
|
file_path = pathlib.Path(path)
|
|
if not file_path.exists():
|
|
raise argparse.ArgumentTypeError(f'{path!r} does not exist.')
|
|
if not file_path.is_file():
|
|
raise argparse.ArgumentTypeError(f'{path!r} is not a file.')
|
|
return file_path
|
|
|
|
|
|
def existing_directory(path: str) -> pathlib.Path:
|
|
file_path = pathlib.Path(path)
|
|
if not file_path.exists():
|
|
raise argparse.ArgumentTypeError(f'{path!r} does not exist.')
|
|
if not file_path.is_dir():
|
|
raise argparse.ArgumentTypeError(f'{path!r} is not a directory.')
|
|
return file_path
|
|
|
|
|
|
def parse_args(args=None, namespace=None):
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument('stairsplus_dump', type=existing_file)
|
|
p.add_argument('schems', type=existing_directory)
|
|
return p.parse_args(args=args, namespace=namespace)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(parse_args())
|