Skip to content

Instantly share code, notes, and snippets.

@Xzonn
Last active December 7, 2023 00:45
Show Gist options
  • Save Xzonn/f779dcb213fa8c366779f8b22c94f0c4 to your computer and use it in GitHub Desktop.
Save Xzonn/f779dcb213fa8c366779f8b22c94f0c4 to your computer and use it in GitHub Desktop.
wmbt exporter for "WarioWare: Get It Together!"
#!/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()
@JokerDKha
Copy link

Can you help about repack this file. Tks for the hard work.

@ADMania
Copy link

ADMania commented Nov 5, 2023

Hello. Do you have importer script for obtained txt file to wmbt?

@Fusseldieb
Copy link

Can you help about repack this file. Tks for the hard work.

Yep, that would be amazing lmao

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment