Files
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

99 lines
3.6 KiB
Python

from collections.abc import Collection
from typing import Optional
from .z80asm.Assembler import GameboyAddress
from .z80asm.Util import hex_str
class RomData:
buffer: bytearray
def __init__(self, file: bytes, name: Optional[str] = None) -> None:
self.file = bytearray(file)
self.name = name
def read_bit(self, address: int, bit_number: int) -> bool:
bitflag = (1 << bit_number)
return (self.buffer[address] & bitflag) != 0
def read_byte(self, address: int) -> int:
return self.file[address]
def read_bytes(self, start_address: int, length: int) -> bytearray:
return self.file[start_address:start_address + length]
def read_word(self, address: int):
word_bytes = self.read_bytes(address, 2)
return (word_bytes[1] * 0x100) + word_bytes[0]
def write_byte(self, address: int, value: int) -> None:
self.file[address] = value
def write_bytes(self, start_address: int, values: Collection[int]) -> None:
self.file[start_address:start_address + len(values)] = values
def write_word(self, address: int, value: int) -> None:
value = value & 0xFFFF
self.write_bytes(address, [value & 0xFF, (value >> 8) & 0xFF])
def write_word_be(self, address: int, value: int) -> None:
value = value & 0xFFFF
self.write_bytes(address, [(value >> 8) & 0xFF, value & 0xFF])
def add_bank(self, fill: int) -> None:
self.file.extend([fill] * 0x4000)
def update_header_checksum(self) -> None:
"""
Updates the 8-bit checksum for ROM data located in the rom header.
"""
result = -0x19
for b in self.read_bytes(0x134, 0x19):
result -= int(b)
self.write_byte(0x14D, result & 0xFF)
def update_checksum(self, address: int):
"""
Updates the 16-bit checksum for ROM data located in the rom header.
This is calculated by summing the non-global-checksum bytes in the rom.
This must not be confused with the header checksum, which is the byte before.
"""
result = 0
for b in self.read_bytes(0x0, address):
result += b
for b in self.read_bytes(address + 2, 0xffffff):
result += b
result &= 0xffff
self.write_word_be(address, result & 0xffff)
def update_rom_size(self) -> None:
"""
Updates the ROM size for ROM data located in the rom header.
"""
if len(self.file) == 0x100000:
self.write_byte(0x148, 0x05)
elif len(self.file) == 0x200000:
self.write_byte(0x148, 0x06)
else:
raise ValueError(f"Invalid ROM size: {hex(len(self.file))}")
def get_chest_addr(self, group_and_room: int, bank: int, table_addr: int) -> int:
"""
Return the address where to edit item ID and sub-ID to modify the contents
of the chest contained in given room of given group
The required bank and address parameters point to chestDataGroupTable
"""
base_addr = GameboyAddress(bank, table_addr).address_in_rom()
room = group_and_room & 0xFF
group = group_and_room >> 8
current_addr = GameboyAddress(bank, self.read_word(base_addr + (group * 2))).address_in_rom()
while self.read_byte(current_addr) != 0xff:
chest_room = self.read_byte(current_addr + 1)
if chest_room == room:
return current_addr + 2
current_addr += 4
raise Exception(f"Unknown chest in room {group}|{hex_str(room)}")
def output(self) -> bytes:
return bytes(self.file)