summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcadmic <cadmic24@gmail.com>2024-07-17 14:56:00 -0700
committerGitHub <noreply@github.com>2024-07-17 17:56:00 -0400
commita6438f05334feb4bb3e7c8ac57ec1d8a7df3df10 (patch)
tree00750fb725ce3535ce372c98171a78f35a2290df
parent7eee97429f0584b25304067aa9cad035cc84aef9 (diff)
Handle messages with different box types/positions between JPN/NES (#1984)
* Handle messages with different box types/positions between JPN/NES * Remove redundant case * More asserts * Be a bit more Pythonic
-rwxr-xr-xtools/msgdis.py81
1 files changed, 55 insertions, 26 deletions
diff --git a/tools/msgdis.py b/tools/msgdis.py
index aaa26c9cf..35006b10e 100755
--- a/tools/msgdis.py
+++ b/tools/msgdis.py
@@ -4,10 +4,12 @@
#
import argparse, re, struct
-from typing import Callable, Dict, List, Optional, Tuple
+from typing import Callable, Dict, List, Optional, Tuple, TypeVar
import version_config
+T = TypeVar("T")
+
item_ids = {
0x00 : "ITEM_DEKU_STICK",
0x01 : "ITEM_DEKU_NUT",
@@ -227,6 +229,15 @@ def read_sfx_ids():
return sfx_ids
+def unique_or_none(lst : List[T]) -> Optional[T]:
+ if not lst:
+ return None
+ elem = lst[0]
+ for e in lst[1:]:
+ if e != elem:
+ return None
+ return elem
+
class MessageDecoder:
def __init__(self, sfx_ids : Dict[int,str], control_end : int, control_codes : Dict[int, Tuple[str, str, Optional[Tuple[Callable[[int], str]]]]], extraction_charmap : Dict[int, str]) -> None:
self.sfx_ids : Dict[int,str] = sfx_ids
@@ -684,16 +695,20 @@ class MessageTableEntry:
box_pos = (info >> 0) & 0xF
return MessageTableEntry(text_id, box_type, box_pos, addr)
-class MessageEntry:
- def __init__(self, message_tables : List[Optional[MessageTableDesc]], text_id : int, box_type : int, box_pos : int) -> None:
- self.text_id : int = text_id
+class MessageData:
+ def __init__(self, box_type : int, box_pos : int, decoded_text : str):
self.box_type : int = box_type
self.box_pos : int = box_pos
- self.data : List[Tuple[Optional[MessageDecoder], Optional[bytes]]] = [(None,None) for _ in message_tables]
+ self.decoded_text : str = decoded_text
+
+class MessageEntry:
+ def __init__(self, message_tables : List[Optional[MessageTableDesc]], text_id : int) -> None:
+ self.text_id : int = text_id
+ self.data : List[Optional[MessageData]] = [None for _ in message_tables]
self.select = tuple(tbl is not None for tbl in message_tables)
- def box_type_str(self) -> str:
- return {
+ def define_message(self, defn : str, box_type : int, box_pos : int, data : List[Optional[MessageData]]) -> str:
+ box_type_str = {
0: "TEXTBOX_TYPE_BLACK",
1: "TEXTBOX_TYPE_WOODEN",
2: "TEXTBOX_TYPE_BLUE",
@@ -701,37 +716,48 @@ class MessageEntry:
4: "TEXTBOX_TYPE_NONE_BOTTOM",
5: "TEXTBOX_TYPE_NONE_NO_SHADOW",
0xB: "TEXTBOX_TYPE_CREDITS",
- }[self.box_type]
-
- def box_pos_str(self) -> str:
- return {
+ }[box_type]
+ box_pos_str = {
0: "TEXTBOX_POS_VARIABLE",
1: "TEXTBOX_POS_TOP",
2: "TEXTBOX_POS_MIDDLE",
3: "TEXTBOX_POS_BOTTOM",
- }[self.box_pos]
+ }[box_pos]
+ out = f"{defn}(0x{self.text_id:04X}, {box_type_str}, {box_pos_str},\n"
+ out += "\n,\n".join(f"MSG(\n{d.decoded_text}\n)" if d is not None else "MSG(/* MISSING */)" for d in data)
+ out += "\n)\n"
+ return out
def decode(self) -> str:
- selection = tuple(not (select and data == (None,None)) for select,data in zip(self.select,self.data))
- assert any(sel for sel in selection)
+ selection = tuple(not (select and data is None) for select,data in zip(self.select,self.data))
+ assert any(selection)
- defn = ""
- if all(sel for sel in selection):
- # Valid for all languages
- defn = "DEFINE_MESSAGE"
+ out = ""
+ if all(selection):
+ shared_box_type = unique_or_none([data.box_type for data in self.data if data is not None])
+ shared_box_pos = unique_or_none([data.box_pos for data in self.data if data is not None])
+ if shared_box_type is not None and shared_box_pos is not None:
+ # Valid for all languages
+ out += self.define_message("DEFINE_MESSAGE", shared_box_type, shared_box_pos, self.data)
+ else:
+ # Some NTSC messages have different box types/positions between JPN and NES,
+ # so emit both DEFINE_MESSAGE_JPN and DEFINE_MESSAGE_NES
+ assert self.data[0] is not None
+ assert self.data[1] is not None
+ assert self.data[2] is None
+ assert self.data[3] is None
+ out += self.define_message("DEFINE_MESSAGE_JPN", self.data[0].box_type, self.data[0].box_pos, [self.data[0], None, None, None])
+ out += self.define_message("DEFINE_MESSAGE_NES", self.data[1].box_type, self.data[1].box_pos, [None, self.data[1], None, None])
elif selection == (True,False,True,True):
# JPN only
- defn = "DEFINE_MESSAGE_JPN"
+ out += self.define_message("DEFINE_MESSAGE_JPN", self.data[0].box_type, self.data[0].box_pos, self.data)
elif selection == (False,True,True,True):
# NES only
- defn = "DEFINE_MESSAGE_NES"
+ out += self.define_message("DEFINE_MESSAGE_NES", self.data[1].box_type, self.data[1].box_pos, self.data)
else:
# Other unimplemented cases
assert False
- out = f"{defn}(0x{self.text_id:04X}, {self.box_type_str()}, {self.box_pos_str()},\n"
- out += "\n,\n".join(f"MSG(\n{decoder.decode(data)}\n)" if decoder is not None else "MSG(/* MISSING */)" for decoder,data in self.data)
- out += "\n)\n"
return out
def collect_messages(message_tables : List[Optional[MessageTableDesc]], version : str,
@@ -776,8 +802,9 @@ def collect_messages(message_tables : List[Optional[MessageTableDesc]], version
size = next_offset - curr_offset
if curr.text_id not in messages:
- messages[curr.text_id] = MessageEntry(message_tables, curr.text_id, curr.box_type, curr.box_pos)
- messages[curr.text_id].data[lang_num] = (desc.decoder, baserom_seg[curr_offset : curr_offset+size])
+ messages[curr.text_id] = MessageEntry(message_tables, curr.text_id)
+ messages[curr.text_id].data[lang_num] = MessageData(
+ curr.box_type, curr.box_pos, desc.decoder.decode(baserom_seg[curr_offset : curr_offset+size]))
else:
# Addresses only
@@ -794,7 +821,9 @@ def collect_messages(message_tables : List[Optional[MessageTableDesc]], version
size = next_offset - curr_offset
# The text id is guaranteed to already exist
- messages[text_id].data[lang_num] = (desc.decoder, baserom_seg[curr_offset:curr_offset+size])
+ parent_data = messages[text_id].data[desc.parent]
+ messages[text_id].data[lang_num] = MessageData(
+ parent_data.box_type, parent_data.box_pos, desc.decoder.decode(baserom_seg[curr_offset:curr_offset+size]))
return messages