use star rod-style message syntax

This commit is contained in:
Alex Bates 2021-02-10 00:12:28 +00:00
parent c4102d350a
commit d687f84d35
3 changed files with 658 additions and 437 deletions

View File

@ -21,21 +21,22 @@ def try_convert_int(s):
def parse_command(source): def parse_command(source):
if source[0] != "[": if source[0] != "[":
return None, [], {}, source return None, [], source
source = source[1:] # "[" source = source[1:] # "["
inside_brackets = "" inside_brackets = ""
while source[0] != "]": while source[0] != "]":
if source[0] == "\n": if source[0] == "\n":
return None, [], {}, source return None, [], source
inside_brackets += source[0] inside_brackets += source[0]
source = source[1:] source = source[1:]
source = source[1:] # "]" source = source[1:] # "]"
command, *args = inside_brackets.split(" ") command, *raw_args = inside_brackets.split(":")
positional_args = [] """
args = []
named_args = {} named_args = {}
if "=" in command: if "=" in command:
@ -48,22 +49,18 @@ def parse_command(source):
key, value = arg.split("=", 1) key, value = arg.split("=", 1)
named_args[key.lower()] = try_convert_int(value.lower()) named_args[key.lower()] = try_convert_int(value.lower())
else: else:
positional_args.append(try_convert_int(arg)) args.append(try_convert_int(arg))
"""
return command.lower(), positional_args, named_args, source args = []
for arg in raw_args:
args.append(try_convert_int(arg.lower()))
def color_to_code(color, ctx="normal"): return command.lower(), args, source
def color_to_code(color, style):
"""
COLORS = { COLORS = {
"normal": {
"normal": 0x0A,
"red": 0x20,
"pink": 0x21,
"purple": 0x22,
"blue": 0x23,
"cyan": 0x24,
"green": 0x25,
"yellow": 0x26,
},
"diary": { "diary": {
"normal": 0x00, "normal": 0x00,
"red": 0x07, "red": 0x07,
@ -96,14 +93,50 @@ def color_to_code(color, ctx="normal"):
"green": 0x1B, "green": 0x1B,
} }
} }
"""
COLORS = {}
if type(color) is int: if type(color) is int:
return color return color
return COLORS.get(ctx, {}).get(color) return COLORS.get(ctx, {
# [style:left], [style:right]
"normal": 0x0A,
"red": 0x20,
"pink": 0x21,
"purple": 0x22,
"blue": 0x23,
"cyan": 0x24,
"green": 0x25,
"yellow": 0x26,
}).get(color)
def resolve_effect(fx):
if type(fx) is int:
return fx
FX = {
"jitter": 0x00,
"wavy": 0x01,
"noise": 0x02,
"fadednoise": 0x03, # 1 arg
"unknown": 0x04,
"fadedjitter": 0x05, # 1 arg
"rainbow": 0x06,
"faded": 0x07, # 1 arg
"wavyb": 0x08,
"rainbowb": 0x09,
"shrinking": 0x0a,
"growing": 0x0b,
"sizejitter": 0x0c,
"sizewave": 0x0d,
"dropshadow": 0x0e,
}
return FX.get(fx)
CHARSET = { CHARSET = {
"𝅘𝅥𝅮": 0x00, #"𝅘𝅥𝅮": 0x00,
"!": 0x01, "!": 0x01,
'"': 0x02, '"': 0x02,
"#": 0x03, "#": 0x03,
@ -247,29 +280,29 @@ CHARSET = {
"¡": 0x8D, "¡": 0x8D,
"¿": 0x8E, "¿": 0x8E,
"ª": 0x8F, "ª": 0x8F,
"": 0x90, # "♥": 0x90,
"": 0x91, # "★": 0x91,
"": 0x92, # "↑": 0x92,
"": 0x93, # "↓": 0x93,
"": 0x94, # "←": 0x94,
"": 0x95, # "→": 0x95,
"": 0x96, # "●": 0x96,
"": 0x97, # "✖": 0x97,
"": 0xA2, "": 0xA2,
"": 0xA3, "": 0xA3,
"": 0xA4, "": 0xA4,
"": 0xA5, "": 0xA5,
" ": 0xF7, " ": 0xF7,
"": [0xFF, 0x24, 0xFF, 0x05, 0x10, 0x98, 0xFF, 0x25], # "Ⓐ": [0xFF, 0x24, 0xFF, 0x05, 0x10, 0x98, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x11, 0x99, 0xFF, 0x25], # "Ⓑ": [0xFF, 0x24, 0xFF, 0x05, 0x11, 0x99, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x12, 0xA1, 0xFF, 0x25], # "Ⓢ": [0xFF, 0x24, 0xFF, 0x05, 0x12, 0xA1, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9D, 0xFF, 0x25], # "▲": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9D, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9E, 0xFF, 0x25], # "▼": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9E, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9F, 0xFF, 0x25], # "◀": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9F, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0xA0, 0xFF, 0x25], # "▶": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0xA0, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9A, 0xFF, 0x25], # "Ⓛ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9A, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9B, 0xFF, 0x25], # "Ⓡ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9B, 0xFF, 0x25],
"": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9C, 0xFF, 0x25], # "Ⓩ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9C, 0xFF, 0x25],
} }
CHARSET_CREDITS = { CHARSET_CREDITS = {
@ -317,6 +350,19 @@ CHARSET_CREDITS = {
" ": 0xF7, " ": 0xF7,
} }
def strip_c_comments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return " "
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
if __name__ == "__main__": if __name__ == "__main__":
if len(argv) < 3: if len(argv) < 3:
print("usage: parse_compile.py [in.msg] [out.msgpack]") print("usage: parse_compile.py [in.msg] [out.msgpack]")
@ -328,88 +374,144 @@ if __name__ == "__main__":
message = None message = None
with open(filename, "r") as f: with open(filename, "r") as f:
source = f.read() source = strip_c_comments(f.read())
lineno = 1 lineno = 1
directive = ""
indent_level = 0
charset = CHARSET charset = CHARSET
font_stack = [0] font_stack = [0]
sound_stack = [0] sound_stack = [0]
color_stack = [0x0A] color_stack = [0x0A]
style = None
explicit_end = False
while len(source) > 0: while len(source) > 0:
if source.startswith("\n"): if source[0] == "\r":
lineno += 1
source = source[1:] source = source[1:]
continue continue
if message is None: if source[0] == "\n":
if source.startswith("//"): lineno += 1
while source[0] != "\n": source = source[1:]
source = source[1:]
else:
command, positional_args, named_args, source = parse_command(source)
if not command: for i in range(indent_level):
print(f"{filename}:{lineno}: expected [message]") if source[0] == " " or source[0] == "\t":
source = source[1:]
else:
break
continue
if message is None:
directive = ""
while source[0] != " ":
if source[0] == "\n":
lineno += 1
elif source[0] == "\r":
pass
else:
directive += source[0]
source = source[1:]
directive = directive.split(":")
if directive[0] != "#message" or len(directive) != 3:
print(f"{filename}:{lineno}: expected #message:SECTION:INDEX directive")
exit(1)
section = int(directive[1], 16)
if directive[2].startswith("(") and directive[2].endswith(")"):
name = directive[2][1:-1]
index = None
else:
name = None
index = int(directive[2], 16)
directive = ""
message = Message(name, section, index)
messages.append(message)
while source[0] != "{":
source = source[1:]
if source[0] == "\n":
lineno += 1
elif source[0] == "\r":
pass
elif source[0] == "{":
break
elif source[0] != " " and source[0] != "\t":
print(f"{filename}:{lineno}: expected opening brace ('{{')")
exit(1) exit(1)
name = positional_args[0] if len(positional_args) > 0 else None source = source[1:] # {
message = Message(name, named_args.get("section"), named_args.get("index"))
messages.append(message) # count indent level
indent_level = 0
while source[0] == " " or source[0] == "\t" or source[0] == "\n" or source[0] == "\r":
if source[0] == " " or source[0] == "\t":
indent_level += 1
source = source[1:]
# TODO: lookahead at all lines until "}" to determine lowest indent value
if indent_level == 4:
break
else: else:
command, positional_args, named_args, source = parse_command(source) command, args, source = parse_command(source)
if command: if command:
if command == "/message": if command == "end":
message.bytes += [0xFD] message.bytes += [0xFD]
explicit_end = True
# padding
while len(message.bytes) % 4 != 0:
message.bytes += [0x00]
message = None
elif command == "raw": elif command == "raw":
message.bytes += [*positional_args] message.bytes += [*args]
elif command == "func": #elif command == "func":
message.bytes += [0xFF, *positional_args] # message.bytes += [0xFF, *args]
elif command == "br": elif command == "br":
message.bytes += [0xF0] message.bytes += [0xF0]
elif command == "prompt": elif command == "wait":
message.bytes += [0xF1] message.bytes += [0xF1]
elif command == "sleep": elif command == "pause":
if len(positional_args) == 0: if len(args) != 1:
print(f"{filename}:{lineno}: {command} command requires a positional parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xF2, positional_args[0]] message.bytes += [0xF2, args[0]]
elif command == "next": elif command == "next":
message.bytes += [0xFB] message.bytes += [0xFB]
elif command == "func_04":
message.bytes += [0xFF, 0x04]
elif command == "pushcolor":
message.bytes += [0xFF, 0x24]
elif command == "popcolor":
message.bytes += [0xFF, 0x25]
elif command == "color": elif command == "color":
if "color" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: color command requires a 'color' parameter") print(f"{filename}:{lineno}: color command requires 1 parameter")
exit(1) exit(1)
color = color_to_code(**named_args) color = color_to_code(args[0], style)
if color is None: if color is None:
print(f"{filename}:{lineno}: unknown color combination {named_args}") print(f"{filename}:{lineno}: unknown color")
exit(1) exit(1)
message.bytes += [0xFF, 0x05, color] message.bytes += [0xFF, 0x05, color]
color_stack.append(color) color_stack.append(color)
elif command == "/color": #elif command == "/color":
color_stack.pop() # color_stack.pop()
message.bytes += [0xFF, 0x05, color_stack[0]] # message.bytes += [0xFF, 0x05, color_stack[0]]
elif command == "style": elif command == "style":
if "style" not in named_args:
print(f"{filename}:{lineno}: style command requires a 'style' parameter")
exit(1)
message.bytes += [0xFC] message.bytes += [0xFC]
style = named_args["style"] style = args[0]
args = args[1:]
if type(style) is int: if type(style) is int:
message.bytes += [style, *positional_args] message.bytes += [style, *args]
else: else:
if style == "right": if style == "right":
message.bytes += [0x01] message.bytes += [0x01]
@ -420,37 +522,45 @@ if __name__ == "__main__":
elif style == "tattle": elif style == "tattle":
message.bytes += [0x04] message.bytes += [0x04]
elif style == "choice": elif style == "choice":
if "w" not in named_args or "h" not in named_args or "x" not in named_args or "y" not in named_args: if len(args) != 4:
print(f"{filename}:{lineno}: 'choice' style requires parameters: x, y, w, h") print(f"{filename}:{lineno}: 'choice' style requires 4 parameters")
exit(1) exit(1)
message.bytes += [0x05, named_args["x"], named_args["y"], named_args["w"], named_args["h"]] message.bytes += [0x05, *args]
elif style == "inspect": elif style == "inspect":
message.bytes += [0x06] message.bytes += [0x06]
elif style == "sign": elif style == "sign":
message.bytes += [0x07] message.bytes += [0x07]
elif style == "lamppost": elif style == "lamppost":
message.bytes += [0x08] if len(args) != 1:
print(f"{filename}:{lineno}: 'lamppost' style requires 1 parameter")
exit(1)
message.bytes += [0x08, args[0]]
elif style == "postcard": elif style == "postcard":
message.bytes += [0x09] if len(args) != 1:
print(f"{filename}:{lineno}: 'lamppost' style requires 1 parameter")
exit(1)
message.bytes += [0x09, args[0]]
elif style == "popup": elif style == "popup":
message.bytes += [0x0A] message.bytes += [0x0A]
elif style == "upgrade": elif style == "upgrade":
if "w" not in named_args or "h" not in named_args or "x" not in named_args or "y" not in named_args: if len(args) != 4:
print(f"{filename}:{lineno}: 'upgrade' style requires parameters: x, y, w, h") print(f"{filename}:{lineno}: 'upgrade' style requires 4 parameters")
exit(1) exit(1)
message.bytes += [0x0C, named_args["w"], named_args["x"], named_args["h"], named_args["y"]] message.bytes += [0x0C, *args]
elif style == "narrate": elif style == "narrate":
message.bytes += [0x0D] message.bytes += [0x0D]
elif style == "epilogue": elif style == "epilogue":
message.bytes += [0x0E] message.bytes += [0x0E]
elif command == "font": elif command == "font":
if "font" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: font command requires a 'font' parameter") print(f"{filename}:{lineno}: font command requires 1 parameter")
exit(1) exit(1)
font = named_args["font"] font = args[0]
if font == "normal": if font == "normal":
font = 0 font = 0
@ -470,203 +580,213 @@ if __name__ == "__main__":
charset = CHARSET_CREDITS charset = CHARSET_CREDITS
else: else:
charset = CHARSET charset = CHARSET
elif command == "/font": # elif command == "/font":
font_stack.pop() # font_stack.pop()
message.bytes += [0xFF, 0x00, font_stack[0]] # message.bytes += [0xFF, 0x00, font_stack[0]]
if font == 3 or font == 4: # if font == 3 or font == 4:
charset = CHARSET_CREDITS # charset = CHARSET_CREDITS
else: # else:
charset = CHARSET # charset = CHARSET
elif command == "noskip": elif command == "inputoff":
message.bytes += [0xFF, 0x07] message.bytes += [0xFF, 0x07]
elif command == "/noskip": elif command == "inputon":
message.bytes += [0xFF, 0x08] message.bytes += [0xFF, 0x08]
elif command == "instant": elif command == "delayoff":
message.bytes += [0xFF, 0x09] message.bytes += [0xFF, 0x09]
elif command == "/instant": elif command == "delayon":
message.bytes += [0xFF, 0x0A] message.bytes += [0xFF, 0x0A]
elif command == "kerning": elif command == "kerning":
if "kerning" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: kerning command requires a 'kerning' parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x0B, named_args["kerning"]] message.bytes += [0xFF, 0x0B, args[0]]
elif command == "scroll": elif command == "scroll":
if len(positional_args) == 0: if len(args) != 1:
print(f"{filename}:{lineno}: scroll command requires a positional parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x0C, positional_args[0]] message.bytes += [0xFF, 0x0C, args[0]]
elif command == "size": elif command == "size":
if "x" not in named_args or "y" not in named_args: if len(args) != 2:
print(f"{filename}:{lineno}: size command requires parameters: x, y") print(f"{filename}:{lineno}: {command} command requires 2 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x0D, named_args["x"], named_args["y"]] message.bytes += [0xFF, 0x0D, *args]
elif command == "/size": elif command == "sizereset":
message.bytes += [0xFF, 0x0E] message.bytes += [0xFF, 0x0E]
elif command == "speed": elif command == "speed":
if "delay" not in named_args or "chars" not in named_args: if len(args) != 2:
print(f"{filename}:{lineno}: speed command requires parameters: delay, chars") print(f"{filename}:{lineno}: {command} command requires 2 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x0F, named_args["delay"], named_args["chars"]] message.bytes += [0xFF, 0x0F, *args]
elif command == "pos": # elif command == "pos":
if "y" not in named_args: # if "y" not in named_args:
print(f"{filename}:{lineno}: pos command requires parameter: y (x is optional)") # print(f"{filename}:{lineno}: pos command requires parameter: y (x is optional)")
# exit(1)
# if "x" in named_args:
# message.bytes += [0xFF, 0x10, named_args["x"], named_args["y"]]
# else:
# message.bytes += [0xFF, 0x11, named_args["y"]]
elif command == "setprintpos":
if len(args) != 2:
print(f"{filename}:{lineno}: {command} command requires 2 parameters")
exit(1) exit(1)
if "x" in named_args: message.bytes += [0xFF, 0x10, *args]
message.bytes += [0xFF, 0x10, named_args["x"], named_args["y"]] elif command == "setprinty":
else: if len(args) != 1:
message.bytes += [0xFF, 0x11, named_args["y"]] print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1)
message.bytes += [0xFF, 0x11, *args]
elif command == "indent": elif command == "indent":
if len(positional_args) == 0: if len(args) != 1:
print(f"{filename}:{lineno}: indent command requires a positional parameter") print(f"{filename}:{lineno}: indent command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x12, positional_args[0]] message.bytes += [0xFF, 0x12, args[0]]
elif command == "down": # elif command == "image":
if len(positional_args) == 0: # if len(args) == 1:
print(f"{filename}:{lineno}: down command requires a positional parameter") # message.bytes += [0xFF, 0x15, args[0]]
# elif len(args) == 7:
# message.bytes += [0xFF, 0x18, *args]
# else:
# print(f"{filename}:{lineno}: image command requires 1 or 7 parameters")
# exit(1)
elif command == "image1":
if len(args) != 1:
print(f"{filename}:{lineno}: {command} command requires 1 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x13, positional_args[0]] message.bytes += [0xFF, 0x15, *args]
elif command == "up": elif command == "image7":
if len(positional_args) == 0: if len(args) != 7:
print(f"{filename}:{lineno}: up command requires a positional parameter") print(f"{filename}:{lineno}: {command} command requires 7 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x14, positional_args[0]] message.bytes += [0xFF, 0x18, *args]
elif command == "image":
if len(positional_args) == 1:
message.bytes += [0xFF, 0x15, positional_args[0]]
elif len(positional_args) == 7:
message.bytes += [0xFF, 0x18, *positional_args]
else:
print(f"{filename}:{lineno}: image command requires 1 or 7 positional parameters")
exit(1)
elif command == "sprite": elif command == "sprite":
if len(positional_args) != 3: if len(args) != 3:
print(f"{filename}:{lineno}: sprite command requires 3 positional parameters") print(f"{filename}:{lineno}: sprite command requires 3 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x16, *positional_args] message.bytes += [0xFF, 0x16, *args]
elif command == "item": elif command == "item":
if len(positional_args) != 2: if len(args) != 2:
print(f"{filename}:{lineno}: item command requires 2 positional parameters") print(f"{filename}:{lineno}: item command requires 2 parameters")
exit(1) exit(1)
message.bytes += [0xFF, 0x17, *positional_args] message.bytes += [0xFF, 0x17, *args]
elif command == "cursor": elif command == "cursor":
if len(positional_args) != 1: if len(args) != 1:
print(f"{filename}:{lineno}: cursor command requires 1 positional parameter") print(f"{filename}:{lineno}: cursor command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x1E, *positional_args] message.bytes += [0xFF, 0x1E, *args]
elif command == "option": elif command == "option":
if len(positional_args) != 1: if len(args) != 1:
print(f"{filename}:{lineno}: option command requires 1 positional parameter") print(f"{filename}:{lineno}: option command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x21, *positional_args] message.bytes += [0xFF, 0x21, *args]
elif command == "choice": elif command == "endchoice":
if len(positional_args) != 1: if len(args) != 1:
print(f"{filename}:{lineno}: choice command requires 1 positional parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x1E, positional_args[0], 0xFF, 0x21, positional_args[0]] message.bytes += [0xFF, 0x1F, args[0]]
elif command == "choicecount": elif command == "setcancel":
if "choicecount" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: choicecount command requires a 'choicecount' parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x1F, named_args["choicecount"]] message.bytes += [0xFF, 0x20, args[0]]
elif command == "cancel": elif command == "startfx":
if "cancel" not in named_args: message.bytes += [0xFF, 0x26, resolve_effect(args[0]), *args[1:]]
print(f"{filename}:{lineno}: cancel command requires a 'cancel' parameter") elif command == "endfx":
exit(1) message.bytes += [0xFF, 0x27, resolve_effect(args[0]), *args[1:]]
# elif command == "shaky":
message.bytes += [0xFF, 0x20, named_args["cancel"]] # message.bytes += [0xFF, 0x26, 0x00]
elif command == "shaky": # elif command == "/shaky":
message.bytes += [0xFF, 0x26, 0x00] # message.bytes += [0xFF, 0x27, 0x00]
elif command == "/shaky": # elif command == "wavy":
message.bytes += [0xFF, 0x27, 0x00] # message.bytes += [0xFF, 0x26, 0x01]
elif command == "wavy": # elif command == "/wavy":
message.bytes += [0xFF, 0x26, 0x01] # message.bytes += [0xFF, 0x27, 0x01]
elif command == "/wavy": # elif command == "shaky":
message.bytes += [0xFF, 0x27, 0x01] # if "opacity" in named_args:
elif command == "shaky": # print(f"{filename}:{lineno}: shaky command doesn't accept parameter 'fade' (hint: did you mean 'faded-shaky'?)")
if "opacity" in named_args: # exit(1)
print(f"{filename}:{lineno}: shaky command doesn't accept parameter 'fade' (hint: did you mean 'faded-shaky'?)") # message.bytes += [0xFF, 0x26, 0x00]
exit(1) # elif command == "/shaky":
message.bytes += [0xFF, 0x26, 0x00] # message.bytes += [0xFF, 0x27, 0x00]
elif command == "/shaky": # elif command == "noise":
message.bytes += [0xFF, 0x27, 0x00] # message.bytes += [0xFF, 0x26, 0x03, named_args.get("fade", 3)]
elif command == "noise": # elif command == "/noise":
message.bytes += [0xFF, 0x26, 0x03, named_args.get("fade", 3)] # message.bytes += [0xFF, 0x27, 0x03]
elif command == "/noise": # elif command == "faded-shaky":
message.bytes += [0xFF, 0x27, 0x03] # message.bytes += [0xFF, 0x26, 0x05, named_args.get("fade", 5)]
elif command == "faded-shaky": # elif command == "/faded-shaky":
message.bytes += [0xFF, 0x26, 0x05, named_args.get("fade", 5)] # message.bytes += [0xFF, 0x27, 0x05]
elif command == "/faded-shaky": # elif command == "fade":
message.bytes += [0xFF, 0x27, 0x05] # message.bytes += [0xFF, 0x26, 0x07, named_args.get("fade", 7)]
elif command == "fade": # elif command == "/fade":
message.bytes += [0xFF, 0x26, 0x07, named_args.get("fade", 7)] # message.bytes += [0xFF, 0x27, 0x07]
elif command == "/fade": # elif command == "shout" or command == "shrinking":
message.bytes += [0xFF, 0x27, 0x07] # message.bytes += [0xFF, 0x26, 0x0A]
elif command == "shout" or command == "shrinking": # elif command == "/shout" or command == "/shrinking":
message.bytes += [0xFF, 0x26, 0x0A] # message.bytes += [0xFF, 0x27, 0x0A]
elif command == "/shout" or command == "/shrinking": # elif command == "whisper" or command == "growing":
message.bytes += [0xFF, 0x27, 0x0A] # message.bytes += [0xFF, 0x26, 0x0B]
elif command == "whisper" or command == "growing": # elif command == "/whisper" or command == "/growing":
message.bytes += [0xFF, 0x26, 0x0B] # message.bytes += [0xFF, 0x27, 0x0B]
elif command == "/whisper" or command == "/growing": # elif command == "scream" or command == "shaky-size":
message.bytes += [0xFF, 0x27, 0x0B] # message.bytes += [0xFF, 0x26, 0x0C]
elif command == "scream" or command == "shaky-size": # elif command == "/scream" or command == "/shaky-size":
message.bytes += [0xFF, 0x26, 0x0C] # message.bytes += [0xFF, 0x27, 0x0C]
elif command == "/scream" or command == "/shaky-size": # elif command == "chortle" or command == "wavy-size":
message.bytes += [0xFF, 0x27, 0x0C] # message.bytes += [0xFF, 0x26, 0x0D]
elif command == "chortle" or command == "wavy-size": # elif command == "/chortle" or command == "/wavy-size":
message.bytes += [0xFF, 0x26, 0x0D] # message.bytes += [0xFF, 0x27, 0x0D]
elif command == "/chortle" or command == "/wavy-size": # elif command == "shadow":
message.bytes += [0xFF, 0x27, 0x0D] # message.bytes += [0xFF, 0x26, 0x0E]
elif command == "shadow": # elif command == "/shadow":
message.bytes += [0xFF, 0x26, 0x0E] # message.bytes += [0xFF, 0x27, 0x0E]
elif command == "/shadow":
message.bytes += [0xFF, 0x27, 0x0E]
elif command == "var": elif command == "var":
if len(positional_args) != 1: if len(args) != 1:
print(f"{filename}:{lineno}: var command requires 1 positional parameter") print(f"{filename}:{lineno}: var command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x28, *positional_args] message.bytes += [0xFF, 0x28, *args]
elif command == "center": elif command == "func_29":
if len(positional_args) != 1: if len(args) != 1:
print(f"{filename}:{lineno}: center command requires 1 positional parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x29, *positional_args] message.bytes += [0xFF, 0x29, *args]
elif command == "volume": elif command == "volume":
if "volume" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: volume command requires a 'volume' parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
message.bytes += [0xFF, 0x2E, named_args["volume"]] message.bytes += [0xFF, 0x2E, *args]
elif command == "sound": elif command == "speechsound":
if "sound" not in named_args: if len(args) != 1:
print(f"{filename}:{lineno}: sound command requires a 'sound' parameter") print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1) exit(1)
sound = named_args["sound"] sound = args[0]
if sound == "normal": # if sound == "normal":
sound = 0 # sound = 0
elif sound == "bowser": # elif sound == "bowser":
sound = 1 # sound = 1
elif sound == "spirit": # elif sound == "star":
sound = 2 # sound = 2
if type(sound) is not int: if type(sound) is not int:
print(f"{filename}:{lineno}: unknown sound '{sound}'") print(f"{filename}:{lineno}: unknown sound '{sound}'")
@ -674,67 +794,131 @@ if __name__ == "__main__":
message.bytes += [0xFF, 0x2F, sound] message.bytes += [0xFF, 0x2F, sound]
sound_stack.append(sound) sound_stack.append(sound)
elif command == "/sound": # elif command == "/sound":
sound_stack.pop() # sound_stack.pop()
message.bytes += [0xFF, 0x2F, sound_stack[0]] # message.bytes += [0xFF, 0x2F, sound_stack[0]]
# elif command == "a":
# color_code = color_to_code(named_args.get("color", "blue"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x98, 0xFF, 0x25]
# elif command == "b":
# color_code = color_to_code(named_args.get("color", "green"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x99, 0xFF, 0x25]
# elif command == "l":
# color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9A, 0xFF, 0x25]
# elif command == "r":
# color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9B, 0xFF, 0x25]
# elif command == "z":
# color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9C, 0xFF, 0x25]
# elif command == "c-up":
# color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9D, 0xFF, 0x25]
# elif command == "c-down":
# color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9E, 0xFF, 0x25]
# elif command == "c-left":
# color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9F, 0xFF, 0x25]
# elif command == "c-right":
# color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA0, 0xFF, 0x25]
# elif command == "start":
# color_code = color_to_code(named_args.get("color", "red"), named_args.get("ctx", "button"))
# message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA1, 0xFF, 0x25]
elif command == "a": elif command == "a":
color_code = color_to_code(named_args.get("color", "blue"), named_args.get("ctx", "button")) message.bytes += [0x98]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x98, 0xFF, 0x25]
elif command == "b": elif command == "b":
color_code = color_to_code(named_args.get("color", "green"), named_args.get("ctx", "button")) message.bytes += [0x99]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x99, 0xFF, 0x25]
elif command == "l": elif command == "l":
color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button")) message.bytes += [0x9a]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9A, 0xFF, 0x25]
elif command == "r": elif command == "r":
color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button")) message.bytes += [0x9b]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9B, 0xFF, 0x25]
elif command == "z": elif command == "z":
color_code = color_to_code(named_args.get("color", "gray"), named_args.get("ctx", "button")) message.bytes += [0x9c]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9C, 0xFF, 0x25]
elif command == "c-up": elif command == "c-up":
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) message.bytes += [0x9d]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9D, 0xFF, 0x25]
elif command == "c-down": elif command == "c-down":
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) message.bytes += [0x9e]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9E, 0xFF, 0x25]
elif command == "c-left": elif command == "c-left":
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) message.bytes += [0x9f]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0x9F, 0xFF, 0x25]
elif command == "c-right": elif command == "c-right":
color_code = color_to_code(named_args.get("color", "yellow"), named_args.get("ctx", "button")) message.bytes += [0xa0]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA0, 0xFF, 0x25]
elif command == "start": elif command == "start":
color_code = color_to_code(named_args.get("color", "red"), named_args.get("ctx", "button")) message.bytes += [0xa1]
message.bytes += [0xFF, 0x24, 0xFF, 0x05, color_code, 0xA1, 0xFF, 0x25]
elif command == "note": elif command == "note":
message.bytes += [0x00] message.bytes += [0x00]
elif command == "heart": elif command == "heart":
message.bytes += [0x90] message.bytes += [0x90]
elif command == "star": elif command == "star":
message.bytes += [0x91] message.bytes += [0x91]
elif command == "arrow-up": elif command == "up":
message.bytes += [0x92] if len(args) == 1:
elif command == "arrow-down": message.bytes += [0xFF, 0x14, args[0]]
message.bytes += [0x93] else:
elif command == "arrow-left": message.bytes += [0x92]
elif command == "down":
if len(args) == 1:
message.bytes += [0xFF, 0x13, args[0]]
else:
message.bytes += [0x93]
elif command == "left":
message.bytes += [0x94] message.bytes += [0x94]
elif command == "arrow-right": elif command == "right":
message.bytes += [0x95] message.bytes += [0x95]
elif command == "circle": elif command == "circle":
message.bytes += [0x96] message.bytes += [0x96]
elif command == "cross": elif command == "cross":
message.bytes += [0x97] message.bytes += [0x97]
elif command == "wait": # elif command == "wait":
print(f"{filename}:{lineno}: unknown command 'wait' (hint: did you mean 'prompt'?)") # print(f"{filename}:{lineno}: unknown command 'wait' (hint: did you mean 'prompt'?)")
exit(1) # exit(1)
elif command == "pause": # elif command == "pause":
print(f"{filename}:{lineno}: unknown command 'pause' (hint: did you mean 'sleep'?)") # print(f"{filename}:{lineno}: unknown command 'pause' (hint: did you mean 'sleep'?)")
exit(1) # exit(1)
elif command == "func_1a":
if len(args) != 3:
print(f"{filename}:{lineno}: {command} command requires 3 parameters")
exit(1)
message.bytes += [0xFF, 0x1A, *args]
elif command == "func_1b":
if len(args) != 2:
print(f"{filename}:{lineno}: {command} command requires 2 parameters")
exit(1)
message.bytes += [0xFF, 0x1B, *args]
elif command == "func_1c":
if len(args) != 1:
print(f"{filename}:{lineno}: {command} command requires 1 parameter")
exit(1)
message.bytes += [0xFF, 0x1C, *args]
elif command == "startanim":
message.bytes += [0xFF, 0x22]
elif command == "endanim":
message.bytes += [0xFF, 0x23]
elif command == "func_2b":
message.bytes += [0xFF, 0x2b]
else: else:
print(f"{filename}:{lineno}: unknown command '{command}'") print(f"{filename}:{lineno}: unknown command '{command}'")
exit(1) exit(1)
else: else:
if source[0] == "}":
if not explicit_end:
message.bytes += [0xFD]
explicit_end = False
# padding
while len(message.bytes) % 4 != 0:
message.bytes += [0x00]
message = None
source = source[1:] # }
indent_level = 0
continue
if source[0] == "\\": if source[0] == "\\":
source = source[1:] source = source[1:]

