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
28 changes: 28 additions & 0 deletions src/mars_patcher/constants/game_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ def tileset_count(rom: Rom) -> int:
raise ValueError(rom.game)


def anim_tileset_entries(rom: Rom) -> int:
"""Returns the address of the animated tileset entries."""
if rom.game == Game.MF:
raise NotImplementedError()
elif rom.game == Game.ZM:
return rom.read_ptr(ReservedPointersZM.ANIM_TILESET_ENTRIES_PTR.value)

raise ValueError("Rom has unknown game loaded.")


def anim_tileset_count(rom: Rom) -> int:
"""Returns the number of animated tilesets in the game."""
if rom.game == Game.MF:
return 0xE
elif rom.game == Game.ZM:
return 0x8
raise ValueError(rom.game)


def area_doors_ptrs(rom: Rom) -> int:
"""Returns the address of the area doors pointers."""
if rom.game == Game.MF:
Expand Down Expand Up @@ -89,6 +108,15 @@ def area_connections_count(rom: Rom) -> int:
raise ValueError("Rom has unknown game loaded.")


def anim_graphics_count(rom: Rom) -> int:
"""Returns the number of animated graphics in the game."""
if rom.game == Game.MF:
return 0x47
elif rom.game == Game.ZM:
return 0x26
raise ValueError(rom.game, rom.region)


def anim_palette_entries(rom: Rom) -> int:
"""Returns the address of the animated palette entries."""
if rom.game == Game.MF:
Expand Down
23 changes: 23 additions & 0 deletions src/mars_patcher/convert_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from mars_patcher.rom import ROM_OFFSET


def u8_to_u16(data: bytes | list[int]) -> list[int]:
"""Converts a bytes object or list of 8-bit integers to a list of 16-bit integers."""
assert len(data) % 2 == 0, "Data length must be a multiple of 2"
return [data[i] | (data[i + 1] << 8) for i in range(0, len(data), 2)]


def u16_to_u8(data: list[int]) -> bytes:
"""Converts a list of 16-bit integers to a bytes object of 8-bit integers."""
output = bytearray()
for val in data:
output.append(val & 0xFF)
output.append(val >> 8)
return output


def ptr_to_u8(val: int) -> bytes:
"""Converts a single pointer to a bytes object of 8-bit integers."""
assert val < ROM_OFFSET, f"Pointer should be less than {ROM_OFFSET:X} but is {val:X}"
val += ROM_OFFSET
return bytes([val & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF, val >> 24])
2 changes: 1 addition & 1 deletion src/mars_patcher/mf/item_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def write_items(self) -> None:
if not min_loc.hidden:
# Get tilemap
tileset = Tileset(rom, room.tileset())
addr = tileset.rle_tilemap_addr()
addr = tileset.tilemap_addr()
# Find tank in tilemap
addr += 2 + (TANK_BG1_START * 8)
tile = TANK_TILE[tank_slot]
Expand Down
9 changes: 3 additions & 6 deletions src/mars_patcher/palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import random

from mars_patcher.color_spaces import HsvColor, OklabColor, RgbBitSize, RgbColor
from mars_patcher.convert_array import u16_to_u8
from mars_patcher.rom import Rom

HUE_VARIATION_RANGE = 180.0
Expand Down Expand Up @@ -80,12 +81,8 @@ def rows(self) -> int:
return len(self.colors) // 16

def byte_data(self) -> bytes:
arr = bytearray()
for color in self.colors:
val = color.rgb_15()
arr.append(val & 0xFF)
arr.append(val >> 8)
return bytes(arr)
values = [c.rgb_15() for c in self.colors]
return u16_to_u8(values)

def write(self, rom: Rom, addr: int) -> None:
data = self.byte_data()
Expand Down
25 changes: 14 additions & 11 deletions src/mars_patcher/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from functools import cache

