forked from mirror/Archipelago
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
99 lines
3.6 KiB
Python
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)
|