View File

@ -9432,55 +9432,55 @@ segments:
- start: 0x1B83000 - start: 0x1B83000
type: PaperMarioMessages type: PaperMarioMessages
files: files:
- intro - 00_introduction
- end/postgame - 01_postgame_celebration
- toad_town/gate - 02_toad_town_gate_sector
- toad_town/castle - 03_toad_town_castle_sector
- toad_town/bridge - 04_toad_town_bridge_sector
- toad_town/train - 05_toad_town_train_sector
- toad_town/warehouse - 06_toad_town_warehouse_sector
- toad_town/docks - 07_toad_town_docks_sector
- toad_town/minigames - 08_minigames
- castle_grounds - 09_castle_grounds
- shooting_star_summit - 0A_shooting_star_summit
- prologue - 0B_prologue
- chapter1 - 0C_chapter_1
- chapter2 - 0D_chapter_2
- chapter3 - 0E_chapter_3
- chapter4 - 0F_chapter_4
- chapter5 - 10_chapter_5
- chapter6 - 11_chapter_6
- chapter7 - 12_chapter_7
- chapter8 - 13_chapter_8
- peach_interludes - 14_peach_segments
- koopa_koot_quests - 15_koopa_koot_favors
- advice/russ_t - 16_russ_t_advice
- toad_town/bulletin_news - 17_news_bulletin
- toad_town/bulletin_gossip - 18_gossip_bulletin
- world/map_tattles - 19_map_tattles
- world/npc_tattles - 1A_npc_tattles
- world/entity_tattles - 1B_entity_tattles
- battle/enemy_tattles - 1C_enemy_tattles
- ui/misc - 1D_menus
- ui/choices - 1E_choices
- ui/pause - 1F_pause
- diary_letters - 20_party_letters_luigi_diary
- advice/merlon - 21_advice_fortunes
- advice/merluvlee - 22_treasure_fortunes
- item/descriptions_23 # TODO: difference between 23,24,25 - 23_item_descriptions # TODO: difference between 23,24,25
- item/descriptions_24 - 24_item_descriptions
- item/descriptions_25 - 25_item_descriptions
- item/names - 26_item_names
- shops - 27_shop_messages
- partner_descriptions - 28_partner_descriptions
- battle/enemy_names - 29_enemy_names
- battle/mario_moves - 2A_mario_moves
- battle/partner_moves - 2B_partner_moves
- quiz/questions - 2C_quiz_questions
- quiz/options - 2D_quiz_options
- end/credits - 2E_credits
- [0x1C84D30, bin] - [0x1C84D30, bin] # junk(?)
- [0x1E00000, bin] # sprites here; Star Rod considers this a copy of sprite/npc.bin, but that doesnt appear to be true - [0x1E00000, bin] # junk (player sprite data; can be zeroed out with no effect)
- [0x1E40000, PaperMarioMapFS] - [0x1E40000, PaperMarioMapFS]
- [0x27FEE22, bin] - [0x27FEE22, bin]
- [0x2800000] - [0x2800000]