from mars_patcher.constants.game_data import character_widths
from mars_patcher.convert_array import u16_to_u8
from mars_patcher.mf.constants.game_data import file_screen_text_ptrs
from mars_patcher.mf.data import get_data_path
from mars_patcher.rom import Region, Rom
from mars_patcher.mf.data import get_data_path as get_data_path_mf
from mars_patcher.rom import Rom
from mars_patcher.zm.data import get_data_path as get_data_path_zm

SPACE_CHAR = 0x40
SPACE_TAG = 0x8000
Expand Down Expand Up @@ -56,13 +58,18 @@ class MessageType(Enum):


@cache
def get_char_map(region: Region) -> dict[str, int]:
path = get_data_path("char_map_mf.json")
def get_char_map(rom: Rom) -> dict[str, int]:
if rom.is_mf():
path = get_data_path_mf("char_map_mf.json")
elif rom.is_zm():
path = get_data_path_zm("char_map_zm.json")
else:
raise ValueError(rom.game)
with open(path, encoding="utf-8") as f:
sections = json.load(f)
char_map: dict[str, int] = {}
for section in sections:
if region.name in section["regions"]:
if rom.region.name in section["regions"]:
char_map.update(section["chars"])
char_map["\n"] = NEWLINE
return char_map
Expand Down Expand Up @@ -118,7 +125,7 @@ def encode_text(
max_width: int = MAX_LINE_WIDTH,
centered: bool = False,
) -> bytes:
char_map = get_char_map(rom.region)
char_map = get_char_map(rom)
char_widths_addr = character_widths(rom)
text: list[int] = []
line_width = 0
Expand Down Expand Up @@ -237,11 +244,7 @@ def encode_text(

text.append(END)

text_bytes = bytearray()
for val in text:
text_bytes.append(val & 0xFF)
text_bytes.append(val >> 8)
return bytes(text_bytes)
return u16_to_u8(text)


def write_seed_hash(rom: Rom, seed_hash: str) -> None:
Expand Down
44 changes: 44 additions & 0 deletions src/mars_patcher/tilemap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from mars_patcher.compress import comp_lz77, decomp_lz77
from mars_patcher.convert_array import u8_to_u16, u16_to_u8
from mars_patcher.rom import Rom


class Tilemap:
def __init__(self, rom: Rom, ptr: int, compressed: bool):
self.rom = rom
self.pointer = ptr
self.compressed = compressed
# Get data
addr = rom.read_ptr(ptr)
self.data: list[int] = []
if compressed:
data, self.data_size = decomp_lz77(rom.data, addr)
self.data = u8_to_u16(data)
else:
addr += 2
while True:
val = rom.read_16(addr)
if val == 0:
if len(self.data) % 4 != 0:
raise ValueError("Tilemap length should be a multiple of 4")
break
if len(self.data) >= 1024 * 4:
raise ValueError("Tilemap is too long")
self.data.append(val)
addr += 2
self.data_size = len(self.data) * 2 + 4

def byte_data(self) -> bytes:
if self.compressed:
data = comp_lz77(u16_to_u8(self.data))
return bytes(data)
else:
return bytes([2, 0]) + u16_to_u8(self.data) + bytes([0, 0])

def write(self, copy: bool) -> None:
data = self.byte_data()
if copy:
self.rom.write_data_with_pointers(data, [self.pointer])
else:
addr = self.rom.read_ptr(self.pointer)
self.rom.write_repointable_data(addr, self.data_size, data, [self.pointer])
41 changes: 37 additions & 4 deletions src/mars_patcher/tileset.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
from mars_patcher.constants.game_data import tileset_entries
from mars_patcher.constants.game_data import anim_tileset_entries, tileset_entries
from mars_patcher.rom import Rom

TILESET_SIZE = 0x14
ANIM_TILESET_SIZE = 0x30


class Tileset:
def __init__(self, rom: Rom, id: int):
self.rom = rom
self.addr = tileset_entries(rom) + id * 0x14
self.addr = tileset_entries(rom) + id * TILESET_SIZE

def block_bg_gfx_ptr(self) -> int:
return self.addr

def block_bg_gfx_addr(self) -> int:
return self.rom.read_ptr(self.block_bg_gfx_ptr())

def palette_ptr(self) -> int:
return self.addr + 4

def palette_addr(self) -> int:
return self.rom.read_ptr(self.palette_ptr())

def tiled_bg_gfx_ptr(self) -> int:
return self.addr + 8

def tiled_bg_gfx_addr(self) -> int:
return self.rom.read_ptr(self.tiled_bg_gfx_ptr())

def tilemap_ptr(self) -> int:
return self.addr + 0xC

def tilemap_addr(self) -> int:
return self.rom.read_ptr(self.tilemap_ptr())

def anim_tileset(self) -> int:
return self.rom.read_8(self.addr + 0x10)

def anim_tileset_addr(self) -> int:
return anim_tileset_entries(self.rom) + self.anim_tileset() * ANIM_TILESET_SIZE

def rle_tilemap_addr(self) -> int:
return self.rom.read_ptr(self.addr + 0xC)
def anim_palette(self) -> int:
return self.rom.read_8(self.addr + 0x11)
30 changes: 15 additions & 15 deletions src/mars_patcher/zm/auto_generated_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@
'TOURIAN_TO_CRATERIA'
]
ValidLanguages = typ.Literal[
'JAPANESE_KANJI',
'JAPANESE_HIRAGANA',
'ENGLISH',
'GERMAN',
'FRENCH',
'ITALIAN',
'SPANISH'
'JapaneseKanji',
'JapaneseHiragana',
'English',
'German',
'French',
'Italian',
'Spanish'
]
Validmusictracks = typ.Literal[
'BRINSTAR',
Expand Down Expand Up @@ -213,15 +213,15 @@
MessageLanguages: typ.TypeAlias = dict[ValidLanguages, str]

class ItemMessages(typ.TypedDict, total=False):
kind: typ.Required[ItemMessagesKind]
languages: MessageLanguages
centered: bool = True
message_id: typ.Annotated[int, '0 <= value <= 56']
Kind: typ.Required[ItemMessagesKind]
Languages: MessageLanguages
Centered: bool = True
MessageID: typ.Annotated[int, '0 <= value <= 56']
"""The Message ID, will display one of the predefined messages in the ROM"""

ItemMessagesKind = typ.Literal[
'CUSTOM_MESSAGE',
'MESSAGE_ID'
'CustomMessage',
'MessageID'
]
Jingle = typ.Literal[
'DEFAULT',
Expand Down Expand Up @@ -266,7 +266,7 @@ class MarsschemazmLocationsMajorLocationsItem(typ.TypedDict, total=False):
item_sprite: ValidItemSprites
"""Valid graphics for item tanks/sprites."""

item_messages: ItemMessages
ItemMessages: ItemMessages
jingle: Jingle
"""The sound that plays when an item is collected"""

Expand All @@ -293,7 +293,7 @@ class MarsschemazmLocationsMinorLocationsItem(typ.TypedDict):
item_sprite: typ.NotRequired[ValidItemSprites]
"""Valid graphics for item tanks/sprites."""

item_messages: typ.NotRequired[ItemMessages]
ItemMessages: typ.NotRequired[ItemMessages]
jingle: typ.NotRequired[Jingle]
"""The sound that plays when an item is collected"""

Expand Down
4 changes: 0 additions & 4 deletions src/mars_patcher/zm/constants/game_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
from mars_patcher.zm.constants.reserved_space import ReservedPointersZM


def tileset_tilemap_sizes_addr(rom: Rom) -> int:
return rom.read_ptr(ReservedPointersZM.TILESET_TILEMAP_SIZES_PTR.value)


def chozo_statue_targets_addr(rom: Rom) -> int:
return rom.read_ptr(ReservedPointersZM.CHOZO_STATUE_TARGETS_PTR.value)

Expand Down
Loading