From 26c23068aa2fbd62ee2bf98d610bb976522cf5cb Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 01:09:11 +0000 Subject: [PATCH 01/11] split and compile messages --- .gitignore | 16 +- Makefile | 25 +- include/messages.h | 50 --- include/types.h | 2 +- sources.mk | 2 + tools/compile_messages.py | 805 ++++++++++++++++++++++++++++++++++++++ tools/n64splat | 2 +- tools/splat.yaml | 53 ++- 8 files changed, 887 insertions(+), 68 deletions(-) create mode 100755 tools/compile_messages.py diff --git a/.gitignore b/.gitignore index 3600fdfb40..349671fabe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,17 +9,17 @@ venv/ ctx.c expected/ settings.mk +.vscode/launch.json # Build artifacts *.ld *.z64 -*.bin -*.i *.Yay0 -bin/ -img/ -build/ -docs/doxygen/ -include/ld_addrs.h +/build/ +/docs/doxygen/ +/include/ld_addrs.h -.vscode/launch.json +# Assets +/bin +/img +/msg diff --git a/Makefile b/Makefile index 9058c9f924..1c60945b93 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,8 @@ ELF := $(BUILD_DIR)/$(TARGET).elf LD_SCRIPT := $(TARGET).ld LD_MAP := $(BUILD_DIR)/$(TARGET).map ASSETS_BIN := $(BUILD_DIR)/bin/assets/assets.bin - +MSG_BIN := $(BUILD_DIR)/msg/messages.bin +GENERATED_HEADERS := include/ld_addrs.h ### Tools ### @@ -61,6 +62,7 @@ ASFLAGS := -EB -Iinclude -march=vr4300 -mtune=vr4300 OLDASFLAGS := -EB -Iinclude -G 0 CFLAGS := -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow LDFLAGS := -T undefined_syms.txt -T undefined_funcs.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -Map $(LD_MAP) --no-check-sections +MSGFLAGS := -Iinclude -Imsg ifeq ($(WATCH_INCLUDES),1) CPPMFLAGS = -MP -MD -MF $@.mk -MT $(BUILD_DIR)/$*.d @@ -69,6 +71,7 @@ endif ifeq ($(NON_MATCHING),1) CPPFLAGS += -DNON_MATCHING +MSGFLAGS += -DNON_MATCHING endif @@ -99,10 +102,10 @@ submodules: split: rm -rf bin img - $(SPLAT) --modes ld bin Yay0 PaperMarioMapFS img + $(SPLAT) --modes bin Yay0 PaperMarioMapFS img split-%: - $(SPLAT) --modes ld $* + $(SPLAT) --modes $* --verbose split-all: rm -rf bin img @@ -130,7 +133,7 @@ $(BUILD_DIR)/%.Yay0.o: $(BUILD_DIR)/%.bin.Yay0 $(LD) -r -b binary -o $@ $< # Compile C files -$(BUILD_DIR)/%.c.o: %.c $(MDEPS) | include/ld_addrs.h +$(BUILD_DIR)/%.c.o: %.c $(MDEPS) | $(GENERATED_HEADERS) @mkdir -p $(shell dirname $@) $(CPP) $(CPPFLAGS) -o - $(CPPMFLAGS) $< | iconv --from UTF-8 --to SHIFT-JIS | $(CC) $(CFLAGS) -o - | $(OLD_AS) $(OLDASFLAGS) -o $@ - @@ -178,22 +181,30 @@ $(BUILD_DIR)/%.i8.png: %.png @mkdir -p $(shell dirname $@) $(PYTHON) tools/convert_image.py i8 $< $@ $(IMG_FLAGS) +# Assets ASSET_FILES := $(foreach asset, $(ASSETS), $(BUILD_DIR)/bin/assets/$(asset)) YAY0_ASSET_FILES := $(foreach asset, $(filter-out %_tex, $(ASSET_FILES)), $(asset).Yay0) - $(BUILD_DIR)/bin/assets/%: bin/assets/%.bin @mkdir -p $(shell dirname $@) @cp $< $@ - $(ASSETS_BIN): $(ASSET_FILES) $(YAY0_ASSET_FILES) sources.mk @mkdir -p $(shell dirname $@) @echo "building $@" @$(PYTHON) tools/build_assets_bin.py $@ $(ASSET_FILES) - $(ASSETS_BIN:.bin=.o): $(ASSETS_BIN) $(LD) -r -b binary -o $@ $< +# Messages +$(MSG_BIN): $(MESSAGES) + @mkdir -p $(shell dirname $@) + @echo "building $@" + @$(PYTHON) tools/compile_messages.py $@ /dev/null $(MESSAGES) +$(MSG_BIN:.bin=.o): $(MSG_BIN) + @mkdir -p $(shell dirname $@) + $(LD) -r -b binary -o $@ $< + $(LD_SCRIPT): $(SPLAT_YAML) + @mkdir -p $(shell dirname $@) $(SPLAT) --modes ld $(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT) diff --git a/include/messages.h b/include/messages.h index a7fea9666f..f580a18400 100644 --- a/include/messages.h +++ b/include/messages.h @@ -7,60 +7,10 @@ typedef s32 MessageID; #define MESSAGE_ID(section, index) ((section << 0x10) + index) -// 00 Introduction -// 01 Postgame Celebration -// 02 Toad Town Gate Sector -// 03 Toad Town Castle Sector -// 04 Toad Town Bridge Sector -// 05 Toad Town Train Sector -// 06 Toad Town Warehouse Sector -// 07 Toad Town Docks Sector -// 08 Minigames -// 09 Castle Grounds -// 0A Shooting Star Summit -// 0B Chapter 0 -// 0C Chapter 1 -// 0D Chapter 2 -// 0E Chapter 3 -// 0F Chapter 4 -// 10 Chapter 5 -// 11 Chapter 6 -// 12 Chapter 7 -// 13 Bowser's Castle -// 14 Peach Segments -// 15 Koopa Koot Favors -// 16 Russ T Advice -// 17 News Bulletin -// 18 Gossip Bulletin - -// 19 Map Tattles #define MessageID_TATTLE_KMR_03 MESSAGE_ID(0x19, 0x3B) #define MessageID_TATTLE_KMR_12 MESSAGE_ID(0x19, 0x40) -// 1A NPC Tattles -// 1B Entity Tattles -// 1C Enemy Tattles - -// 1D Menus I #define MessageID_SIGN_MUSHROOM_GOOMBA_TRAP MESSAGE_ID(0x1D, 0x167) #define MessageID_SIGN_GOOMBA_KINGS_FORTRESS_AHEAD MESSAGE_ID(0x1D, 0x168) -// 1E Choices -// 1F Menus II -// 20 Party Letters + Luigi's Diary -// 21 Advice Fortunes -// 22 Treasure Fortunes -// 23 Item Descriptions I -// 24 Item Descriptions II -// 25 Item Descriptions III -// 26 Item Names -// 27 Shop Messages -// 28 Partner Descriptions -// 29 Enemy Names -// 2A Mario Moves -// 2B Partner Moves -// 2C Quiz Questions -// 2D Quiz Options -// 2E Credits - #endif diff --git a/include/types.h b/include/types.h index ac7a5dcf3d..298e297e4d 100644 --- a/include/types.h +++ b/include/types.h @@ -11,6 +11,6 @@ #define UNK_ARGS typedef s32 FormationID; -#define FORMATION_ID(section, stage, index) (section << 16) + (stage << 8) + index +#define FORMATION_ID(section, stage, index) ((section << 16) + (stage << 8) + index) #endif diff --git a/sources.mk b/sources.mk index f2c0e86888..2d3efdad4e 100644 --- a/sources.mk +++ b/sources.mk @@ -56,5 +56,7 @@ ASSETS := \ title_data \ party_kurio party_kameki party_pinki party_pareta party_resa party_akari party_opuku party_pokopi +MESSAGES := $(shell find msg -type f -name "*.msg") + # Image settings $(BUILD_DIR)/img/battle/text_action_command_ratings.ia4.png: IMG_FLAGS = --flip-y diff --git a/tools/compile_messages.py b/tools/compile_messages.py new file mode 100755 index 0000000000..9d64c3c929 --- /dev/null +++ b/tools/compile_messages.py @@ -0,0 +1,805 @@ +#! /usr/bin/python3 + +from sys import argv +from collections import OrderedDict +import re + +class Message: + def __init__(self, name, section, index): + self.name = name + self.section = section + self.index = index + + self.bytes = [] + +def try_convert_int(s): + try: + return int(s, base=0) + except: + return s + +def parse_command(source): + if source[0] != "[": + return None, [], {}, source + source = source[1:] # "[" + + inside_brackets = "" + while source[0] != "]": + if source[0] == "\n": + return None, [], {}, source + + inside_brackets += source[0] + source = source[1:] + source = source[1:] # "]" + + command, *args = inside_brackets.split(" ") + + positional_args = [] + named_args = {} + + if "=" in command: + key, value = command.split("=", 1) + command = key + named_args[key] = try_convert_int(value) + + for arg in args: + if "=" in arg: + key, value = arg.split("=", 1) + named_args[key.lower()] = try_convert_int(value.lower()) + else: + positional_args.append(try_convert_int(arg)) + + return command.lower(), positional_args, named_args, source + +def color_to_code(color, ctx="normal"): + COLORS = { + "normal": { + "normal": 0x0A, + "red": 0x20, + "pink": 0x21, + "purple": 0x22, + "blue": 0x23, + "cyan": 0x24, + "green": 0x25, + "yellow": 0x26, + }, + "diary": { + "normal": 0x00, + "red": 0x07, + }, + "inspect": { + "dark": 0x17, + }, + "button": { + "blue": 0x10, + "green": 0x11, + "red": 0x12, + "yellow": 0x13, + "gray": 0x14, + "grey": 0x14, + }, + "popup": { + "red": 0x28, + "pink": 0x29, + "purple": 0x2A, + "blue": 0x2B, + "teal": 0x2C, + "green": 0x2D, + "yellow": 0x2E, + "normal": 0x2F, + }, + "sign": { + "normal": 0x18, + "red": 0x19, + "blue": 0x1A, + "green": 0x1B, + } + } + + if type(color) is int: + return color + + return COLORS.get(ctx, {}).get(color) + +CHARSET = { + "𝅘𝅥𝅮": 0x00, + "!": 0x01, + '"': 0x02, + "#": 0x03, + "$": 0x04, + "%": 0x05, + "&": 0x06, + "'": 0x07, + "(": 0x08, + ")": 0x09, + "*": 0x0A, + "+": 0x0B, + ",": 0x0C, + "-": 0x0D, + ".": 0x0E, + "/": 0x0F, + "0": 0x10, + "1": 0x11, + "2": 0x12, + "3": 0x13, + "4": 0x14, + "5": 0x15, + "6": 0x16, + "7": 0x17, + "8": 0x18, + "9": 0x19, + ":": 0x1A, + ";": 0x1B, + "<": 0x1C, + "=": 0x1D, + ">": 0x1E, + "?": 0x1F, + "@": 0x20, + "A": 0x21, + "B": 0x22, + "C": 0x23, + "D": 0x24, + "E": 0x25, + "F": 0x26, + "G": 0x27, + "H": 0x28, + "I": 0x29, + "J": 0x2A, + "K": 0x2B, + "L": 0x2C, + "M": 0x2D, + "N": 0x2E, + "O": 0x2F, + "P": 0x30, + "Q": 0x31, + "R": 0x32, + "S": 0x33, + "T": 0x34, + "U": 0x35, + "V": 0x36, + "W": 0x37, + "X": 0x38, + "Y": 0x39, + "Z": 0x3A, + "[": 0x3B, + "¥": 0x3C, + "]": 0x3D, + "^": 0x3E, + "_": 0x3F, + "`": 0x40, + "a": 0x41, + "b": 0x42, + "c": 0x43, + "d": 0x44, + "e": 0x45, + "f": 0x46, + "g": 0x47, + "h": 0x48, + "i": 0x49, + "j": 0x4A, + "k": 0x4B, + "l": 0x4C, + "m": 0x4D, + "n": 0x4E, + "o": 0x4F, + "p": 0x50, + "q": 0x51, + "r": 0x52, + "s": 0x53, + "t": 0x54, + "u": 0x55, + "v": 0x56, + "w": 0x57, + "x": 0x58, + "y": 0x59, + "z": 0x5A, + "{": 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, + "Ⓐ": [0xFF, 0x24, 0xFF, 0x05, 0x10, 0x98, 0xFF, 0x25], + "Ⓑ": [0xFF, 0x24, 0xFF, 0x05, 0x11, 0x99, 0xFF, 0x25], + "Ⓢ": [0xFF, 0x24, 0xFF, 0x05, 0x12, 0xA1, 0xFF, 0x25], + "⬆": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9D, 0xFF, 0x25], + "⬇": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9E, 0xFF, 0x25], + "⬅": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9F, 0xFF, 0x25], + "➡": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0xA0, 0xFF, 0x25], + "Ⓛ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9A, 0xFF, 0x25], + "Ⓡ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9B, 0xFF, 0x25], + "Ⓩ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9C, 0xFF, 0x25], +} + +CHARSET_CREDITS = { + "A": 0x00, + "B": 0x01, + "C": 0x02, + "D": 0x03, + "E": 0x04, + "F": 0x05, + "G": 0x06, + "H": 0x07, + "I": 0x08, + "J": 0x09, + "K": 0x0A, + "L": 0x0B, + "M": 0x0C, + "N": 0x0D, + "O": 0x0E, + "P": 0x0F, + "Q": 0x10, + "R": 0x11, + "S": 0x12, + "T": 0x13, + "U": 0x14, + "V": 0x15, + "W": 0x16, + "X": 0x17, + "Y": 0x18, + "Z": 0x19, + "'": 0x1A, + ".": 0x1B, + ",": 0x1C, + "0": 0x1D, + "1": 0x1E, + "2": 0x1F, + "3": 0x20, + "4": 0x21, + "5": 0x22, + "6": 0x23, + "7": 0x24, + "8": 0x25, + "9": 0x26, + "©": 0x27, + "&": 0x28, + " ": 0xF7, +} + +if __name__ == "__main__": + if len(argv) < 4: + print("usage: compile_messages.py [OUTBIN] [OUTHEADER] [INFILES]") + exit(1) + + _, outfile, outheader, *infiles = argv + + messages = [] + + for filename in infiles: + message = None + with open(filename, "r") as f: + source = f.read() + lineno = 1 + + charset = CHARSET + font_stack = [0] + sound_stack = [0] + color_stack = [0x0A] + + while len(source) > 0: + if source.startswith("\n"): + lineno += 1 + source = source[1:] + continue + + if message is None: + if source.startswith("//"): + while source[0] != "\n": + source = source[1:] + else: + command, positional_args, named_args, source = parse_command(source) + + if not command: + print(f"{filename}:{lineno}: expected [message]") + exit(1) + + name = positional_args[0] if len(positional_args) > 0 else None + message = Message(name, named_args.get("section"), named_args.get("index")) + messages.append(message) + else: + command, positional_args, named_args, source = parse_command(source) + + if command: + if command == "/message": + message.bytes += [0xFD] + + # padding + while len(message.bytes) % 4 != 0: + message.bytes += [0x00] + + message = None + elif command == "raw": + message.bytes += [*positional_args] + elif command == "func": + message.bytes += [0xFF, *positional_args] + elif command == "br": + message.bytes += [0xF0] + elif command == "prompt": + message.bytes += [0xF1] + elif command == "sleep": + if len(positional_args) == 0: + print(f"{filename}:{lineno}: {command} command requires a positional parameter") + exit(1) + + message.bytes += [0xF2, positional_args[0]] + elif command == "next": + message.bytes += [0xFB] + elif command == "color": + if "color" not in named_args: + print(f"{filename}:{lineno}: color command requires a 'color' parameter") + exit(1) + + color = color_to_code(**named_args) + + if color is None: + print(f"{filename}:{lineno}: unknown color combination {named_args}") + exit(1) + + message.bytes += [0xFF, 0x05, color] + color_stack.append(color) + elif command == "/color": + color_stack.pop() + message.bytes += [0xFF, 0x05, color_stack[0]] + elif command == "style": + if "style" not in named_args: + print(f"{filename}:{lineno}: style command requires a 'style' parameter") + exit(1) + + message.bytes += [0xFC] + + style = named_args["style"] + if type(style) is int: + message.bytes += [style, *positional_args] + else: + if style == "right": + message.bytes += [0x01] + elif style == "left": + message.bytes += [0x02] + elif style == "center": + message.bytes += [0x03] + elif style == "tattle": + message.bytes += [0x04] + 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: + print(f"{filename}:{lineno}: 'choice' style requires parameters: x, y, w, h") + exit(1) + + message.bytes += [0x05, named_args["w"], named_args["x"], named_args["h"], named_args["y"]] + elif style == "inspect": + message.bytes += [0x06] + elif style == "sign": + message.bytes += [0x07] + elif style == "lamppost": + message.bytes += [0x08] + elif style == "postcard": + message.bytes += [0x09] + elif style == "popup": + message.bytes += [0x0A] + 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: + print(f"{filename}:{lineno}: 'upgrade' style requires parameters: x, y, w, h") + exit(1) + + message.bytes += [0x0C, named_args["w"], named_args["x"], named_args["h"], named_args["y"]] + elif style == "narrate": + message.bytes += [0x0D] + elif style == "epilogue": + message.bytes += [0x0E] + elif command == "font": + if "font" not in named_args: + print(f"{filename}:{lineno}: font command requires a 'font' parameter") + exit(1) + + font = named_args["font"] + + if font == "normal": + font = 0 + elif font == "title": + font = 3 + elif font == "subtitle": + font = 4 + + if type(font) is not int: + print(f"{filename}:{lineno}: unknown font '{font}'") + exit(1) + + message.bytes += [0xFF, 0x00, font] + font_stack.append(font) + + if font == 3 or font == 4: + charset = CHARSET_CREDITS + else: + charset = CHARSET + elif command == "/font": + font_stack.pop() + message.bytes += [0xFF, 0x00, font_stack[0]] + + if font == 3 or font == 4: + charset = CHARSET_CREDITS + else: + charset = CHARSET + elif command == "noskip": + message.bytes += [0xFF, 0x07] + elif command == "/noskip": + message.bytes += [0xFF, 0x08] + elif command == "instant": + message.bytes += [0xFF, 0x09] + elif command == "/instant": + message.bytes += [0xFF, 0x0A] + elif command == "kerning": + if "kerning" not in named_args: + print(f"{filename}:{lineno}: kerning command requires a 'kerning' parameter") + exit(1) + + message.bytes += [0xFF, 0x0B, named_args["kerning"]] + elif command == "scroll": + if len(positional_args) == 0: + print(f"{filename}:{lineno}: scroll command requires a positional parameter") + exit(1) + + message.bytes += [0xFF, 0x0C, positional_args[0]] + elif command == "size": + if "x" not in named_args or "y" not in named_args: + print(f"{filename}:{lineno}: size command requires parameters: x, y") + exit(1) + + message.bytes += [0xFF, 0x0D, named_args["x"], named_args["y"]] + elif command == "/size": + message.bytes += [0xFF, 0x0E] + elif command == "speed": + if "delay" not in named_args or "chars" not in named_args: + print(f"{filename}:{lineno}: speed command requires parameters: delay, chars") + exit(1) + + message.bytes += [0xFF, 0x0F, named_args["delay"], named_args["chars"]] + elif command == "pos": + if "y" not in named_args: + 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 == "indent": + if len(positional_args) == 0: + print(f"{filename}:{lineno}: indent command requires a positional parameter") + exit(1) + + message.bytes += [0xFF, 0x12, positional_args[0]] + elif command == "down": + if len(positional_args) == 0: + print(f"{filename}:{lineno}: down command requires a positional parameter") + exit(1) + + message.bytes += [0xFF, 0x13, positional_args[0]] + elif command == "up": + if len(positional_args) == 0: + print(f"{filename}:{lineno}: up command requires a positional parameter") + exit(1) + + message.bytes += [0xFF, 0x14, positional_args[0]] + 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": + if len(positional_args) != 3: + print(f"{filename}:{lineno}: sprite command requires 3 positional parameters") + exit(1) + + message.bytes += [0xFF, 0x16, *positional_args] + elif command == "item": + if len(positional_args) != 2: + print(f"{filename}:{lineno}: item command requires 2 positional parameters") + exit(1) + + message.bytes += [0xFF, 0x17, *positional_args] + elif command == "cursor": + if len(positional_args) != 1: + print(f"{filename}:{lineno}: cursor command requires 1 positional parameter") + exit(1) + + message.bytes += [0xFF, 0x1E, *positional_args] + elif command == "option": + if len(positional_args) != 1: + print(f"{filename}:{lineno}: option command requires 1 positional parameter") + exit(1) + + message.bytes += [0xFF, 0x21, *positional_args] + elif command == "choice": + if len(positional_args) != 1: + print(f"{filename}:{lineno}: choice command requires 1 positional parameter") + exit(1) + + message.bytes += [0xFF, 0x1E, positional_args[0], 0xFF, 0x21, positional_args[0]] + elif command == "choicecount": + if "choicecount" not in named_args: + print(f"{filename}:{lineno}: choicecount command requires a 'choicecount' parameter") + exit(1) + + message.bytes += [0xFF, 0x1F, named_args["choicecount"]] + elif command == "cancel": + if "cancel" not in named_args: + print(f"{filename}:{lineno}: cancel command requires a 'cancel' parameter") + exit(1) + + message.bytes += [0xFF, 0x20, named_args["cancel"]] + elif command == "shaky": + message.bytes += [0xFF, 0x26, 0x00] + elif command == "/shaky": + message.bytes += [0xFF, 0x27, 0x00] + elif command == "wavy": + message.bytes += [0xFF, 0x26, 0x01] + elif command == "/wavy": + message.bytes += [0xFF, 0x27, 0x01] + elif command == "shaky": + if "opacity" in named_args: + print(f"{filename}:{lineno}: shaky command doesn't accept parameter 'fade' (hint: did you mean 'faded-shaky'?)") + exit(1) + message.bytes += [0xFF, 0x26, 0x00] + elif command == "/shaky": + message.bytes += [0xFF, 0x27, 0x00] + elif command == "noise": + message.bytes += [0xFF, 0x26, 0x03, named_args.get("fade", 3)] + elif command == "/noise": + message.bytes += [0xFF, 0x27, 0x03] + elif command == "faded-shaky": + message.bytes += [0xFF, 0x26, 0x05, named_args.get("fade", 5)] + elif command == "/faded-shaky": + message.bytes += [0xFF, 0x27, 0x05] + elif command == "fade": + message.bytes += [0xFF, 0x26, 0x07, named_args.get("fade", 7)] + elif command == "/fade": + message.bytes += [0xFF, 0x27, 0x07] + elif command == "shout" or command == "shrinking": + message.bytes += [0xFF, 0x26, 0x0A] + elif command == "/shout" or command == "/shrinking": + message.bytes += [0xFF, 0x27, 0x0A] + elif command == "whisper" or command == "growing": + message.bytes += [0xFF, 0x26, 0x0B] + elif command == "/whisper" or command == "/growing": + message.bytes += [0xFF, 0x27, 0x0B] + elif command == "scream" or command == "shaky-size": + message.bytes += [0xFF, 0x26, 0x0C] + elif command == "/scream" or command == "/shaky-size": + message.bytes += [0xFF, 0x27, 0x0C] + elif command == "chortle" or command == "wavy-size": + message.bytes += [0xFF, 0x26, 0x0D] + elif command == "/chortle" or command == "/wavy-size": + message.bytes += [0xFF, 0x27, 0x0D] + elif command == "shadow": + message.bytes += [0xFF, 0x26, 0x0E] + elif command == "/shadow": + message.bytes += [0xFF, 0x27, 0x0E] + elif command == "var": + if len(positional_args) != 1: + print(f"{filename}:{lineno}: var command requires 1 positional parameter") + exit(1) + + message.bytes += [0xFF, 0x28, *positional_args] + elif command == "center": + if len(positional_args) != 1: + print(f"{filename}:{lineno}: center command requires 1 positional parameter") + exit(1) + + message.bytes += [0xFF, 0x29, *positional_args] + elif command == "volume": + if "volume" not in named_args: + print(f"{filename}:{lineno}: volume command requires a 'volume' parameter") + exit(1) + + message.bytes += [0xFF, 0x2E, named_args["volume"]] + elif command == "sound": + if "sound" not in named_args: + print(f"{filename}:{lineno}: sound command requires a 'sound' parameter") + exit(1) + + sound = named_args["sound"] + + if sound == "normal": + sound = 0 + elif sound == "bowser": + sound = 1 + elif sound == "spirit": + sound = 2 + + if type(sound) is not int: + print(f"{filename}:{lineno}: unknown sound '{sound}'") + exit(1) + + message.bytes += [0xFF, 0x2F, sound] + sound_stack.append(sound) + elif command == "/sound": + sound_stack.pop() + 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 == "wait": + print(f"{filename}:{lineno}: unknown command 'wait' (hint: did you mean 'prompt'?)") + exit(1) + elif command == "pause": + print(f"{filename}:{lineno}: unknown command 'pause' (hint: did you mean 'sleep'?)") + exit(1) + else: + print(f"{filename}:{lineno}: unknown command '{command}'") + exit(1) + else: + if source[0] == "\\": + source = source[1:] + + if source[0] in charset: + data = charset[source[0]] + + if type(data) is int: + message.bytes.append(data) + else: + message.bytes += data + + source = source[1:] + else: + print(f"{filename}:{lineno}: unsupported character '{source[0]}' for current font") + exit(1) + + if message != None: + print(f"{filename}: missing [/message]") + exit(1) + + with open(outfile, "wb") as f: + messages.sort(key=lambda msg: bool(msg.section) + bool(msg.index)) + + names = OrderedDict() + + sections = [] * 0x2E + for message in messages: + if message.section is None: + # allocate a section + for section_idx, section in enumerate(sections): + if len(section) < 0xFFF: + break + else: + section_idx = message.section + while len(sections) <= section_idx: + sections.append([]) + section = sections[section_idx] + + index = message.index if message.index is not None else len(section) + + if message.name: + if message.name in names: + print(f"warning: multiple messages with name '{message.name}'") + + names[message.name] = (section_idx, index) + + section.append(bytes(message.bytes)) + + f.seek((len(sections) + 1) * 4) # skip past table of contents + + section_offsets = [] + for section in sections: + message_offsets = [] + for message in section: + message_offsets.append(f.tell()) + f.write(message) + + section_offset = f.tell() + section_offsets.append(section_offset) + for offset in message_offsets: + f.write(offset.to_bytes(4, byteorder="big")) + f.write(section_offset.to_bytes(4, byteorder="big")) + + # padding + while f.tell() % 0x10 != 0: + f.write(b'\0\0\0\0') + + f.seek(0) + for offset in section_offsets: + f.write(offset.to_bytes(4, byteorder="big")) + f.write(b'\0\0\0\0') + + with open(outheader, "w") as f: + f.write( + "#ifndef _MESSAGE_IDS_H_\n" + "#define _MESSAGE_IDS_H_\n" + "\n" + '#include "messages.h"\n' + "\n" + ) + + for name, i in names.items(): + section, index = i + f.write(f"#define MessageID_{name} MESSAGE_ID({section}, {index})\n") + + f.write("\n#endif\n") diff --git a/tools/n64splat b/tools/n64splat index 8f74e0bcce..bdfab59ce4 160000 --- a/tools/n64splat +++ b/tools/n64splat @@ -1 +1 @@ -Subproject commit 8f74e0bccec04e12c3dec27f64c257f61def594c +Subproject commit bdfab59ce46268b0d25a751b69392ea63b68812e diff --git a/tools/splat.yaml b/tools/splat.yaml index 5312fdce50..e0164af11b 100644 --- a/tools/splat.yaml +++ b/tools/splat.yaml @@ -6978,6 +6978,57 @@ segments: - [0x1B81E88, "Yay0"] - [0x1B82058, "Yay0"] - [0x1B82202, "bin"] - - [0x1E40000, "PaperMarioMapFS"] + - start: 0x1B83000 + type: PaperMarioMessages + files: + - intro + - end/postgame + - toad_town/gate + - toad_town/castle + - toad_town/bridge + - toad_town/train + - toad_town/warehouse + - toad_town/docks + - toad_town/minigames + - castle_grounds + - shooting_star_summit + - prologue + - chapter1 + - chapter2 + - chapter3 + - chapter4 + - chapter5 + - chapter6 + - chapter7 + - chapter8 + - peach_interludes + - koopa_koot_quests + - advice/russ_t + - toad_town/bulletin_news + - toad_town/bulletin_gossip + - world/map_tattles + - world/npc_tattles + - world/entity_tattles + - battle/enemy_tattles + - ui/misc + - ui/choices + - ui/pause + - diary_letters + - advice/merlon + - advice/merluvlee + - item/descriptions_23 # TODO: difference between 23,24,25 + - item/descriptions_24 + - item/descriptions_25 + - item/names + - shops + - partner_descriptions + - battle/enemy_names + - battle/mario_moves + - battle/partner_moves + - quiz/questions + - quiz/options + - end/credits + - [0x1C84D30, bin] + - [0x1E40000, PaperMarioMapFS] - [0x27FEE22, "bin"] - [0x2800000] From 793ee56416537271f2c1521eec0cff0ddbb7d061 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 01:10:49 +0000 Subject: [PATCH 02/11] remove unused mk variable MSGFLAGS --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 1c60945b93..59326271bc 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,6 @@ ASFLAGS := -EB -Iinclude -march=vr4300 -mtune=vr4300 OLDASFLAGS := -EB -Iinclude -G 0 CFLAGS := -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow LDFLAGS := -T undefined_syms.txt -T undefined_funcs.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -Map $(LD_MAP) --no-check-sections -MSGFLAGS := -Iinclude -Imsg ifeq ($(WATCH_INCLUDES),1) CPPMFLAGS = -MP -MD -MF $@.mk -MT $(BUILD_DIR)/$*.d @@ -71,7 +70,6 @@ endif ifeq ($(NON_MATCHING),1) CPPFLAGS += -DNON_MATCHING -MSGFLAGS += -DNON_MATCHING endif From 78fb3df09c3066b4587920a937e1cb4682b7a574 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 01:13:56 +0000 Subject: [PATCH 03/11] add alias for some non-ASCII chars as per star rod --- tools/compile_messages.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/compile_messages.py b/tools/compile_messages.py index 9d64c3c929..cc97509013 100755 --- a/tools/compile_messages.py +++ b/tools/compile_messages.py @@ -707,6 +707,24 @@ if __name__ == "__main__": 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 == "note": + message.bytes += [0x00] + elif command == "heart": + message.bytes += [0x90] + elif command == "star": + message.bytes += [0x91] + elif command == "up": + message.bytes += [0x92] + elif command == "down": + message.bytes += [0x93] + elif command == "left": + message.bytes += [0x94] + elif command == "right": + message.bytes += [0x95] + elif command == "circle": + message.bytes += [0x96] + elif command == "cross": + message.bytes += [0x97] elif command == "wait": print(f"{filename}:{lineno}: unknown command 'wait' (hint: did you mean 'prompt'?)") exit(1) From 750c9e7ccdc6799f833f151c340efaf13f79919d Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 11:56:01 +0000 Subject: [PATCH 04/11] add .msg syntax docs --- docs/message-colors.jpg | Bin 0 -> 48944 bytes docs/messages.md | 212 ++++++++++++++++++++++++++++++++++++++ tools/compile_messages.py | 8 +- 3 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 docs/message-colors.jpg create mode 100644 docs/messages.md diff --git a/docs/message-colors.jpg b/docs/message-colors.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65f3ab8190dd19b95d726eec24688f1bec5186ed GIT binary patch literal 48944 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 T`)~NM|CZDHFD`xpJgxi>oA5Mg literal 0 HcmV?d00001 diff --git a/docs/messages.md b/docs/messages.md new file mode 100644 index 0000000000..ba54e91d36 --- /dev/null +++ b/docs/messages.md @@ -0,0 +1,212 @@ +# `.msg` syntax + +## Character Set + +`[font=normal]`: 𝅘𝅥𝅮!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[¥]^_`abcdefghijklmnopqrstuvwxyz{|}~°ÀÁÂÄÇÈÉÊËÌÍÎÏÑÒÓÔÖÙÚÛÜßàáâäçèéêëìíîïñòóôöùúûü¡¿ª♥★↑↓←→●✖“”‘’ ⒶⒷⓈ⬆⬇⬅➡ⓁⓇⓏ + +`[font=title]` and `[font=subtitle]`: ABCDEFGHIJKLMNOPQRSTUVWXYZ'.,0123456789©& + +## Tags + +Similar to BBCode, tags begin with `[` and end with `]`. They may take positional arguments (`value`) and named arguments (`arg=value`). The order of named arguments does not matter. Values are parsed as integers if possible, and hexidecimal integers are supported. Tag names and argument names are case-insensitive. + +### `[message name section= index=]...[/message]` + +Marks the beginning and end of a message. + +All parameters on the opening tag are optional. +If `section` and/or `index` are omitted, the message will be allocated a section and/or index respectively. + +### `[br]` +Line break. + +### `[prompt]` +Waits for the A button to be pressed before continuing. + +### `[next]` +Scrolls down the message box to begin a new 'paragraph.' + +### `[sleep n]` +Waits for `n` frames before continuing. + +### `[color= ctx=]` + +Supported color names are: + +`ctx=normal` (default): normal red pink purple blue cyan green yellow + +`ctx=diary`: normal red + +`ctx=inspect`: dark + +`ctx=button`: blue green red yellow gray grey + +`ctx=popup`: normal red pink purple blue teal green yellow + +`ctx=sign`: normal red blue green + +To use other colors, provide `color` as an integer (e.g. `[color=0x10]`). Here are all the colors supported by the engine: +![Message colors](message-colors.jpg) + +### `[/color]` +Resets the color to what it was before the most recent `[color=]` tag. The default color at the start of the message is assumed to be `[color=normal]`, which is true for most messages. + +### `[a]` `[b]` `[l]` `[r] `[z]` `[start]` `[c-up]` `[c-down]` `[c-left]` `[c-right]` + +Shorthand for the button prompt characters ⒶⒷⓁⓇⓏⓈ⬆⬇⬅➡ respectively. You can override the color used with a named parameter `[a color=]`. + +Supported color names: blue green red yellow gray grey + +### `[style=]` + +Sets the box style to use for this message. Supported styles are: + +`[style=right]` `[style=left]` `[style=center]` - Standard speech bubble with the speaker coming from the given direction + +`[style=tattle]` - Small bubble used for overworld tattles + +`[style=choice x= y= w= h=]` - Box for multiple-choice options + +`[style=inspect]` - Internal narration box, often used when inspecting objects by pressing A + +`[style=sign]` `[style=lamppost]` `[style=postcard]` - Boxes with custom backgrounds + +`[style=popup]` - Box in center of screen that grows dynamically depending on how long the message is + +`[style=upgrade x= y= w= h=]` - Super Block box + +`[style=narrate]` - Narration; used when you obtain new partners + +`[style=epilogue]` - Used for post-chapter descriptions of what Mario and party did + +### `[font=]` + +Supported fonts: normal title subtitle + +Note that the `title` and `subtitle` fonts use a different character set to `normal`. + +### `[/font]` + +Resets the font to what it was before the most recent `[font=]` tag. + +### `[noskip]...[/noskip]` + +Disables the B button from skipping text within. + +### `[instant]...[/instant]` + +Causes all text within to appear instantly. + +### `[kerning=]` + +Modifies the spacing between letters. + +### `[scroll n]` + +Scrolls down `n` lines. + +### `[size x= y=]` + +Changes font size. + +### `[/size]` + +Resets the size back to x=10 y=10 (the default). + +### `[speed delay= chars=]` + +Changes text printing speed. `delay` is the number of frames between each print, and `chars` is the number of characters to print at once. For example, `[speed delay=5 chars=3]` would print 3 characters every 5 frames. + +### `[pos x= y=]` + +Overrides the current text printing position. + +`x` is optional. If only `y` is provided, the x position will not change. + +### `[indent n]` + +Indents the following text by `n` tabs. + +### `[up n]` and `[down n]` + +Moves the text printing position up/down by `n` pixels respectively. + +### `[image id]` + +Displays the given image. This requires extra setup when printing the message. + +There is also a 7-parameter variant of `image` that is not yet understood. + +### `[sprite unknown id raster]` + +Displays the given sprite. + +### `[item a b]` + +Displays the given world icon. + +### `[cursor n]` and `[option n]` + +Denotes the position for the hand cursor to appear and the start of the option text for choice `n` of a `[style=choice]` box. + +### `[choicecount=]` + +Sets the number of options given in this `[style=choice]` box. + +### `[cancel=]` + +Sets the option number to be selected if the user presses B. +If this is not provided, pressing B does nothing. + +### Effects + +- `[shaky]...[/shaky]` +- `[noise fade=]...[/noise]` +- `[faded-shaky fade=]...[/faded-shaky]` +- `[fade=]...[/fade]` +- `[shout]...[/shout]` or `[shrinking]...[/shrinking]` +- `[whisper]...[/whisper]` or `[growing]...[/growing]` +- `[scream]...[/scream]` or `[shaky-size]...[/shaky-size]` +- `[chortle]...[/chortle]` or `[wavy-size]...[/wavy-size]` +- `[shadow]...[/shadow]` + +### `[var n]` + +Replaced with the value of message variable `n`. Must be set before the message is printed. + +### `[center=]` + +Centers the following text. Parameter purpose is unknown. + +### `[volume=]` + +Changes the volume of the following text. + +### `[sound=]` + +Changes the speech sound for the following text. + +Supported sound names: normal bowser spirit + +You can also use an integer for the parameter. + +### `[/sound]` + +Resets the speech sound to what it was before the most recent `[sound=]` tag. + +### `[note]` `[heart]` `[star]` `[circle]` `[cross]` `[arrow-up]` `[arrow-down]` `[arrow-left]` `[arrow-right]` + +Equivalent to the characters 𝅘𝅥𝅮♥★●✖↑↓←→ respectively. + +### `[raw ...]` + +Outputs all arguments provided as bytes, without modification. + +### `[func ...]` + +Same as `raw`, but prefixed with 0xFF. + +## Comments + +`//` line comments are allowed anywhere outside of a `[message]...[/message]` block and will be ignored. +Block comments are not supported. diff --git a/tools/compile_messages.py b/tools/compile_messages.py index cc97509013..b8bdc1ce6f 100755 --- a/tools/compile_messages.py +++ b/tools/compile_messages.py @@ -713,13 +713,13 @@ if __name__ == "__main__": message.bytes += [0x90] elif command == "star": message.bytes += [0x91] - elif command == "up": + elif command == "arrow-up": message.bytes += [0x92] - elif command == "down": + elif command == "arrow-down": message.bytes += [0x93] - elif command == "left": + elif command == "arrow-left": message.bytes += [0x94] - elif command == "right": + elif command == "arrow-right": message.bytes += [0x95] elif command == "circle": message.bytes += [0x96] From f17579def29d9db75e48c0e347934979f7c1a38c Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 7 Nov 2020 11:58:42 +0000 Subject: [PATCH 05/11] whoops --- docs/messages.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/messages.md b/docs/messages.md index ba54e91d36..0d8ed87ef8 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -10,6 +10,8 @@ Similar to BBCode, tags begin with `[` and end with `]`. They may take positional arguments (`value`) and named arguments (`arg=value`). The order of named arguments does not matter. Values are parsed as integers if possible, and hexidecimal integers are supported. Tag names and argument names are case-insensitive. +To write a literal `[` character (and not start a tag), prefix it with a backslash; i.e. `\[`. + ### `[message name section= index=]...[/message]` Marks the beginning and end of a message. @@ -51,7 +53,7 @@ To use other colors, provide `color` as an integer (e.g. `[color=0x10]`). Here a ### `[/color]` Resets the color to what it was before the most recent `[color=]` tag. The default color at the start of the message is assumed to be `[color=normal]`, which is true for most messages. -### `[a]` `[b]` `[l]` `[r] `[z]` `[start]` `[c-up]` `[c-down]` `[c-left]` `[c-right]` +### `[a]` `[b]` `[l]` `[r]` `[z]` `[start]` `[c-up]` `[c-down]` `[c-left]` `[c-right]` Shorthand for the button prompt characters ⒶⒷⓁⓇⓏⓈ⬆⬇⬅➡ respectively. You can override the color used with a named parameter `[a color=]`. From d5a8f66d2b93728b42ca5d5f6363947b87a44927 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 12:06:31 +0000 Subject: [PATCH 06/11] change C button chars --- docs/messages.md | 6 ++++-- tools/compile_messages.py | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/messages.md b/docs/messages.md index ba54e91d36..59ee903c28 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -2,7 +2,7 @@ ## Character Set -`[font=normal]`: 𝅘𝅥𝅮!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[¥]^_`abcdefghijklmnopqrstuvwxyz{|}~°ÀÁÂÄÇÈÉÊËÌÍÎÏÑÒÓÔÖÙÚÛÜßàáâäçèéêëìíîïñòóôöùúûü¡¿ª♥★↑↓←→●✖“”‘’ ⒶⒷⓈ⬆⬇⬅➡ⓁⓇⓏ +`[font=normal]`: 𝅘𝅥𝅮!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[¥]^_`abcdefghijklmnopqrstuvwxyz{|}~°ÀÁÂÄÇÈÉÊËÌÍÎÏÑÒÓÔÖÙÚÛÜßàáâäçèéêëìíîïñòóôöùúûü¡¿ª♥★↑↓←→●✖“”‘’ ⒶⒷⓈ▲▼◀▶ⓁⓇⓏ `[font=title]` and `[font=subtitle]`: ABCDEFGHIJKLMNOPQRSTUVWXYZ'.,0123456789©& @@ -10,6 +10,8 @@ Similar to BBCode, tags begin with `[` and end with `]`. They may take positional arguments (`value`) and named arguments (`arg=value`). The order of named arguments does not matter. Values are parsed as integers if possible, and hexidecimal integers are supported. Tag names and argument names are case-insensitive. +To write a literal `[` character (and not start a tag), prefix it with a backslash; i.e. `\[`. + ### `[message name section= index=]...[/message]` Marks the beginning and end of a message. @@ -53,7 +55,7 @@ Resets the color to what it was before the most recent `[color=]` tag. The defau ### `[a]` `[b]` `[l]` `[r] `[z]` `[start]` `[c-up]` `[c-down]` `[c-left]` `[c-right]` -Shorthand for the button prompt characters ⒶⒷⓁⓇⓏⓈ⬆⬇⬅➡ respectively. You can override the color used with a named parameter `[a color=]`. +Shorthand for the button prompt characters ⒶⒷⓁⓇⓏⓈ▲▼◀▶ respectively. You can override the color used with a named parameter `[a color=]`. Supported color names: blue green red yellow gray grey diff --git a/tools/compile_messages.py b/tools/compile_messages.py index b8bdc1ce6f..e92d716376 100755 --- a/tools/compile_messages.py +++ b/tools/compile_messages.py @@ -262,10 +262,10 @@ CHARSET = { "Ⓐ": [0xFF, 0x24, 0xFF, 0x05, 0x10, 0x98, 0xFF, 0x25], "Ⓑ": [0xFF, 0x24, 0xFF, 0x05, 0x11, 0x99, 0xFF, 0x25], "Ⓢ": [0xFF, 0x24, 0xFF, 0x05, 0x12, 0xA1, 0xFF, 0x25], - "⬆": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9D, 0xFF, 0x25], - "⬇": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9E, 0xFF, 0x25], - "⬅": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9F, 0xFF, 0x25], - "➡": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0xA0, 0xFF, 0x25], + "▲": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9D, 0xFF, 0x25], + "▼": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9E, 0xFF, 0x25], + "◀": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0x9F, 0xFF, 0x25], + "▶": [0xFF, 0x24, 0xFF, 0x05, 0x13, 0xA0, 0xFF, 0x25], "Ⓛ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9A, 0xFF, 0x25], "Ⓡ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9B, 0xFF, 0x25], "Ⓩ": [0xFF, 0x24, 0xFF, 0x05, 0x14, 0x9C, 0xFF, 0x25], From 4f48865aee7a065094e3f931c6b9dfc39780cada Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 12:06:38 +0000 Subject: [PATCH 07/11] fix build --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 59326271bc..e01fd54b0c 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ ELF := $(BUILD_DIR)/$(TARGET).elf LD_SCRIPT := $(TARGET).ld LD_MAP := $(BUILD_DIR)/$(TARGET).map ASSETS_BIN := $(BUILD_DIR)/bin/assets/assets.bin -MSG_BIN := $(BUILD_DIR)/msg/messages.bin +MSG_BIN := $(BUILD_DIR)/msg.bin GENERATED_HEADERS := include/ld_addrs.h ### Tools ### From 1e7fa8e418f1082c23566d818949656c8f5c7ec1 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 14:36:42 +0000 Subject: [PATCH 08/11] bump splat --- tools/n64splat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/n64splat b/tools/n64splat index bdfab59ce4..5ada79c3f2 160000 --- a/tools/n64splat +++ b/tools/n64splat @@ -1 +1 @@ -Subproject commit bdfab59ce46268b0d25a751b69392ea63b68812e +Subproject commit 5ada79c3f2ddd0995e84a192dca65e063898ef08 From 96a20fc06012b88678f70fa76fda19a7cf053098 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 14:45:16 +0000 Subject: [PATCH 09/11] bump splat --- tools/n64splat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/n64splat b/tools/n64splat index 5ada79c3f2..6106762b05 160000 --- a/tools/n64splat +++ b/tools/n64splat @@ -1 +1 @@ -Subproject commit 5ada79c3f2ddd0995e84a192dca65e063898ef08 +Subproject commit 6106762b0561e40a640c11852a17bb87963c7ba8 From 9dfead1cd16a7e65e17810a8b7b6c2eb161c2806 Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 14:51:24 +0000 Subject: [PATCH 10/11] add LD_SCRIPT to setup --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e01fd54b0c..05a5b9d368 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ clean: clean-code: rm -rf $(BUILD_DIR)/src -setup: clean submodules split +setup: clean submodules split $(LD_SCRIPT) make -C tools submodules: From 12f29419dbb56a5775643330b7565f3bbe0662bd Mon Sep 17 00:00:00 2001 From: Alex Bates <16batesa@gmail.com> Date: Sat, 7 Nov 2020 14:55:16 +0000 Subject: [PATCH 11/11] split PaperMarioMessages --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 05a5b9d368..cf93519fb9 100644 --- a/Makefile +++ b/Makefile @@ -100,7 +100,7 @@ submodules: split: rm -rf bin img - $(SPLAT) --modes bin Yay0 PaperMarioMapFS img + $(SPLAT) --modes bin Yay0 PaperMarioMapFS PaperMarioMessages img split-%: $(SPLAT) --modes $* --verbose