View File

@ -1,8 +1,9 @@
from segtypes.n64.segment import N64Segment from segtypes.n64.segment import N64Segment
from pathlib import Path from pathlib import Path
import re
CHARSET = { CHARSET = {
0x00: "𝅘𝅥𝅮", 0x00: "[note]",
0x01: "!", 0x01: "!",
0x02: '"', 0x02: '"',
0x03: "#", 0x03: "#",
@ -146,142 +147,163 @@ CHARSET = {
0x8D: "¡", 0x8D: "¡",
0x8E: "¿", 0x8E: "¿",
0x8F: "ª", 0x8F: "ª",
0x90: "", 0x90: "[heart]",
0x91: "", 0x91: "[star]",
0x92: "", 0x92: "[up]",
0x93: "", 0x93: "[down]",
0x94: "", 0x94: "[left]",
0x95: "", 0x95: "[right]",
0x96: "", 0x96: "[circle]",
0x97: "", 0x97: "[cross]",
0x98: "[a]",
0x99: "[b]",
0x9A: "[l]",
0x9B: "[r]",
0x9C: "[z]",
0x9D: "[c-up]",
0x9E: "[c-down]",
0x9F: "[c-left]",
0xA0: "[c-right]",
0xA1: "[start]",
0xA2: "", 0xA2: "",
0xA3: "", 0xA3: "",
0xA4: "", 0xA4: "",
0xA5: "", 0xA5: "",
0xF7: " ", 0xF7: " ",
0xF0: "[br]\n", 0xF0: "[br]\n",
0xF1: "[prompt]", 0xF1: "[wait]",
0xF2: {None: lambda d: (f"[sleep {d[0]}]", 1)}, 0xF2: {None: lambda d: (f"[pause:{d[0]}]", 1)},
0xFB: "[next]\n", 0xFB: "[next]\n",
0xFC: { 0xFC: {
0x01: "[style=right]\n", 0x01: "[style:right]\n",
0x02: "[style=left]\n", 0x02: "[style:left]\n",
0x03: "[style=center]\n", 0x03: "[style:center]\n",
0x04: "[style=tattle]\n", 0x04: "[style:tattle]\n",
0x05: {None: lambda d: (f"[style=choice x={d[0]} y={d[1]} w={d[2]} h={d[3]}]\n", 4)}, 0x05: {None: lambda d: (f"[style:choice:{d[0]}:{d[1]}:{d[2]}:{d[3]}]\n", 4)},
0x06: "[style=inspect]\n", 0x06: "[style:inspect]\n",
0x07: "[style=sign]\n", 0x07: "[style:sign]\n",
0x08: "[style=lamppost]\n", 0x08: {None: lambda d: (f"[style:lamppost:{d[0]}]\n", 1)},
0x09: "[style=postcard]\n", 0x09: {None: lambda d: (f"[style:postcard:{d[0]}]\n", 1)},
0x0A: "[style=popup]\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)}, 0x0C: {None: lambda d: (f"[style:upgrade:{d[0]}:{d[1]}:{d[2]}:{d[3]}]\n", 4)},
0x0D: "[style=narrate]\n", 0x0D: "[style:narrate]\n",
0x0E: "[style=epilogue]\n", 0x0E: "[style:epilogue]\n",
}, },
0xFF: { 0xFF: {
0x00: { 0x00: {
0: "[font=normal]", 0: "[font:normal]",
3: "[font=title]\n", 3: "[font:title]\n",
4: "[font=subtitle]\n", 4: "[font:subtitle]\n",
}, },
0x04: "[func_04]",
0x05: { 0x05: {
0x0A: "[color=normal]", # 0x0A: "[color:normal]",
0x20: "[color=red]", # 0x20: "[color:red]",
0x21: "[color=pink]", # 0x21: "[color:pink]",
0x22: "[color=purple]", # 0x22: "[color:purple]",
0x23: "[color=blue]", # 0x23: "[color:blue]",
0x24: "[color=cyan]", # 0x24: "[color:cyan]",
0x25: "[color=green]", # 0x25: "[color:green]",
0x26: "[color=yellow]", # 0x26: "[color:yellow]",
0x00: "[color=normal ctx=diary]", # 0x00: "[color=normal ctx=diary]",
0x07: "[color=red ctx=diary]", # 0x07: "[color=red ctx=diary]",
0x17: "[color=dark ctx=inspect]", # 0x17: "[color=dark ctx=inspect]",
0x18: "[color=normal ctx=sign]", # 0x18: "[color=normal ctx=sign]",
0x19: "[color=red ctx=sign]", # 0x19: "[color=red ctx=sign]",
0x1A: "[color=blue ctx=sign]", # 0x1A: "[color=blue ctx=sign]",
0x1B: "[color=green ctx=sign]", # 0x1B: "[color=green ctx=sign]",
0x28: "[color=red ctx=popup]", # 0x28: "[color=red ctx=popup]",
0x29: "[color=pink ctx=popup]", # 0x29: "[color=pink ctx=popup]",
0x2A: "[color=purple ctx=popup]", # 0x2A: "[color=purple ctx=popup]",
0x2B: "[color=blue ctx=popup]", # 0x2B: "[color=blue ctx=popup]",
0x2C: "[color=teal ctx=popup]", # 0x2C: "[color=teal ctx=popup]",
0x2D: "[color=green ctx=popup]", # 0x2D: "[color=green ctx=popup]",
0x2E: "[color=yellow ctx=popup]", # 0x2E: "[color=yellow ctx=popup]",
0x2F: "[color=normal ctx=popup]", # 0x2F: "[color=normal ctx=popup]",
None: lambda d: (f"[color:0x{d[0]:X}]", 1),
}, },
0x07: "[noskip]\n", 0x07: "[inputOff]\n",
0x08: "[/noskip]\n", 0x08: "[inputOn]\n",
0x09: "[instant]\n", 0x09: "[delayOff]\n",
0x0A: "[/instant]\n", 0x0A: "[delayOn]\n",
0x0B: {None: lambda d: (f"[kerning={d[0]}]", 1)}, 0x0B: {None: lambda d: (f"[kerning:{d[0]}]", 1)},
0x0C: {None: lambda d: (f"[scroll {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)}, 0x0D: {None: lambda d: (f"[size:{d[0]}:{d[0]}]\n", 2)},
0x0E: "[/size]\n", 0x0E: "[sizeReset]\n",
0x0F: {None: lambda d: (f"[speed delay={d[0]} chars={d[1]}]", 2)}, 0x0F: {None: lambda d: (f"[speed:{d[0]}:{d[1]}]", 2)},
0x10: {None: lambda d: (f"[pos x={d[0]} y={d[1]}]", 2)}, 0x10: {None: lambda d: (f"[setPrintPos:{d[0]}:{d[1]}]", 2)},
0x11: {None: lambda d: (f"[pos y={d[0]}]", 1)}, 0x11: {None: lambda d: (f"[setPrintY:{d[0]}]", 1)},
0x12: {None: lambda d: (f"[indent {d[0]}]", 1)}, 0x12: {None: lambda d: (f"[indent:{d[0]}]", 1)},
0x13: {None: lambda d: (f"[down {d[0]}]", 1)}, 0x13: {None: lambda d: (f"[down:{d[0]}]", 1)},
0x14: {None: lambda d: (f"[up {d[0]}]", 1)}, 0x14: {None: lambda d: (f"[up:{d[0]}]", 1)},
0x15: {None: lambda d: (f"[image {d[0]}]\n", 1)}, 0x15: {None: lambda d: (f"[image1:{d[0]}]\n", 1)},
0x16: {None: lambda d: (f"[sprite {d[0]} {d[1]} {d[2]}]\n", 3)}, 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)}, 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)}, 0x18: {None: lambda d: (f"[image7:{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)}, 0x1A: {None: lambda d: (f"[func_1A:{d[0]}:{d[1]}:{d[2]}]", 3)},
0x1F: {None: lambda d: (f"[choicecount={d[0]}]", 1)}, 0x1B: {None: lambda d: (f"[func_1B:{d[0]}:{d[1]}]", 2)},
0x20: {None: lambda d: (f"[cancel={d[0]}]", 1)}, 0x1C: {None: lambda d: (f"[func_1C:{d[0]}]", 1)},
0x21: {None: lambda d: (f"[option {d[0]}]", 1)}, 0x1E: {None: lambda d: (f"[cursor:{d[0]}]", 1)},
0x24: {0xFF: {0x05: { 0x1F: {None: lambda d: (f"[endChoice:{d[0]}]", 1)},
0x10: {0x98: {0xFF: {0x25: ""}}}, 0x20: {None: lambda d: (f"[setCancel:{d[0]}]", 1)},
0x11: {0x99: {0xFF: {0x25: ""}}}, 0x21: {None: lambda d: (f"[option:{d[0]}]", 1)},
0x12: {0xA1: {0xFF: {0x25: ""}}}, 0x22: "[startAnim]",
0x13: { 0x23: "[endAnim]",
0x9D: {0xFF: {0x25: ""}}, # 0x24: {0xFF: {0x05: {
0x9E: {0xFF: {0x25: ""}}, # 0x10: {0x98: {0xFF: {0x25: "Ⓐ"}}},
0x9F: {0xFF: {0x25: ""}}, # 0x11: {0x99: {0xFF: {0x25: "Ⓑ"}}},
0xA0: {0xFF: {0x25: ""}}, # 0x12: {0xA1: {0xFF: {0x25: "Ⓢ"}}},
}, # 0x13: {
0x14: {0x9C: {0xFF: {0x25: ""}}}, # 0x9D: {0xFF: {0x25: "▲"}},
}}}, # 0x9E: {0xFF: {0x25: "▼"}},
# 0x9F: {0xFF: {0x25: "◀"}},
# 0xA0: {0xFF: {0x25: "▶"}},
# },
# 0x14: {0x9C: {0xFF: {0x25: "Ⓩ"}}},
# }}},
0x24: "[pushColor]",
0x25: "[popColor]",
0x26: { 0x26: {
0x00: "[shaky]", 0x00: "[startFX:jitter]",
0x01: "[wavy]", 0x01: "[startFX:wavy]",
0x03: {None: lambda d: (f"[noise fade={d[0]}]", 1)}, 0x03: {None: lambda d: (f"[startFX:fadedNoise:{d[0]}]", 1)},
0x05: {None: lambda d: (f"[faded-shaky fade={d[0]}]", 1)}, 0x05: {None: lambda d: (f"[startFX:fadedJitter:{d[0]}]", 1)},
0x07: {None: lambda d: (f"[fade={d[0]}]", 1)}, 0x07: {None: lambda d: (f"[startFX:faded:{d[0]}]", 1)},
0x0A: "[shout]", 0x0A: "[startFX:shrinking]",
0x0B: "[whisper]", 0x0B: "[startFX:growing]",
0x0C: "[scream]", 0x0C: "[startFX:sizeJitter]",
0x0D: "[chortle]", 0x0D: "[startFX:sizeWave]",
0x0E: "[shadow]", 0x0E: "[startFX:dropShadow]",
}, },
0x27: { 0x27: {
0x00: "[/shaky]", 0x00: "[endFX:jitter]",
0x01: "[/wavy]", 0x01: "[endFX:wavy]",
0x03: "[/noise]", 0x03: "[endFX:fadedNoise]",
0x05: "[/faded-shaky]", 0x05: "[endFX:fadedJitter]",
0x07: "[/fade]", 0x07: "[endFX:faded]",
0x0A: "[/shout]", 0x0A: "[endFX:shrinking]",
0x0B: "[/whisper]", 0x0B: "[endFX:growing]",
0x0C: "[/scream]", 0x0C: "[endFX:sizeJitter]",
0x0D: "[/chortle]", 0x0D: "[endFX:sizeWave]",
0x0E: "[/shadow]", 0x0E: "[endFX:dropShadow]",
}, },
0x28: {None: lambda d: (f"[var {d[0]}]", 1)}, 0x28: {None: lambda d: (f"[var:{d[0]}]", 1)},
0x29: {None: lambda d: (f"[center {d[0]}]", 1)}, 0x29: {None: lambda d: (f"[func_29:{d[0]}]", 1)},
0x2E: {None: lambda d: (f"[volume={d[0]}]", 1)}, 0x2B: "[func_2B]",
0x2E: {None: lambda d: (f"[volume:{d[0]}]", 1)},
0x2F: { 0x2F: {
1: "[sound=bowser]\n", #1: "[speechSound:bowser]\n",
2: "[sound=spirit]\n", #2: "[speechSound:star]\n",
None: lambda d: (f"[sound={d[0]}]\n", 1), None: lambda d: (f"[speechSound:{d[0]}]\n", 1),
}, },
None: lambda d: (f"[func 0x{d[0]:X}]", 1), #None: lambda d: (f"[func_{d[0]:02X}]", 1),
}, },
None: lambda d: (f"[raw 0x{d[0]:02X}]", 1), None: lambda d: (f"[raw:0x{d[0]:02X}]", 1),
} }
CHARSET_CREDITS = { CHARSET_CREDITS = {
@ -349,6 +371,12 @@ class N64SegPaperMarioMessages(N64Segment):
section_offsets.append(offset) section_offsets.append(offset)
pos += 4 pos += 4
msg_dir = Path(base_path, self.options["assets_dir"], self.name)
msg_dir.mkdir(parents=True, exist_ok=True)
# delete existing files
self.delete_dir_childs(msg_dir)
for i, section_offset in enumerate(section_offsets): for i, section_offset in enumerate(section_offsets):
name = f"{i:02X}" name = f"{i:02X}"
if len(self.files) >= i: if len(self.files) >= i:
@ -367,15 +395,24 @@ class N64SegPaperMarioMessages(N64Segment):
self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})") self.log(f"Reading {len(msg_offsets)} messages in section {name} (0x{i:02X})")
path = Path(base_path, self.options["assets_dir"], self.name, name + ".msg") path = msg_dir / Path(name + ".msg")
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w") as self.f: with open(path, "w") as self.f:
for j, msg_offset in enumerate(msg_offsets): for j, msg_offset in enumerate(msg_offsets):
if j != 0: if j != 0:
self.f.write("\n") self.f.write("\n")
self.f.write(f"[message section=0x{i:02X} index={j}]\n") self.f.write(f"#message:{i:02X}:{j:03X} {{\n ")
self.write_message_markup(data[msg_offset:]) self.write_message_markup(data[msg_offset:])
self.f.write("\n[/message]\n") self.f.write("\n}\n")
@staticmethod
def delete_dir_childs(path):
for f in path.iterdir():
if f.is_dir():
N64SegPaperMarioMessages.delete_dir_childs(f)
f.rmdir()
else:
f.unlink()
def get_ld_files(self): def get_ld_files(self):
return [(self.options["assets_dir"], self.name, ".data", self.rom_start)] return [(self.options["assets_dir"], self.name, ".data", self.rom_start)]
@ -420,9 +457,9 @@ class N64SegPaperMarioMessages(N64Segment):
raise ValueError(value) raise ValueError(value)
def write_markup(self, markup): def write_markup(self, markup):
self.f.write(markup) self.f.write(re.sub("\n", "\n ", markup))
if markup == "[font=title]\n" or markup == "[font=subtitle]\n": if markup == "[font:title]\n" or markup == "[font:subtitle]\n":
self.root_charset = CHARSET_CREDITS self.root_charset = CHARSET_CREDITS
elif markup == "[font=normal]": elif markup == "[font:normal]":
self.root_charset = CHARSET self.root_charset = CHARSET