mirror of
https://github.com/BrianPugh/game-and-watch-patch.git
synced 2025-12-16 07:16:26 +01:00
130 lines
3.1 KiB
Python
130 lines
3.1 KiB
Python
from math import ceil
|
|
|
|
from colorama import Fore, Style
|
|
|
|
|
|
def printi(msg, *args):
|
|
print(Fore.MAGENTA + msg + Style.RESET_ALL, *args)
|
|
|
|
|
|
def printe(msg, *args):
|
|
print(Fore.YELLOW + msg + Style.RESET_ALL, *args)
|
|
|
|
|
|
def printd(msg, *args):
|
|
print(Fore.BLUE + msg + Style.RESET_ALL, *args)
|
|
|
|
|
|
def round_down_word(val):
|
|
return (val // 4) * 4
|
|
|
|
|
|
def round_up_word(val):
|
|
return ceil(val / 4) * 4
|
|
|
|
|
|
def round_down_page(val):
|
|
return (val // 4096) * 4096
|
|
|
|
|
|
def round_up_page(val):
|
|
return ceil(val / 4096) * 4096
|
|
|
|
|
|
def seconds_to_frames(seconds):
|
|
return int(round(60 * seconds))
|
|
|
|
|
|
def fds_crc(data, checksum=0x8000):
|
|
"""
|
|
Do not include any existing checksum, not even the blank checksums 00 00 or FF FF.
|
|
The formula will automatically count 2 0x00 bytes without the programmer adding them manually.
|
|
Also, do not include the gap terminator (0x80) in the data.
|
|
If you wish to do so, change sum to 0x0000.
|
|
"""
|
|
size = len(data)
|
|
for i in range(size + 2):
|
|
if i < size:
|
|
byte = data[i]
|
|
else:
|
|
byte = 0x00
|
|
|
|
for bit_index in range(8):
|
|
bit = (byte >> bit_index) & 0x1
|
|
carry = checksum & 0x1
|
|
checksum = (checksum >> 1) | (bit << 15)
|
|
if carry:
|
|
checksum ^= 0x8408
|
|
return checksum.to_bytes(2, "little")
|
|
|
|
|
|
def fds_remove_crc_gaps(rom):
|
|
"""Remove each block's CRC padding so it can be played by FDS
|
|
https://wiki.nesdev.org/w/index.php/FDS_disk_format
|
|
"""
|
|
offset = 0x0
|
|
|
|
def get_block(size, crc_gap=2):
|
|
nonlocal offset
|
|
block = rom[offset : offset + size]
|
|
offset += size + crc_gap
|
|
|
|
return block
|
|
|
|
disk_info_block = get_block(0x38)
|
|
|
|
file_amount_block = get_block(0x2)
|
|
assert file_amount_block[0] == 0x02
|
|
n_files = file_amount_block[1]
|
|
|
|
blocks = [disk_info_block, file_amount_block]
|
|
for i in range(n_files):
|
|
file_header_block = get_block(0x10)
|
|
assert file_header_block[0] == 3
|
|
blocks.append(file_header_block)
|
|
|
|
file_size = int.from_bytes(file_header_block[13 : 13 + 2], "little")
|
|
file_data_block = get_block(file_size + 1)
|
|
blocks.append(file_data_block)
|
|
|
|
out = b"".join(blocks)
|
|
|
|
# Zero pad to be 65500 bytes long
|
|
padding = b"\x00" * (65500 - len(out))
|
|
out += padding
|
|
|
|
return out
|
|
|
|
|
|
def fds_add_crc_gaps(rom):
|
|
"""Add CRC gaps"""
|
|
offset = 0x0
|
|
|
|
def get_block(size):
|
|
nonlocal offset
|
|
block = bytes(rom[offset : offset + size])
|
|
offset += size
|
|
crc = fds_crc(block)
|
|
return block + crc
|
|
|
|
disk_info_block = get_block(0x38)
|
|
|
|
file_amount_block = get_block(0x2)
|
|
assert file_amount_block[0] == 0x02
|
|
n_files = file_amount_block[1]
|
|
|
|
blocks = [disk_info_block, file_amount_block]
|
|
|
|
for i in range(n_files):
|
|
file_header_block = get_block(0x10)
|
|
assert file_header_block[0] == 3
|
|
blocks.append(file_header_block)
|
|
|
|
file_size = int.from_bytes(file_header_block[13 : 13 + 2], "little")
|
|
file_data_block = get_block(file_size + 1)
|
|
blocks.append(file_data_block)
|
|
|
|
out = b"".join(blocks)
|
|
|
|
return out
|