Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/mars_patcher/auto_generated_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,11 @@
'Spanish'
]

Itemmessages: typ.TypeAlias = dict[Validlanguages, typ.Annotated[str, 'len() <= 112']]
Messagelanguages: typ.TypeAlias = dict[Validlanguages, str]
class Itemmessages(typ.TypedDict, total=False):
Languages: Messagelanguages
Centered: bool = True

class BlocklayerItem(typ.TypedDict, total=False):
X: Typeu8
"""The X position in the room that should get edited."""
Expand Down
2 changes: 2 additions & 0 deletions src/mars_patcher/constants/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ class ItemSprite(Enum):
KEY_ITEM: Final = "Item"
KEY_ITEM_SPRITE: Final = "ItemSprite"
KEY_ITEM_MESSAGES: Final = "ItemMessages"
KEY_LANGUAGES: Final = "Languages"
KEY_CENTERED: Final = "Centered"


SOURCE_ENUMS = {
Expand Down
61 changes: 36 additions & 25 deletions src/mars_patcher/data/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,29 +629,29 @@
"default": false
},
"RoomNames": {
"type": "array",
"description": "Specifies a name to be displayed when the A Button is pressed on the pause menu.",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"Area": {
"$ref": "#/$defs/AreaID",
"description": "The area ID where this room is located."
},
"Room": {
"$ref": "#/$defs/TypeU8",
"description": "The room ID."
},
"Name": {
"type": "string",
"description": "Specifies what text should appear for this room. Two lines are available, with an absolute maximum of 56 characters per line, if all characters used are small. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, will display 'Unknown Room'.",
"maxLength": 112
}
},
"required": ["Area", "Room", "Name"]
"type": "array",
"description": "Specifies a name to be displayed when the A Button is pressed on the pause menu.",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"Area": {
"$ref": "#/$defs/AreaID",
"description": "The area ID where this room is located."
},
"Room": {
"$ref": "#/$defs/TypeU8",
"description": "The room ID."
},
"Name": {
"type": "string",
"description": "Specifies what text should appear for this room. Two lines are available, with an absolute maximum of 56 characters per line, if all characters used are small. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, will display 'Unknown Room'.",
"maxLength": 112
}
}
},
"required": ["Area", "Room", "Name"]
}
}
},
"required": [
"SeedHash",
Expand Down Expand Up @@ -903,16 +903,27 @@
"Spanish"
]
},
"ItemMessages": {
"MessageLanguages": {
"type": "object",
"propertyNames": {
"$ref": "#/$defs/ValidLanguages"
},
"required": ["English"],
"additionalProperties": {
"type": "string",
"description": "Specifies what text should appear on acquiring this item. Two lines are available, with an absolute maximum of 56 characters per line, if all characters used are small. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, a message based on the Item will be shown. If a language is not provided, it will use the provided English message.",
"maxLength": 112
"description": "Specifies what text should appear for a 2 line message. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, a message based on the Item will be shown. If a language is not provided, it will use the provided English message."
}
},
"ItemMessages": {
"type": "object",
"properties": {
"Languages": {
"$ref": "#/$defs/MessageLanguages"
},
"Centered": {
"type": "boolean",
"default": true
}
}
},
"BlockLayer": {
Expand Down
2 changes: 1 addition & 1 deletion src/mars_patcher/item_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ def write_items(self) -> None:
item_addr = MINOR_LOCS_ARRAY + ((room_entry_index + item_index) * MINOR_LOC_SIZE)
read_area = rom.read_8(item_addr)
read_room = rom.read_8(item_addr + 1)
_read_room_index = rom.read_8(item_addr + 2)
read_block_x = rom.read_8(item_addr + 3)
read_block_y = rom.read_8(item_addr + 4)

Expand Down Expand Up @@ -180,6 +179,7 @@ def write_custom_message(
if lang in messages.item_messages
else messages.item_messages[Language.ENGLISH]
),
centered=messages.centered,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why isnt this just messages.centered?

Also pretty sure this should be messages.get("Centered", False)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this hasn't been addressed yet fwiw.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Because the positional argument there is max_width, which is excluded
  2. I accidentally set the default to false in the schema, which is fixed now

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was mixing stuff up for both, oops.

)
message_addr = rom.reserve_free_space(len(encoded_text) * 2)
rom.write_ptr(message_table_addrs[lang] + (4 * custom_message_id), message_addr)
Expand Down
16 changes: 10 additions & 6 deletions src/mars_patcher/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
KEY_AREA,
KEY_BLOCK_X,
KEY_BLOCK_Y,
KEY_CENTERED,
KEY_HIDDEN,
KEY_ITEM,
KEY_ITEM_MESSAGES,
KEY_ITEM_SPRITE,
KEY_LANGUAGES,
KEY_MAJOR_LOCS,
KEY_MINOR_LOCS,
KEY_ORIGINAL,
Expand All @@ -27,7 +29,7 @@
from mars_patcher.text import Language

if TYPE_CHECKING:
from mars_patcher.auto_generated_types import MarsschemaLocations
from mars_patcher.auto_generated_types import Itemmessages, MarsschemaLocations


class Location:
Expand Down Expand Up @@ -95,16 +97,18 @@ class ItemMessages:
"Spanish": Language.SPANISH,
}

