Last active
December 7, 2023 00:45
-
-
Save Xzonn/f779dcb213fa8c366779f8b22c94f0c4 to your computer and use it in GitHub Desktop.
wmbt exporter for "WarioWare: Get It Together!"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# -*- coding: UTF-8 -*- | |
import os | |
import re | |
import struct | |
import sys | |
_control_names = { | |
"0001": "face", | |
"0002": "size", | |
"0003": "color", | |
"0004": "page_break", | |
"0209": "key" | |
} | |
def export_wmbt(bin): | |
magic = bin[0x00 : 0x08] | |
assert magic == b"MsgStdBn" | |
endianness = bin[0x08 : 0x0a] | |
assert endianness == b"\xff\xfe" | |
encoding, version, number_of_blocks = struct.unpack("<BBH", bin[0x0c : 0x10]) | |
file_size, = struct.unpack("<L", bin[0x12 : 0x16]) | |
# assert file_size == len(bin) | |
pos = 0x20 | |
while pos < len(bin): | |
type = bin[pos : pos + 0x04] | |
size, = struct.unpack("<L", bin[pos + 0x04 : pos + 0x08]) | |
data = bin[pos + 0x10 : pos + 0x10 + size] | |
pos += 0x10 + ((size - 1) // 0x10 + 1) * 0x10 | |
if type == b"LBL1": | |
keys = block_lbl1(data) | |
elif type == b"TXTW" or type == b"TXT2": | |
values = block_txtw(data) | |
data = dict(zip(keys, values)) | |
return data | |
def block_lbl1(data): | |
count, = struct.unpack("<L", data[0x00 : 0x04]) | |
keys = [] | |
for i in range(count): | |
label_count, start = struct.unpack("<LL", data[0x04 + 0x08 * i : 0x0c + 0x08 * i]) | |
pos = start | |
for j in range(label_count): | |
key_length = data[pos] | |
key = data[pos + 1 : pos + key_length + 1].decode("utf-8") | |
index, = struct.unpack("<L", data[pos + key_length + 1 : pos + key_length + 5]) | |
pos += key_length + 5 | |
keys.append((key, index)) | |
return map(lambda x: x[0], sorted(keys, key = lambda x: x[1])) | |
def block_txtw(data): | |
count, = struct.unpack("<L", data[0x00 : 0x04]) | |
values = [] | |
for i in range(count): | |
start, = struct.unpack("<L", data[0x04 + 0x04 * i : 0x08 + 0x04 * i]) | |
if i == count - 1: | |
end = len(data) | |
else: | |
end, = struct.unpack("<L", data[0x08 + 0x04 * i : 0x0c + 0x04 * i]) | |
chars = struct.unpack(f"<{(end - start) // 2}H", data[start : end]) | |
txt = parse_txt(chars) | |
assert txt[-1] == "\0" | |
values.append(txt[ : -1]) | |
return values | |
def control_name(tag_group, tag_type): | |
code = f"{tag_group:02X}{tag_type:02X}" | |
if code in _control_names: | |
return _control_names[code], True | |
else: | |
return code, False | |
def parse_txt(chars): | |
txt = "" | |
pos = 0 | |
while pos < len(chars): | |
char = chars[pos] | |
if char == 0x0e: | |
tag_group, tag_type, para_size = chars[pos + 0x01 : pos + 0x04] | |
assert para_size % 2 == 0 | |
para_size = para_size // 2 | |
paras = chars[pos + 0x04 : pos + 0x04 + para_size] | |
name, is_named = control_name(tag_group, tag_type) | |
if is_named: | |
txt += f"{{{name}" | |
if name == "color": | |
txt += "[#" + "".join(map(lambda x: f"{x:04X}", paras)) + "]" | |
elif name == "key": | |
txt += "[" | |
key_1_len = paras[0] // 2 | |
key_2_len = paras[key_1_len + 1] // 2 | |
txt += "".join(map(chr, paras[1 : key_1_len + 1])) | |
if key_2_len > 0: | |
txt += "|" | |
txt += "".join(map(chr, paras[key_1_len + 2 : key_1_len + key_2_len + 2])) | |
txt += "]" | |
elif para_size > 0: | |
txt += "[" + "".join(map(lambda x: f"{x:04X}", paras)) + "]" | |
else: | |
txt += f"{{[{name}" | |
if para_size > 0: | |
txt += "|" + "".join(map(lambda x: f"{x:04X}", paras)) + "]" | |
txt += "}" | |
pos += para_size + 4 | |
elif char == 0x0f: | |
tag_group, tag_type = chars[pos + 0x01 : pos + 0x03] | |
txt += "{/" + control_name(tag_group, tag_type)[0] + "}" | |
pos += 3 | |
else: | |
txt += chr(char) | |
pos += 1 | |
return txt | |
def parse_path(path, root = ""): | |
path = path.replace("\\", "/") | |
root = root.replace("\\", "/") | |
if path.startswith(root): | |
path = path[len(root) : ] | |
path = re.sub(r"^(\.*/)+", "", path) | |
return path | |
if __name__ == "__main__": | |
args = sys.argv | |
if len(args) > 1: | |
root = args[1] | |
else: | |
root = "./" | |
if len(args) > 2: | |
export = args[2] | |
else: | |
export = "export.txt" | |
ext = ".wmbt" | |
w = os.walk(root) | |
g = open(export, "w", -1, "utf-8") | |
for root, dirs, files in w: | |
for file in files: | |
if file.endswith(ext): | |
path = os.path.join(root, file) | |
with open(path, "rb") as f: | |
txts = export_wmbt(f.read()) | |
g.write(f"{parse_path(path, root)}\n") | |
for k, v in txts.items(): | |
v = v.replace("\\", "\\\\").replace("\t", "\\t").replace("\n", "\\n") | |
g.write(f"{k}\t{v}\n") | |
g.flush() | |
g.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yep, that would be amazing lmao