diff --git a/configure.py b/configure.py index 9c52efd06b..047916b066 100755 --- a/configure.py +++ b/configure.py @@ -315,9 +315,8 @@ async def main(): description="ld_addrs_h $in") n.newline() - # $msg_combine_headers n.rule("msg_combine", - command="$python tools/msg/combine.py $out $in --headers $msg_combine_headers", + command="$python tools/msg/combine.py $out $in", description="combine messages") n.rule("msg", command="$python tools/msg/parse_compile.py $in $out", @@ -386,10 +385,11 @@ async def main(): n.build(add_generated_header("$builddir/include/ld_addrs.h"), "ld_addrs_h", "$builddir/$target.ld") # messages - msg_files = [] + msg_files = set() for d in ASSET_DIRS: - msg_files.extend(glob(d + "/**/*.msg", recursive=True)) - msg_files = list(set(msg_files)) # dedup + for f in glob(d + "/**/*.msg", recursive=True): + msg_files.add(find_asset(f[len(d)+1:])) + msg_files = list(msg_files) for msg_file in msg_files: n.build( f"$builddir/{msg_file.split('/', 1)[1]}.bin", @@ -397,15 +397,13 @@ async def main(): msg_file, implicit="tools/msg/parse_compile.py", ) - msg_headers = [add_generated_header(f"$builddir/include/{msg_file.split('/', 1)[1]}.h") for msg_file in msg_files] + #msg_headers = [add_generated_header(f"$builddir/include/{msg_file.split('/', 1)[1]}.h") for msg_file in msg_files] msg_bins = [f"$builddir/{msg_file.split('/', 1)[1]}.bin" for msg_file in msg_files] n.build( - "$builddir/msg.bin", + ["$builddir/msg.bin", add_generated_header(f"$builddir/include/message_ids.h")], "msg_combine", msg_bins, implicit="tools/msg/combine.py", - implicit_outputs=msg_headers, - variables={ "msg_combine_headers": msg_headers } ) n.build("$builddir/msg.o", "bin", "$builddir/msg.bin") diff --git a/include/messages.h b/include/messages.h index c8a8789817..869794d1b5 100644 --- a/include/messages.h +++ b/include/messages.h @@ -5,12 +5,7 @@ typedef s32 MessageID; +// Prefer editing splat.yaml's msg ids section than using this directly! #define MESSAGE_ID(section, index) (((section << 0x10) + index)) -#define MessageID_TATTLE_KMR_03 MESSAGE_ID(0x19, 0x3B) -#define MessageID_TATTLE_KMR_12 MESSAGE_ID(0x19, 0x40) - -#define MessageID_SIGN_MUSHROOM_GOOMBA_TRAP MESSAGE_ID(0x1D, 0x167) -#define MessageID_SIGN_GOOMBA_KINGS_FORTRESS_AHEAD MESSAGE_ID(0x1D, 0x168) - #endif diff --git a/src/world/area_kmr/kmr_03/8C7F90.c b/src/world/area_kmr/kmr_03/8C7F90.c index b1246aeb20..f96d6b4a2b 100644 --- a/src/world/area_kmr/kmr_03/8C7F90.c +++ b/src/world/area_kmr/kmr_03/8C7F90.c @@ -1,4 +1,5 @@ #include "kmr_03.h" +#include "message_ids.h" #include "../../partners.h" Script N(Main); @@ -61,7 +62,7 @@ MapConfig N(config) = { .entryList = N(entryList), .entryCount = ENTRY_COUNT(N(entryList)), .background = &gBackgroundImage, - .tattle = MessageID_TATTLE_KMR_03, + .tattle = MSG_kmr_03_tattle, }; Script N(Script_802406C0) = SCRIPT({ diff --git a/src/world/area_kmr/kmr_12/events.c b/src/world/area_kmr/kmr_12/events.c index 8d02050f8a..980ece82af 100644 --- a/src/world/area_kmr/kmr_12/events.c +++ b/src/world/area_kmr/kmr_12/events.c @@ -1,4 +1,5 @@ #include "kmr_12.h" +#include "message_ids.h" #include "sprite/npc/goomba.h" Script N(ExitWest) = EXIT_WALK_SCRIPT(60, 0, "kmr_07", 1); @@ -59,7 +60,7 @@ Script N(ReadWestSign) = SCRIPT({ // "Eat a Mushroom to regain your energy!" suspend group 1; DisablePlayerInput(TRUE); - ShowMessageAtScreenPos(MessageID_SIGN_MUSHROOM_GOOMBA_TRAP, 160, 40); + ShowMessageAtScreenPos(MSG_kmr_12_sign_trap, 160, 40); resume group 1; SI_FLAG(0) = FALSE; @@ -201,7 +202,7 @@ Script N(ReadEastSign) = SCRIPT({ func_802D5830(1); DisablePlayerInput(1); - ShowMessageAtScreenPos(MessageID_SIGN_GOOMBA_KINGS_FORTRESS_AHEAD, 160, 40); + ShowMessageAtScreenPos(MSG_kmr_12_sign_to_fortress, 160, 40); DisablePlayerInput(0); func_802D5830(0); }); diff --git a/src/world/area_kmr/kmr_12/header.c b/src/world/area_kmr/kmr_12/header.c index 9d6ae992db..3ff536959d 100644 --- a/src/world/area_kmr/kmr_12/header.c +++ b/src/world/area_kmr/kmr_12/header.c @@ -1,4 +1,5 @@ #include "kmr_12.h" +#include "message_ids.h" Vec4f N(entryList)[] = { { -126.0f, 0.0f, 12.0f, 90.0f }, // west, towards Red/Blue Goomba miniboss room @@ -10,7 +11,7 @@ MapConfig N(config) = { .entryList = N(entryList), .entryCount = ENTRY_COUNT(N(entryList)), .background = &gBackgroundImage, - .tattle = MessageID_TATTLE_KMR_12, + .tattle = MSG_kmr_12_tattle, }; Script N(PlayMusic) = SCRIPT({ diff --git a/tools/msg/combine.py b/tools/msg/combine.py index 2a38642821..d0ebb8be8b 100755 --- a/tools/msg/combine.py +++ b/tools/msg/combine.py @@ -16,30 +16,38 @@ class Message: if __name__ == "__main__": if len(argv) < 3: - print("usage: combine.py [out.bin] [compiled...] --headers [out.h]") + print("usage: combine.py [out.bin] [out.h] [compiled...]") exit(1) - _, outfile, *infiles = argv + _, outfile, header_file, *infiles = argv messages = [] - header_files = [] + #header_files = [] for i, infile in enumerate(infiles): - if infile == "--headers": - header_files = infiles[i+1:] - break + # if infile == "--headers": + # header_files = infiles[i+1:] + # break with open(infile, "rb") as f: messages.extend(Message(msg, i) for msg in msgpack.unpack(f)) with open(outfile, "wb") as f: # sectioned+indexed, followed by just sectioned, followed by just indexed, followed by named (unsectioned & unindexed) - messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index)) + #messages.sort(key=lambda msg: bool(msg.section)<<2 + bool(msg.index)) names = set() - sections = [] * 0x2E - messages_by_file = {} + sections = [] + #messages_by_file = {} + + # this logic could probably be a bit better (i.e. read ahead, so no overwriting happens) + def section_get_unused_id(section): + max_index = 0 + for index in section: + if index > max_index: + max_index = index + return max_index + 1 for message in messages: if message.section is None: @@ -47,35 +55,45 @@ if __name__ == "__main__": for section_idx, section in enumerate(sections): if len(section) < 0xFFF: break + message.section = section_idx else: section_idx = message.section while len(sections) <= section_idx: - sections.append([]) + sections.append({}) section = sections[section_idx] - message.index = index = message.index if message.index is not None else len(section) + if message.index is None: + message.index = section_get_unused_id(section) if message.name: if message.name in names: - print(f"warning: multiple messages with name '{message.name}'") + print(f"error: multiple messages with name '{message.name}'") + exit(1) else: names.add(message.name) - if message.header_file_index in messages_by_file: - messages_by_file[message.header_file_index].add(message) - else: - messages_by_file[message.header_file_index] = set([message]) + # if message.header_file_index in messages_by_file: + # messages_by_file[message.header_file_index].add(message) + # else: + # messages_by_file[message.header_file_index] = set([message]) - section.append(message.bytes) + if message.index in section: + print(f"error: multiple messages allocated to id {section_idx:02X}:{message.index:03X}") + exit(1) + + section[message.index] = message f.seek((len(sections) + 1) * 4) # skip past table of contents section_offsets = [] for section in sections: + # convert dict into sorted list + section = [msg for idx, msg in sorted(section.items(), key=lambda ele: ele[0])] + message_offsets = [] for message in section: message_offsets.append(f.tell()) - f.write(message) + f.write(message.bytes) section_offset = f.tell() section_offsets.append(section_offset) @@ -92,34 +110,17 @@ if __name__ == "__main__": f.write(offset.to_bytes(4, byteorder="big")) f.write(b'\0\0\0\0') - for i, header_file in enumerate(header_files): - messages = messages_by_file.get(i, []) - - h = ( - f"#ifndef _MESSAGE_IDS_{i}_H_\n" - f"#define _MESSAGE_IDS_{i}_H_\n" + with open(header_file, "w") as f: + f.write( + f"#ifndef _MESSAGE_IDS_H_\n" + f"#define _MESSAGE_IDS_H_\n" "\n" '#include "messages.h"\n' "\n" ) for message in messages: - h += f"#define MSG_{message.name} MESSAGE_ID({message.section}, {message.index})\n" + if message.name: + f.write(f"#define MSG_{message.name} MESSAGE_ID(0x{message.section:02X}, 0x{message.index:03X})\n") - h += "\n#endif\n" - h_lines = h.splitlines() - - # this doesnt work properly with ninja. the build is fast enough anyway - """ - # only rewrite the header file if its content changed - with open(header_file, "r") as f: - cur_h_lines = f.read().splitlines() - is_different = cur_h_lines != h_lines - - if is_different: - with open(header_file, "w") as f: - f.write(h) - """ - - with open(header_file, "w") as f: - f.write(h) + f.write("\n#endif\n") diff --git a/tools/splat.yaml b/tools/splat.yaml index 932d511a28..5035074375 100644 --- a/tools/splat.yaml +++ b/tools/splat.yaml @@ -9467,7 +9467,7 @@ segments: - 20_party_letters_luigi_diary - 21_advice_fortunes - 22_treasure_fortunes - - 23_item_descriptions # TODO: difference between 23,24,25 + - 23_item_descriptions # TODO: difference between 23,24,25 (shops, menus, pickups?) - 24_item_descriptions - 25_item_descriptions - 26_item_names @@ -9479,6 +9479,11 @@ segments: - 2C_quiz_questions - 2D_quiz_options - 2E_credits + ids: + - [0x19, 0x03B, kmr_03_tattle] + - [0x19, 0x040, kmr_12_tattle] + - [0x1D, 0x167, kmr_12_sign_trap] + - [0x1D, 0x168, kmr_12_sign_to_fortress] - [0x1C84D30, bin] # junk(?) - [0x1E00000, bin] # junk (player sprite data; can be zeroed out with no effect) - [0x1E40000, PaperMarioMapFS] diff --git a/tools/splat_ext/PaperMarioMessages.py b/tools/splat_ext/PaperMarioMessages.py index b8f212337b..ca141c7117 100644 --- a/tools/splat_ext/PaperMarioMessages.py +++ b/tools/splat_ext/PaperMarioMessages.py @@ -356,6 +356,7 @@ 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 [] + self.ids = segment["ids"] def split(self, rom_bytes, base_path): data = rom_bytes[self.rom_start: self.rom_end] @@ -401,7 +402,17 @@ class N64SegPaperMarioMessages(N64Segment): for j, msg_offset in enumerate(msg_offsets): if j != 0: self.f.write("\n") - self.f.write(f"#message:{i:02X}:{j:03X} {{\n ") + + msg_name = None + for section, index, goodname in self.ids: + if i == section and j == index: + msg_name = goodname + break + + if msg_name is None: + self.f.write(f"#message:{i:02X}:{j:03X} {{\n ") + else: + self.f.write(f"#message:{i:02X}:({msg_name}) {{\n ") self.write_message_markup(data[msg_offset:]) self.f.write("\n}\n")