def __init__(self, item_messages: dict[Language, str]):
def __init__(self, item_messages: dict[Language, str], centered: bool):
self.item_messages = item_messages
self.centered = centered

@classmethod
def from_json(cls, data: dict) -> ItemMessages:
def from_json(cls, data: Itemmessages) -> ItemMessages:
item_messages: dict[Language, str] = {}
for lang, message in data.items():
lang = cls.LANG_ENUMS[lang]
for lang_name, message in data[KEY_LANGUAGES].items():
lang = cls.LANG_ENUMS[lang_name]
item_messages[lang] = message
return cls(item_messages)
centered = data.get(KEY_CENTERED, True)
return cls(item_messages, centered)


class LocationSettings:
Expand Down
51 changes: 44 additions & 7 deletions src/mars_patcher/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@
from mars_patcher.data import get_data_path
from mars_patcher.rom import Region, Rom

SPACE = 0x40
SPACE_CHAR = 0x40
SPACE_TAG = 0x8000
NEXT = 0xFD00
NEWLINE = 0xFE00
END = 0xFF00
VALUE_MARKUP_TAG = {
"SPACE": (0x8000, 8),
"SPACE": (SPACE_TAG, 8),
"COLOR": (0x8100, 8),
"SPEED": (0x8200, 8),
"INDENT": (0x8300, 8),
"PLAY_SOUND": (0x9000, 12),
"STOP_SOUND": (0xA000, 12),
"WAIT": (0xE100, 8),
}
BREAKING_CHARS = {SPACE, NEXT, NEWLINE}
BREAKING_CHARS = {SPACE_CHAR, NEXT, NEWLINE}
NEWLINE_CHARS = {NEXT, NEWLINE}

KANJI_START = 0x4A0
KANJI_WIDTH = 10
Expand Down Expand Up @@ -77,11 +79,43 @@ def parse_value_markup_tag(tag: str) -> int | None:
raise ValueError(f"Invalid value markup tag '{tag}'")


def get_char_width(rom: Rom, char_widths_addr: int, char_val: int) -> int:
if char_val >= 0x8000:
return 0
if char_val < KANJI_START:
return rom.read_8(char_widths_addr + char_val)
return KANJI_WIDTH


def center_text(rom: Rom, char_vals: list[int], max_width: int) -> None:
char_widths_addr = character_widths(rom)
line_start = 0
line_width = 0
index = 0
while index < len(char_vals):
char_val = char_vals[index]
index += 1
if char_val in NEWLINE_CHARS or index == len(char_vals):
if line_width > 0:
assert line_width <= max_width
space_val = SPACE_TAG + (max_width - line_width) // 2
char_vals.insert(line_start, space_val)
index += 1
line_width = 0
line_start = index
else:
line_width += get_char_width(rom, char_widths_addr, char_val)


def encode_text(
rom: Rom, message_type: MessageType, string: str, max_width: int = MAX_LINE_WIDTH
rom: Rom,
message_type: MessageType,
string: str,
max_width: int = MAX_LINE_WIDTH,
centered: bool = False,
) -> list[int]:
char_map = get_char_map(rom.region)
char_widths = character_widths(rom)
char_widths_addr = character_widths(rom)
text: list[int] = []
line_width = 0
line_number = 0
Expand Down Expand Up @@ -124,14 +158,14 @@ def encode_text(
escaped = False

char_val = char_map[char]
char_width = rom.read_8(char_widths + char_val) if char_val < KANJI_START else KANJI_WIDTH
char_width = get_char_width(rom, char_widths_addr, char_val)
line_width += char_width
width_since_break += char_width

if char_val in BREAKING_CHARS:
prev_break = len(text)
width_since_break = 0
if char_val == NEXT or char_val == NEWLINE:
if char_val in NEWLINE_CHARS:
line_width = 0
line_number += 1

Expand Down Expand Up @@ -174,6 +208,9 @@ def encode_text(
if message_type == MessageType.ONE_LINE and (NEXT in text or NEWLINE in text):
raise ValueError(f'String cannot have newlines:\n"{string}"')

if centered:
center_text(rom, text, max_width)

if message_type == MessageType.TWO_LINE and NEWLINE not in text:
# Two line messages MUST have two lines, append NEWLINE if none exists
text.append(NEWLINE)
Expand Down