papermario/tools/splat_ext/PaperMarioMessages.py
2020-11-11 16:21:25 -05:00

431 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from segtypes.segment import N64Segment
from pathlib import Path
CHARSET = {
0x00: "𝅘𝅥𝅮",
0x01: "!",
0x02: '"',
0x03: "#",
0x04: "$",
0x05: "%",
0x06: "&",
0x07: "'",
0x08: "(",
0x09: ")",
0x0A: "*",
0x0B: "+",
0x0C: ",",
0x0D: "-",
0x0E: ".",
0x0F: "/",
0x10: "0",
0x11: "1",
0x12: "2",
0x13: "3",
0x14: "4",
0x15: "5",
0x16: "6",
0x17: "7",
0x18: "8",
0x19: "9",
0x1A: ":",
0x1B: ";",
0x1C: "<",
0x1D: "=",
0x1E: ">",
0x1F: "?",
0x20: "@",
0x21: "A",
0x22: "B",
0x23: "C",
0x24: "D",
0x25: "E",
0x26: "F",
0x27: "G",
0x28: "H",
0x29: "I",
0x2A: "J",
0x2B: "K",
0x2C: "L",
0x2D: "M",
0x2E: "N",
0x2F: "O",
0x30: "P",
0x31: "Q",
0x32: "R",
0x33: "S",
0x34: "T",
0x35: "U",
0x36: "V",
0x37: "W",
0x38: "X",
0x39: "Y",
0x3A: "Z",
0x3B: "\\[",
0x3C: "¥",
0x3D: "]",
0x3E: "^",
0x3F: "_",
0x40: "`",
0x41: "a",
0x42: "b",
0x43: "c",
0x44: "d",
0x45: "e",
0x46: "f",
0x47: "g",
0x48: "h",
0x49: "i",
0x4A: "j",
0x4B: "k",
0x4C: "l",
0x4D: "m",
0x4E: "n",
0x4F: "o",
0x50: "p",
0x51: "q",
0x52: "r",
0x53: "s",
0x54: "t",
0x55: "u",
0x56: "v",
0x57: "w",
0x58: "x",
0x59: "y",
0x5A: "z",
0x5B: "{",
0x5C: "|",
0x5D: "}",
0x5E: "~",
0x5F: "°",
0x60: "À",
0x61: "Á",
0x62: "Â",
0x63: "Ä",
0x64: "Ç",
0x65: "È",
0x66: "É",
0x67: "Ê",
0x68: "Ë",
0x69: "Ì",
0x6A: "Í",
0x6B: "Î",
0x6C: "Ï",
0x6D: "Ñ",
0x6E: "Ò",
0x6F: "Ó",
0x70: "Ô",
0x71: "Ö",
0x72: "Ù",
0x73: "Ú",
0x74: "Û",
0x75: "Ü",
0x76: "ß",
0x77: "à",
0x78: "á",
0x79: "â",
0x7A: "ä",
0x7B: "ç",
0x7C: "è",
0x7D: "é",
0x7E: "ê",
0x7F: "ë",
0x80: "ì",
0x81: "í",
0x82: "î",
0x83: "ï",
0x84: "ñ",
0x85: "ò",
0x86: "ó",
0x87: "ô",
0x88: "ö",
0x89: "ù",
0x8A: "ú",
0x8B: "û",
0x8C: "ü",
0x8D: "¡",
0x8E: "¿",
0x8F: "ª",
0x90: "",
0x91: "",
0x92: "",
0x93: "",
0x94: "",
0x95: "",
0x96: "",
0x97: "",
0xA2: "",
0xA3: "",
0xA4: "",
0xA5: "",
0xF7: " ",
0xF0: "[br]\n",
0xF1: "[prompt]",
0xF2: {None: lambda d: (f"[sleep {d[0]}]", 1)},
0xFB: "[next]\n",
0xFC: {
0x01: "[style=right]\n",
0x02: "[style=left]\n",
0x03: "[style=center]\n",
0x04: "[style=tattle]\n",
0x05: {None: lambda d: (f"[style=choice x={d[1]} y={d[3]} w={d[0]} h={d[2]}]\n", 4)},
0x06: "[style=inspect]\n",
0x07: "[style=sign]\n",
0x08: "[style=lamppost]\n",
0x09: "[style=postcard]\n",
0x0A: "[style=popup]\n",
0x0C: {None: lambda d: (f"[style=upgrade x={d[1]} y={d[3]} w={d[0]} h={d[2]}]\n", 4)},
0x0D: "[style=narrate]\n",
0x0E: "[style=epilogue]\n",
},
0xFF: {
0x00: {
0: "[font=normal]",
3: "[font=title]\n",
4: "[font=subtitle]\n",
},
0x05: {
0x0A: "[color=normal]",
0x20: "[color=red]",
0x21: "[color=pink]",
0x22: "[color=purple]",
0x23: "[color=blue]",
0x24: "[color=cyan]",
0x25: "[color=green]",
0x26: "[color=yellow]",
0x00: "[color=normal ctx=diary]",
0x07: "[color=red ctx=diary]",
0x17: "[color=dark ctx=inspect]",
0x18: "[color=normal ctx=sign]",
0x19: "[color=red ctx=sign]",
0x1A: "[color=blue ctx=sign]",
0x1B: "[color=green ctx=sign]",
0x28: "[color=red ctx=popup]",
0x29: "[color=pink ctx=popup]",
0x2A: "[color=purple ctx=popup]",
0x2B: "[color=blue ctx=popup]",
0x2C: "[color=teal ctx=popup]",
0x2D: "[color=green ctx=popup]",
0x2E: "[color=yellow ctx=popup]",
0x2F: "[color=normal ctx=popup]",
},
0x07: "[noskip]\n",
0x08: "[/noskip]\n",
0x09: "[instant]\n",
0x0A: "[/instant]\n",
0x0B: {None: lambda d: (f"[kerning={d[0]}]", 1)},
0x0C: {None: lambda d: (f"[scroll {d[0]}]", 1)},
0x0D: {None: lambda d: (f"[size x={d[0]} y={d[0]}]\n", 2)},
0x0E: "[/size]\n",
0x0F: {None: lambda d: (f"[speed delay={d[0]} chars={d[1]}]", 2)},
0x10: {None: lambda d: (f"[pos x={d[0]} y={d[1]}]", 2)},
0x11: {None: lambda d: (f"[pos y={d[0]}]", 1)},
0x12: {None: lambda d: (f"[indent {d[0]}]", 1)},
0x13: {None: lambda d: (f"[down {d[0]}]", 1)},
0x14: {None: lambda d: (f"[up {d[0]}]", 1)},
0x15: {None: lambda d: (f"[image {d[0]}]\n", 1)},
0x16: {None: lambda d: (f"[sprite {d[0]} {d[1]} {d[2]}]\n", 3)},
0x17: {None: lambda d: (f"[item {d[0]} {d[1]}]\n", 2)},
0x18: {None: lambda d: (f"[image {d[0]} {d[1]} {d[2]} {d[3]} {d[4]} {d[5]} {d[6]}]\n", 7)},
0x1E: {None: lambda d: (f"[cursor {d[0]}]", 1)},
0x1F: {None: lambda d: (f"[choicecount={d[0]}]", 1)},
0x20: {None: lambda d: (f"[cancel={d[0]}]", 1)},
0x21: {None: lambda d: (f"[option {d[0]}]", 1)},
0x24: {0xFF: {0x05: {
0x10: {0x98: {0xFF: {0x25: ""}}},
0x11: {0x99: {0xFF: {0x25: ""}}},
0x12: {0xA1: {0xFF: {0x25: ""}}},
0x13: {
0x9D: {0xFF: {0x25: ""}},
0x9E: {0xFF: {0x25: ""}},
0x9F: {0xFF: {0x25: ""}},
0xA0: {0xFF: {0x25: ""}},
},
0x14: {0x9C: {0xFF: {0x25: ""}}},
}}},
0x26: {
0x00: "[shaky]",
0x01: "[wavy]",
0x03: {None: lambda d: (f"[noise fade={d[0]}]", 1)},
0x05: {None: lambda d: (f"[faded-shaky fade={d[0]}]", 1)},
0x07: {None: lambda d: (f"[fade={d[0]}]", 1)},
0x0A: "[shout]",
0x0B: "[whisper]",
0x0C: "[scream]",
0x0D: "[chortle]",
0x0E: "[shadow]",
},
0x27: {
0x00: "[/shaky]",
0x01: "[/wavy]",
0x03: "[/noise]",
0x05: "[/faded-shaky]",
0x07: "[/fade]",
0x0A: "[/shout]",
0x0B: "[/whisper]",
0x0C: "[/scream]",
0x0D: "[/chortle]",
0x0E: "[/shadow]",
},
0x28: {None: lambda d: (f"[var {d[0]}]", 1)},
0x29: {None: lambda d: (f"[center {d[0]}]", 1)},
0x2E: {None: lambda d: (f"[volume={d[0]}]", 1)},
0x2F: {
1: "[sound=bowser]\n",
2: "[sound=spirit]\n",
None: lambda d: (f"[sound={d[0]}]\n", 1),
},
None: lambda d: (f"[func 0x{d[0]:X}]", 1),
},
None: lambda d: (f"[raw 0x{d[0]:02X}]", 1),
}
CHARSET_CREDITS = {
**CHARSET,
0x00: "A",
0x01: "B",
0x02: "C",
0x03: "D",
0x04: "E",
0x05: "F",
0x06: "G",
0x07: "H",
0x08: "I",
0x09: "J",
0x0A: "K",
0x0B: "L",
0x0C: "M",
0x0D: "N",
0x0E: "O",
0x0F: "P",
0x10: "Q",
0x11: "R",
0x12: "S",
0x13: "T",
0x14: "U",
0x15: "V",
0x16: "W",
0x17: "X",
0x18: "Y",
0x19: "Z",
0x1A: "'",
0x1B: ".",
0x1C: ",",
0x1D: "0",
0x1E: "1",
0x1F: "2",
0x20: "3",
0x21: "4",
0x22: "5",
0x23: "6",
0x24: "7",
0x25: "8",
0x26: "9",
0x27: "©",
0x28: "&",
0xF7: " ",
}
class N64SegPaperMarioMessages(N64Segment):
def __init__(self, segment, next_segment, options):
super().__init__(segment, next_segment, options)
self.files = segment.get("files", []) if type(segment) is dict else []
def split(self, rom_bytes, base_path):
data = rom_bytes[self.rom_start: self.rom_end]
section_offsets = []
pos = 0
while True:
offset = int.from_bytes(data[pos:pos+4], byteorder="big")
if offset == 0:
break
section_offsets.append(offset)
pos += 4
for i, section_offset in enumerate(section_offsets):
name = f"{i:02X}"
if len(self.files) >= i:
name = self.files[i]
msg_offsets = []
pos = section_offset
while True:
offset = int.from_bytes(data[pos:pos+4], byteorder="big")
if offset == section_offset:
break
msg_offsets.append(offset)
pos += 4
self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})")
path = Path(base_path, self.name, name + ".msg")
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w") as f:
for j, msg_offset in enumerate(msg_offsets):
if j != 0:
f.write("\n")
f.write(f"[message section=0x{i:02X} index={j}]\n")
self.write_message_markup(data[msg_offset:], f)
f.write("\n[/message]\n")
def get_ld_files(self):
return [("", self.name, ".data")]
@staticmethod
def get_default_name(addr):
return "msg"
def write_message_markup(self, data, f):
pos = 0
font = CHARSET
while data[pos] != 0xFD:
markup, delta = self.char_to_markup(data[pos:], charset=font)
f.write(markup)
pos += delta
if markup == "[font=title]\n" or markup == "[font=subtitle]\n":
font = CHARSET_CREDITS
elif markup == "[font=normal]":
font = CHARSET
def char_to_markup(self, data, charset=CHARSET):
value = None
char = int(data[0])
if char in charset:
value = charset[char]
elif None in charset:
value = charset[None]
if type(value) is str:
return value, 1
if callable(value):
return value(data)
if type(value) is dict:
markup, delta = self.char_to_markup(data[1:], charset=value)
if markup is None:
if None in charset:
value = charset[None]
if callable(value):
return value(data)
return value, 1
else:
return markup, delta + 1
return None, 0