2022-10-20 17:41:11 +00:00
|
|
|
import os
|
2024-03-03 21:10:14 +00:00
|
|
|
from typing import Any, BinaryIO, Optional, cast
|
|
|
|
import zipfile
|
|
|
|
|
|
|
|
from typing_extensions import override
|
|
|
|
|
|
|
|
import Utils
|
2024-03-14 21:29:29 +00:00
|
|
|
from worlds.Files import APAutoPatchInterface
|
2024-03-03 21:10:14 +00:00
|
|
|
|
|
|
|
from zilliandomizer.patch import Patcher
|
|
|
|
|
|
|
|
from .gen_data import GenData
|
2022-10-20 17:41:11 +00:00
|
|
|
|
|
|
|
USHASH = 'd4bf9e7bcf9a48da53785d2ae7bc4270'
|
|
|
|
|
|
|
|
|
2024-03-14 21:29:29 +00:00
|
|
|
class ZillionPatch(APAutoPatchInterface):
|
2022-10-20 17:41:11 +00:00
|
|
|
hash = USHASH
|
|
|
|
game = "Zillion"
|
|
|
|
patch_file_ending = ".apzl"
|
|
|
|
result_file_ending = ".sms"
|
|
|
|
|
2024-03-03 21:10:14 +00:00
|
|
|
gen_data_str: str
|
|
|
|
""" JSON encoded """
|
|
|
|
|
|
|
|
def __init__(self, *args: Any, gen_data_str: str = "", **kwargs: Any) -> None:
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.gen_data_str = gen_data_str
|
|
|
|
|
2022-10-20 17:41:11 +00:00
|
|
|
@classmethod
|
|
|
|
def get_source_data(cls) -> bytes:
|
|
|
|
with open(get_base_rom_path(), "rb") as stream:
|
|
|
|
return read_rom(stream)
|
|
|
|
|
2024-03-03 21:10:14 +00:00
|
|
|
@override
|
|
|
|
def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
|
|
|
|
super().write_contents(opened_zipfile)
|
|
|
|
opened_zipfile.writestr("gen_data.json",
|
|
|
|
self.gen_data_str,
|
|
|
|
compress_type=zipfile.ZIP_DEFLATED)
|
|
|
|
|
|
|
|
@override
|
|
|
|
def read_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
|
|
|
|
super().read_contents(opened_zipfile)
|
|
|
|
self.gen_data_str = opened_zipfile.read("gen_data.json").decode()
|
|
|
|
|
|
|
|
def patch(self, target: str) -> None:
|
|
|
|
self.read()
|
|
|
|
write_rom_from_gen_data(self.gen_data_str, target)
|
|
|
|
|
2022-10-20 17:41:11 +00:00
|
|
|
|
|
|
|
def get_base_rom_path(file_name: Optional[str] = None) -> str:
|
|
|
|
options = Utils.get_options()
|
|
|
|
if not file_name:
|
|
|
|
file_name = cast(str, options["zillion_options"]["rom_file"])
|
|
|
|
if not os.path.exists(file_name):
|
|
|
|
file_name = Utils.user_path(file_name)
|
|
|
|
return file_name
|
|
|
|
|
|
|
|
|
|
|
|
def read_rom(stream: BinaryIO) -> bytes:
|
|
|
|
""" reads rom into bytearray """
|
|
|
|
data = stream.read()
|
|
|
|
# I'm not aware of any sms header.
|
|
|
|
return data
|
2024-03-03 21:10:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
def write_rom_from_gen_data(gen_data_str: str, output_rom_file_name: str) -> None:
|
|
|
|
""" take the output of `GenData.to_json`, and create rom from it """
|
|
|
|
gen_data = GenData.from_json(gen_data_str)
|
|
|
|
|
|
|
|
base_rom_path = get_base_rom_path()
|
|
|
|
zz_patcher = Patcher(base_rom_path)
|
|
|
|
|
|
|
|
zz_patcher.write_locations(gen_data.zz_game.regions, gen_data.zz_game.char_order[0])
|
|
|
|
zz_patcher.all_fixes_and_options(gen_data.zz_game)
|
|
|
|
zz_patcher.set_external_item_interface(gen_data.zz_game.char_order[0], gen_data.zz_game.options.max_level)
|
|
|
|
zz_patcher.set_multiworld_items(gen_data.multi_items)
|
|
|
|
zz_patcher.set_rom_to_ram_data(gen_data.game_id)
|
|
|
|
|
|
|
|
patched_rom_bytes = zz_patcher.get_patched_bytes()
|
|
|
|
with open(output_rom_file_name, "wb") as binary_file:
|
|
|
|
binary_file.write(patched_rom_bytes)
|