diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..5521872 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,7 @@ +changelog: + exclude: + labels: + - ignore-for-release + authors: + - pre-commit-ci + - dependabot diff --git a/.github/workflows/dependency.yml b/.github/workflows/dependency.yml new file mode 100644 index 0000000..f83a62f --- /dev/null +++ b/.github/workflows/dependency.yml @@ -0,0 +1,20 @@ +name: Dependency auto-merge +on: pull_request_target + +permissions: + contents: write + pull-requests: write + +jobs: + dependency: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'pre-commit-ci[bot]' }} + steps: + - name: Enable auto-merge for Dependency PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Automatically approve the PR + uses: hmarr/auto-approve-action@v4 \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 683444e..16932f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,4 +7,5 @@ repos: rev: v0.6.3 hooks: - id: ruff - args: [ --fix, --exit-non-zero-on-fix ] \ No newline at end of file + args: [ --fix, --exit-non-zero-on-fix ] + - id: ruff-format \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0cfaf26..6d4ea53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,8 +65,27 @@ filterwarnings = [ [tool.ruff] line-length = 120 -select = ["E", "F", "W", "C90", "I", "UP"] +target-version = "py310" src = ["src"] -# Version to target for generated code. -target-version = "py310" +[tool.ruff.lint] +select = [ + "E", "F", "W", "C90", "I", "UP", "C4", + "RSE", + "TCH", + "PTH", + "COM818", "COM819", + "ISC", + "PIE", + "PT", + + "PLC", + + "PLE", + "PLR0402", "PLR1701", "PLR1711", "PLR1722", "PLR0206", + "PLR0133", "PLR0124", + "PLW", +] +extend-ignore = [ + "ISC001", # may cause conflicts with formatter +] diff --git a/src/open_prime_rando/__pyinstaller/__init__.py b/src/open_prime_rando/__pyinstaller/__init__.py index b232646..f0af04f 100644 --- a/src/open_prime_rando/__pyinstaller/__init__.py +++ b/src/open_prime_rando/__pyinstaller/__init__.py @@ -1,4 +1,5 @@ import os +from pathlib import Path # Functions # ========= @@ -13,5 +14,6 @@ # This function returns a list containing only the path to this # directory, which is the location of these hooks. -def get_hook_dirs(): - return [os.path.dirname(__file__)] + +def get_hook_dirs() -> list[str]: + return [os.fspath(Path(__file__).parent)] diff --git a/src/open_prime_rando/__pyinstaller/hook-open_prime_rando.py b/src/open_prime_rando/__pyinstaller/hook-open_prime_rando.py index 624512d..4db2870 100644 --- a/src/open_prime_rando/__pyinstaller/hook-open_prime_rando.py +++ b/src/open_prime_rando/__pyinstaller/hook-open_prime_rando.py @@ -4,7 +4,7 @@ # https://pyinstaller.readthedocs.io/en/stable/hooks.html#provide-hooks-with-package -datas = collect_data_files('open_prime_rando', excludes=['__pyinstaller']) +datas = collect_data_files("open_prime_rando", excludes=["__pyinstaller"]) hiddenimports = [ f"open_prime_rando.echoes.asset_ids{module_name}" for module_name in open_prime_rando.echoes.asset_ids.world._DEDICATED_FILES.values() diff --git a/src/open_prime_rando/cli.py b/src/open_prime_rando/cli.py index 7d59c90..4a88f07 100644 --- a/src/open_prime_rando/cli.py +++ b/src/open_prime_rando/cli.py @@ -17,45 +17,45 @@ def create_parser(): parser = argparse.ArgumentParser() parser.add_argument("--game", required=True, choices=sorted(_game_to_patcher.keys())) input_group = parser.add_mutually_exclusive_group(required=True) - input_group.add_argument("--input-paks", type=Path, - help="Path to where the paks to randomize") - input_group.add_argument("--input-iso", type=Path, - help="Path to a ISO to randomize") - parser.add_argument("--output-paks", required=True, type=Path, - help="Path to where the modified paks will be written to.") - parser.add_argument("--input-json", type=Path, required=True, - help="Path to the configuration json.") + input_group.add_argument("--input-paks", type=Path, help="Path to where the paks to randomize") + input_group.add_argument("--input-iso", type=Path, help="Path to a ISO to randomize") + parser.add_argument( + "--output-paks", required=True, type=Path, help="Path to where the modified paks will be written to." + ) + parser.add_argument("--input-json", type=Path, required=True, help="Path to the configuration json.") return parser def setup_logging(): handlers = { - 'default': { - 'level': 'DEBUG', - 'formatter': 'default', - 'class': 'logging.StreamHandler', - 'stream': 'ext://sys.stdout', # Default is stderr + "default": { + "level": "DEBUG", + "formatter": "default", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", # Default is stderr }, } - logging.config.dictConfig({ - 'version': 1, - 'formatters': { - 'default': { - 'format': '[%(asctime)s] [%(levelname)s] [%(name)s] %(funcName)s: %(message)s', - } - }, - 'handlers': handlers, - 'disable_existing_loggers': False, - 'loggers': { - 'default': { - 'level': 'DEBUG', + logging.config.dictConfig( + { + "version": 1, + "formatters": { + "default": { + "format": "[%(asctime)s] [%(levelname)s] [%(name)s] %(funcName)s: %(message)s", + } }, - }, - 'root': { - 'level': 'DEBUG', - 'handlers': list(handlers.keys()), - }, - }) + "handlers": handlers, + "disable_existing_loggers": False, + "loggers": { + "default": { + "level": "DEBUG", + }, + }, + "root": { + "level": "DEBUG", + "handlers": list(handlers.keys()), + }, + } + ) logging.info("Hello world.") diff --git a/src/open_prime_rando/dol_patching/all_prime_dol_patches.py b/src/open_prime_rando/dol_patching/all_prime_dol_patches.py index 2e60a89..2510a1e 100644 --- a/src/open_prime_rando/dol_patching/all_prime_dol_patches.py +++ b/src/open_prime_rando/dol_patching/all_prime_dol_patches.py @@ -43,6 +43,7 @@ class PowerupFunctionsAddresses: incr_pickup: Increments the given item amount decr_pickup: Decrements the given item amount """ + add_power_up: int incr_pickup: int decr_pickup: int @@ -75,7 +76,7 @@ def remote_execution_patch_start(game: Game) -> list[BaseInstruction]: ] elif game == Game.ECHOES: cutscene_check = [ - lwz(r0, 0x16fc, r31), + lwz(r0, 0x16FC, r31), ] else: raise ValueError(f"Unsupported game: {game}") @@ -87,15 +88,12 @@ def remote_execution_patch_start(game: Game) -> list[BaseInstruction]: stw(r0, _remote_execution_stack_size, r1), stmw(GeneralRegister(32 - _registers_to_save), _remote_execution_stack_size - 4 - _registers_to_save * 4, r1), or_(r31, r3, r3), - # return if no pending op lbz(r4, 0x2, r31), cmpwi(r4, 0x0), bne((len(return_code) + 1) * 4, relative=True), - # clean return if flag is not set *return_code, - # if camera count > 0 then we're in a cutscene so we return as we don't want # to handle receiving items during a cutscene *cutscene_check, @@ -109,12 +107,10 @@ def remote_execution_patch_start(game: Game) -> list[BaseInstruction]: return [ *intro, - # fetch the instructions again, since they're being overwritten externally # this clears Dolphin's JIT cache custom_ppc.load_current_address(r30, 8), li(r4, num_bytes_to_invalidate), - icbi(4, 30), # invalidate using r30 + r4 cmpwi(r4, 0x0), addi(r4, r4, -32), @@ -157,12 +153,10 @@ def call_display_hud_patch(patch_addresses: StringDisplayPatchAddresses) -> list stb(r6, 0x16, r1), # hint memo stb(r7, 0x17, r1), # fade in text stw(r9, 0x18, r1), # unk - # setup wstring addi(r3, r1, 0x1C), custom_ppc.load_unsigned_32bit(r4, patch_addresses.message_receiver_string_ref), bl(patch_addresses.wstring_constructor), - # r4 = CHUDMemoParms addi(r4, r1, 0x10), bl(patch_addresses.display_hud_memo), @@ -172,19 +166,22 @@ def call_display_hud_patch(patch_addresses: StringDisplayPatchAddresses) -> list def _load_player_state(game: Game, target_register: GeneralRegister, state_mgr: GeneralRegister = r31): if game == Game.PRIME: return [ - lwz(target_register, 0x8b8, state_mgr), + lwz(target_register, 0x8B8, state_mgr), lwz(target_register, 0, target_register), ] elif game == Game.ECHOES: return [ - lwz(target_register, 0x150c, state_mgr), + lwz(target_register, 0x150C, state_mgr), ] else: raise ValueError(f"Unsupported game {game}") def adjust_item_amount_and_capacity_patch( - patch_addresses: PowerupFunctionsAddresses, game: Game, item_id: int, delta: int, + patch_addresses: PowerupFunctionsAddresses, + game: Game, + item_id: int, + delta: int, ) -> list[BaseInstruction]: # r31 = CStateManager if game == Game.PRIME and item_id in ARTIFACT_ITEMS: @@ -203,7 +200,10 @@ def adjust_item_amount_and_capacity_patch( def increment_item_capacity_patch( - patch_addresses: PowerupFunctionsAddresses, game: Game, item_id: int, delta: int = 1, + patch_addresses: PowerupFunctionsAddresses, + game: Game, + item_id: int, + delta: int = 1, ) -> list[BaseInstruction]: return [ *_load_player_state(game, r3, r31), @@ -214,7 +214,10 @@ def increment_item_capacity_patch( def adjust_item_amount_patch( - patch_addresses: PowerupFunctionsAddresses, game: Game, item_id: int, delta: int, + patch_addresses: PowerupFunctionsAddresses, + game: Game, + item_id: int, + delta: int, ) -> list[BaseInstruction]: return [ *_load_player_state(game, r3, r31), @@ -236,9 +239,9 @@ def apply_remote_execution_patch(game: Game, patch_addresses: StringDisplayPatch dol_file.write_instructions(patch_addresses.update_hint_state, remote_execution_patch(game)) -def create_remote_execution_body(game: Game, - patch_addresses: StringDisplayPatchAddresses, - instructions: list[BaseInstruction]) -> tuple[int, bytes]: +def create_remote_execution_body( + game: Game, patch_addresses: StringDisplayPatchAddresses, instructions: list[BaseInstruction] +) -> tuple[int, bytes]: """ Return the address and the bytes for executing the given instructions via remote code execution. """ @@ -254,14 +257,15 @@ def create_remote_execution_body(game: Game, body_bytes = bytes(assembler.assemble_instructions(body_address, body_instructions)) if len(body_bytes) > _remote_execution_max_byte_count - remote_start_byte_count: - raise ValueError(f"Received {len(body_instructions)} instructions with total {len(body_bytes)} bytes, " - f"but limit is {_remote_execution_max_byte_count - remote_start_byte_count}.") + raise ValueError( + f"Received {len(body_instructions)} instructions with total {len(body_bytes)} bytes, " + f"but limit is {_remote_execution_max_byte_count - remote_start_byte_count}." + ) return body_address, body_bytes -def apply_energy_tank_capacity_patch(patch_addresses: HealthCapacityAddresses, energy_per_tank: int, - dol_file: DolFile): +def apply_energy_tank_capacity_patch(patch_addresses: HealthCapacityAddresses, energy_per_tank: int, dol_file: DolFile): """ Patches the base health capacity and the energy tank capacity with matching values. """ @@ -271,19 +275,20 @@ def apply_energy_tank_capacity_patch(patch_addresses: HealthCapacityAddresses, e dol_file.write(patch_addresses.energy_tank_capacity, struct.pack(">f", tank_capacity)) -def apply_reverse_energy_tank_heal_patch(sd2_base: int, - addresses: DangerousEnergyTankAddresses, - active: bool, - game: Game, - dol_file: DolFile, - ): +def apply_reverse_energy_tank_heal_patch( + sd2_base: int, + addresses: DangerousEnergyTankAddresses, + active: bool, + game: Game, + dol_file: DolFile, +): if game == Game.ECHOES: health_offset = 0x14 refill_item = 0x29 patch_offset = 0x90 elif game == Game.CORRUPTION: - health_offset = 0xc + health_offset = 0xC refill_item = 0x12 patch_offset = 0x138 diff --git a/src/open_prime_rando/dol_patching/corruption/dol_versions.py b/src/open_prime_rando/dol_patching/corruption/dol_versions.py index 2df45d7..7515f49 100644 --- a/src/open_prime_rando/dol_patching/corruption/dol_versions.py +++ b/src/open_prime_rando/dol_patching/corruption/dol_versions.py @@ -7,14 +7,13 @@ CorruptionDolVersion( game=Game.CORRUPTION, description="Wii NTSC", - build_string_address=0x805822b0, + build_string_address=0x805822B0, build_string=b"!#$MetroidBuildInfo!#$2007/07/27 13:13 Build v3.436 (MP3)", sda2_base=0x806869C0, - sda13_base=0x806801c0, - game_state_pointer=0x8067dc0c, - cplayer_vtable=0x80592c78, - cstate_manager_global=0x805c4f70, - + sda13_base=0x806801C0, + game_state_pointer=0x8067DC0C, + cplayer_vtable=0x80592C78, + cstate_manager_global=0x805C4F70, string_display=None, # string_display=StringDisplayPatchAddresses( # update_hint_state=None, @@ -24,45 +23,43 @@ # max_message_size=200, # ), powerup_functions=PowerupFunctionsAddresses( - add_power_up=0x8019111c, - incr_pickup=0x801913a0, - decr_pickup=0x8019151c, + add_power_up=0x8019111C, + incr_pickup=0x801913A0, + decr_pickup=0x8019151C, ), ), CorruptionDolVersion( game=Game.CORRUPTION, description="Wii PAL", - build_string_address=0x805843a8, + build_string_address=0x805843A8, build_string=b"!#$MetroidBuildInfo!#$2007/08/24 16:52 Build v3.453 (mp3)", - sda2_base=0x80688fe0, - sda13_base=0x806827c0, + sda2_base=0x80688FE0, + sda13_base=0x806827C0, game_state_pointer=0x80680234, cplayer_vtable=0x80595238, - cstate_manager_global=0x805c7570, - + cstate_manager_global=0x805C7570, string_display=None, powerup_functions=PowerupFunctionsAddresses( - add_power_up=0x80191e2c, - incr_pickup=0x801920b0, - decr_pickup=0x8019222c, + add_power_up=0x80191E2C, + incr_pickup=0x801920B0, + decr_pickup=0x8019222C, ), ), CorruptionDolVersion( game=Game.CORRUPTION, description="Wii NTSC-J", - build_string_address=0x80587c2c, + build_string_address=0x80587C2C, build_string=b"!#$MetroidBuildInfo!#$2007/11/12 14:15 Build v3.495 (jpn)", - sda2_base=0x8068c840, + sda2_base=0x8068C840, sda13_base=0x80686000, - game_state_pointer=0x80683a7c, + game_state_pointer=0x80683A7C, cplayer_vtable=0x80598650, - cstate_manager_global=0x805caa30, - + cstate_manager_global=0x805CAA30, string_display=None, powerup_functions=PowerupFunctionsAddresses( - add_power_up=0x801932d0, + add_power_up=0x801932D0, incr_pickup=0x80193554, - decr_pickup=0x801936d0, + decr_pickup=0x801936D0, ), ), ] diff --git a/src/open_prime_rando/dol_patching/dol_version.py b/src/open_prime_rando/dol_patching/dol_version.py index a4dafef..46e8c7a 100644 --- a/src/open_prime_rando/dol_patching/dol_version.py +++ b/src/open_prime_rando/dol_patching/dol_version.py @@ -20,7 +20,7 @@ def find_version_for_dol(dol_file: DolFile, all_versions: Iterable[DolVersion]) with dol_file: for version in all_versions: build_string = dol_file.read(version.build_string_address, len(version.build_string)) - if (build_string[:6], build_string[6 + 16:]) == (version.build_string[:6], version.build_string[6 + 16:]): + if (build_string[:6], build_string[6 + 16 :]) == (version.build_string[:6], version.build_string[6 + 16 :]): return version raise RuntimeError("Unsupported game version") diff --git a/src/open_prime_rando/dol_patching/echoes/dol_patcher.py b/src/open_prime_rando/dol_patching/echoes/dol_patcher.py index 45dec5f..7e834e2 100644 --- a/src/open_prime_rando/dol_patching/echoes/dol_patcher.py +++ b/src/open_prime_rando/dol_patching/echoes/dol_patcher.py @@ -44,27 +44,27 @@ def apply_patches(dol_file: DolFile, patches_data: EchoesDolPatchesData): with dol_file: all_prime_dol_patches.apply_build_info_patch(dol_file, patches_data.world_uuid, version) all_prime_dol_patches.apply_remote_execution_patch(version.game, version.string_display, dol_file) - all_prime_dol_patches.apply_energy_tank_capacity_patch(version.health_capacity, patches_data.energy_per_tank, - dol_file) - all_prime_dol_patches.apply_reverse_energy_tank_heal_patch(version.sda2_base, version.dangerous_energy_tank, - patches_data.dangerous_energy_tank, - version.game, dol_file) + all_prime_dol_patches.apply_energy_tank_capacity_patch( + version.health_capacity, patches_data.energy_per_tank, dol_file + ) + all_prime_dol_patches.apply_reverse_energy_tank_heal_patch( + version.sda2_base, version.dangerous_energy_tank, patches_data.dangerous_energy_tank, version.game, dol_file + ) dol_patches.apply_fixes(version, dol_file) dol_patches.change_powerup_should_persist( - version, dol_file, - ["Double Damage", "Unlimited Missiles", "Unlimited Beam Ammo"] + version, dol_file, ["Double Damage", "Unlimited Missiles", "Unlimited Beam Ammo"] ) dol_patches.apply_unvisited_room_names(version, dol_file, patches_data.unvisited_room_names) dol_patches.apply_teleporter_sounds(version, dol_file, patches_data.teleporter_sounds) - dol_patches.apply_game_options_patch(version.game_options_constructor_address, - patches_data.user_preferences, dol_file) - dol_patches.apply_beam_cost_patch(version.beam_cost_addresses, patches_data.beam_configurations, - dol_file) - dol_patches.apply_safe_zone_heal_patch(version.safe_zone, version.sda2_base, - patches_data.safe_zone_heal_per_second, dol_file) - dol_patches.apply_starting_visor_patch(version.starting_beam_visor, patches_data.default_items, - dol_file) + dol_patches.apply_game_options_patch( + version.game_options_constructor_address, patches_data.user_preferences, dol_file + ) + dol_patches.apply_beam_cost_patch(version.beam_cost_addresses, patches_data.beam_configurations, dol_file) + dol_patches.apply_safe_zone_heal_patch( + version.safe_zone, version.sda2_base, patches_data.safe_zone_heal_per_second, dol_file + ) + dol_patches.apply_starting_visor_patch(version.starting_beam_visor, patches_data.default_items, dol_file) dol_patches.apply_map_door_changes(version.map_door_types, dol_file) diff --git a/src/open_prime_rando/dol_patching/echoes/dol_patches.py b/src/open_prime_rando/dol_patching/echoes/dol_patches.py index 62aed17..87c1492 100644 --- a/src/open_prime_rando/dol_patching/echoes/dol_patches.py +++ b/src/open_prime_rando/dol_patching/echoes/dol_patches.py @@ -97,13 +97,13 @@ class StartingBeamVisorAddresses: ) -def apply_game_options_patch(game_options_constructor_offset: int, user_preferences: OprEchoesUserPreferences, - dol_file: DolFile): +def apply_game_options_patch( + game_options_constructor_offset: int, user_preferences: OprEchoesUserPreferences, dol_file: DolFile +): patch = [ # Unknown purpose, but keep for safety - stw(r31, 0x1c, r1), + stw(r31, 0x1C, r1), or_(r31, r3, r3), - # For a later function call we don't touch addi(r3, r1, 0x8), ] @@ -112,25 +112,27 @@ def apply_game_options_patch(game_options_constructor_offset: int, user_preferen value = getattr(user_preferences, preference_name) if isinstance(value, Enum): value = value.value - patch.extend([ - li(r0, value), - stw(r0, (0x04 * i), r31), - ]) + patch.extend( + [ + li(r0, value), + stw(r0, (0x04 * i), r31), + ] + ) flag_values = [ - getattr(user_preferences, flag_name) - if flag_name is not None else False - for flag_name in _FLAGS_ORDER + getattr(user_preferences, flag_name) if flag_name is not None else False for flag_name in _FLAGS_ORDER ] bit_mask = int("".join(str(int(flag)) for flag in flag_values), 2) - patch.extend([ - li(r0, bit_mask), - stb(r0, 0x04 * len(_PREFERENCES_ORDER), r31), - li(r0, 0), - stw(r0, 0x2c, r31), - stw(r0, 0x30, r31), - stw(r0, 0x34, r31), - ]) + patch.extend( + [ + li(r0, bit_mask), + stb(r0, 0x04 * len(_PREFERENCES_ORDER), r31), + li(r0, 0), + stw(r0, 0x2C, r31), + stw(r0, 0x30, r31), + stw(r0, 0x34, r31), + ] + ) instructions_space = 34 instructions_to_fill = instructions_space - len(patch) @@ -159,31 +161,37 @@ def get_beam_ammo_amount(index: int): instructions.append(bdnz(f"_before_get_ammo_type_{beam_index + 1}_{index}")) if beam_ammo_types[index] == -1: - instructions.extend([ - li(r3, 0), # No ammo type, so load result - b("_end"), # and return - ]) + instructions.extend( + [ + li(r3, 0), # No ammo type, so load result + b("_end"), # and return + ] + ) else: - instructions.extend([ - li(r4, beam_ammo_types[index]), - b(label), - ]) + instructions.extend( + [ + li(r4, beam_ammo_types[index]), + b(label), + ] + ) instructions[0].with_label(f"_before_get_ammo_type_{beam_index}_{index}") body.extend(instructions) - body.extend([ - or_(r3, r31, r31).with_label(label), # arg1 = playerState, arg2 is already there - li(r5, 1), # arg3 = true, allow multiplayer ammo stuff - bl("CPlayerState::GetItemAmount"), # r3 = ammoCount - ]) + body.extend( + [ + or_(r3, r31, r31).with_label(label), # arg1 = playerState, arg2 is already there + li(r5, 1), # arg3 = true, allow multiplayer ammo stuff + bl("CPlayerState::GetItemAmount"), # r3 = ammoCount + ] + ) return body get_uncharged_cost = [ custom_ppc.load_unsigned_32bit(r4, symbols["BeamIdToChargedShotAmmoCost"]), lwz(r5, 0x774, r30), # r5 = get current beam - rlwinm(r5, r5, 0x2, 0x0, 0x1d), # r5 *= 4 + rlwinm(r5, r5, 0x2, 0x0, 0x1D), # r5 *= 4 lwzx(r4, r4, r5), # ammoCost_r4 = UnchargedCosts_r4[currentBeam] ] compare_count_to_cost = [ @@ -198,35 +206,28 @@ def get_beam_ammo_amount(index: int): stwu(r1, -0x10, r1), mfspr(r0, LR), stw(r0, 0x14, r1), - # Save r31 and r30 - stw(r31, 0xc, r1), + stw(r31, 0xC, r1), stw(r30, 0x8, r1), - # Save a pointer to CPlayerGun or_(r30, r3, r3), bl("CPlayerGun::GetPlayer"), - # Get and save a pointer to CPlayerState lwz(r31, 0x1314, r3), - # check ammo type 1 *get_beam_ammo_amount(0), # r3 = ammo amount *get_uncharged_cost, # r4 = uncharged_cost *compare_count_to_cost, - # check ammo type 2 *get_beam_ammo_amount(1), # r3 = ammo amount *get_uncharged_cost, # r4 = uncharged_cost *compare_count_to_cost, - # All ammo types for this beam are fine! li(r3, 0), b("_end"), # and return - # end lwz(r0, 0x14, r1).with_label("_end"), - lwz(r31, 0xc, r1), + lwz(r31, 0xC, r1), lwz(r30, 0x8, r1), mtspr(LR, r0), addi(r1, r1, 0x10), @@ -234,9 +235,9 @@ def get_beam_ammo_amount(index: int): ] -def apply_beam_cost_patch(patch_addresses: BeamCostAddresses, - beam_configurations: Iterable[BeamAmmoConfiguration], - dol_file: DolFile): +def apply_beam_cost_patch( + patch_addresses: BeamCostAddresses, beam_configurations: Iterable[BeamAmmoConfiguration], dol_file: DolFile +): uncharged_costs = [] charged_costs = [] combo_costs = [] @@ -248,10 +249,12 @@ def apply_beam_cost_patch(patch_addresses: BeamCostAddresses, charged_costs.append(beam_config.charged_cost) combo_costs.append(beam_config.combo_ammo_cost) missile_costs.append(beam_config.combo_missile_cost) - ammo_types.append(( - beam_config.ammo_a, - beam_config.ammo_b, - )) + ammo_types.append( + ( + beam_config.ammo_a, + beam_config.ammo_b, + ) + ) # The following patch also changes the fact that the game doesn't check if there's enough ammo for Power Beam # we start our patch right after the `addi r3,r31,0x0` @@ -259,41 +262,33 @@ def apply_beam_cost_patch(patch_addresses: BeamCostAddresses, offset_to_body_end = 0xB4 ammo_type_patch = [ lwz(r10, 0x774, r25), # r10 = get current beam - rlwinm(r10, r10, 0x2, 0x0, 0x1d), # r10 *= 4 - + rlwinm(r10, r10, 0x2, 0x0, 0x1D), # r10 *= 4 lwzx(r0, r3, r10), # r0 = BeamIdToUnchargedShotAmmoCost[currentBeam] stw(r0, 0x0, r29), # *outBeamAmmoCost = r0 - lwz(r10, 0x774, r25), # r10 = get current beam addi(r10, r10, 0x1), # r10 = r10 + 1 mtspr(CTR, r10), # count_register = r10 - # Power Beam bdnz("dark_beam"), # if (--count_register > 0) goto li(r3, ammo_types[0][0]), li(r9, ammo_types[0][1]), b("update_out_beam_type"), - # Dark Beam bdnz("light_beam").with_label("dark_beam"), # if (--count_register > 0) goto li(r3, ammo_types[1][0]), li(r9, ammo_types[1][1]), b("update_out_beam_type"), - # Light Beam bdnz("annihilator_beam").with_label("light_beam"), # if (--count_register > 0) goto li(r3, ammo_types[2][0]), li(r9, ammo_types[2][1]), b("update_out_beam_type"), - # Annihilator Beam li(r3, ammo_types[3][0]).with_label("annihilator_beam"), li(r9, ammo_types[3][1]), - # update_out_beam_type stw(r3, 0x0, r27).with_label("update_out_beam_type"), # *outBeamAmmoTypeA = r3 stw(r9, 0x0, r28), # *outBeamAmmoTypeB = r9 - b(patch_addresses.get_beam_ammo_type_and_costs + offset_to_body_end), # jump to the code for getting the charged/combo costs and then check if has ammo # The address in question is at 0x801ccd64 for NTSC @@ -317,15 +312,13 @@ def apply_beam_cost_patch(patch_addresses: BeamCostAddresses, dol_file.write("BeamIdToUnchargedShotAmmoCost", charged_costs_patch) dol_file.write("BeamIdToChargeComboAmmoCost", combo_costs_patch) dol_file.write("g_ChargeComboMissileCosts", missile_costs_patch) - dol_file.write_instructions(patch_addresses.get_beam_ammo_type_and_costs + ammo_type_patch_offset, - ammo_type_patch) + dol_file.write_instructions(patch_addresses.get_beam_ammo_type_and_costs + ammo_type_patch_offset, ammo_type_patch) dol_file.write_instructions("CPlayerGun::IsOutOfAmmoToShoot", _is_out_of_ammo_patch(dol_file.symbols, ammo_types)) -def apply_safe_zone_heal_patch(patch_addresses: SafeZoneAddresses, - sda2_base: int, - heal_per_second: float, - dol_file: DolFile): +def apply_safe_zone_heal_patch( + patch_addresses: SafeZoneAddresses, sda2_base: int, heal_per_second: float, dol_file: DolFile +): offset = patch_addresses.heal_per_frame_constant - sda2_base dol_file.write(patch_addresses.heal_per_frame_constant, struct.pack(">f", heal_per_second / 60)) @@ -340,35 +333,44 @@ def apply_starting_visor_patch(addresses: StartingBeamVisorAddresses, default_it default_beam = beam_order.index(default_items["beam"]) # Patch CPlayerState constructor with default values - dol_file.write_instructions(addresses.player_state_constructor_clean + 0x54, [ - bl(addresses.health_info_constructor), - - li(r0, default_beam), - stw(r0, 0xc, r30), # xc_currentBeam - - li(r0, default_visor), - stw(r0, 0x30, r30), # x30_currentVisor - stw(r0, 0x34, r30), # x34_transitioningVisor - - li(r3, 0), - ]) + dol_file.write_instructions( + addresses.player_state_constructor_clean + 0x54, + [ + bl(addresses.health_info_constructor), + li(r0, default_beam), + stw(r0, 0xC, r30), # xc_currentBeam + li(r0, default_visor), + stw(r0, 0x30, r30), # x30_currentVisor + stw(r0, 0x34, r30), # x34_transitioningVisor + li(r3, 0), + ], + ) # Patch CPlayerState constructor for loading save files - dol_file.write_instructions(addresses.player_state_constructor_decode + 0x5C, [ - li(r0, default_visor), - stw(r0, 0x30, r30), - stw(r0, 0x34, r30), - ]) + dol_file.write_instructions( + addresses.player_state_constructor_decode + 0x5C, + [ + li(r0, default_visor), + stw(r0, 0x30, r30), + stw(r0, 0x34, r30), + ], + ) # Patch EnterMorphBallState's call for StartTransitionToVisor to use the new default visor - dol_file.write_instructions(addresses.enter_morph_ball_state + 0xE8, [ - li(r4, default_visor), - ]) + dol_file.write_instructions( + addresses.enter_morph_ball_state + 0xE8, + [ + li(r4, default_visor), + ], + ) # Patch CPlayerState::ResetVisor so elevators use the new default visor - dol_file.write_instructions(addresses.reset_visor, [ - li(r0, default_visor), - ]) + dol_file.write_instructions( + addresses.reset_visor, + [ + li(r0, default_visor), + ], + ) @dataclasses.dataclass(frozen=True) @@ -391,19 +393,28 @@ class EchoesDolVersion(BasePrimeDolVersion): def apply_fixes(version: EchoesDolVersion, dol_file: DolFile): dol_file.symbols["CMapWorldInfo::IsAnythingSet"] = version.anything_set_address - dol_file.write_instructions("CMapWorldInfo::IsAnythingSet", [ - li(r3, 1), - blr(), - ]) - - dol_file.write_instructions(version.rs_debugger_printf_loop_address, [ - nop(), - ]) + dol_file.write_instructions( + "CMapWorldInfo::IsAnythingSet", + [ + li(r3, 1), + blr(), + ], + ) + + dol_file.write_instructions( + version.rs_debugger_printf_loop_address, + [ + nop(), + ], + ) # Disable Double Damage VFX by checking for an invalid item id - dol_file.write_instructions(version.double_damage_vfx, [ - li(r4, 999), - ]) + dol_file.write_instructions( + version.double_damage_vfx, + [ + li(r4, 999), + ], + ) def change_powerup_should_persist(version: EchoesDolVersion, dol_file: DolFile, powerups: list[str]): @@ -415,9 +426,12 @@ def change_powerup_should_persist(version: EchoesDolVersion, dol_file: DolFile, def apply_unvisited_room_names(version: EchoesDolVersion, dol_file: DolFile, enabled: bool): # In CAutoMapper::Update, the function checks for `mwInfo.IsMapped` then `mwInfo.IsAreaVisited` and if both are # false, sets a variable to false. This variable indicates if the room name is displayed used. - dol_file.write_instructions(version.unvisited_room_names_address, [ - li(r28, 1 if enabled else 0), - ]) + dol_file.write_instructions( + version.unvisited_room_names_address, + [ + li(r28, 1 if enabled else 0), + ], + ) def apply_teleporter_sounds(version: EchoesDolVersion, dol_file: DolFile, enabled: bool): @@ -428,16 +442,14 @@ def apply_teleporter_sounds(version: EchoesDolVersion, dol_file: DolFile, enable else: inst = blr() - dol_file.write_instructions("CWorldTransManager::SfxStart", [ - inst - ]) + dol_file.write_instructions("CWorldTransManager::SfxStart", [inst]) def freeze_player(): return [ - lfs(f1, -0x707c, r2), # timeout = 5.0f - lwz(r3, 0x14fc, r31), # player = manager->players[0] - lhz(r6, -0x40da, r2), # sfxId = kInvalidSoundId + lfs(f1, -0x707C, r2), # timeout = 5.0f + lwz(r3, 0x14FC, r31), # player = manager->players[0] + lhz(r6, -0x40DA, r2), # sfxId = kInvalidSoundId or_(r4, r31, r31), # mgr li(r5, -0x1), # steamTextureId li(r7, -0x1), # iceTextureId @@ -474,14 +486,17 @@ def apply_map_door_changes(door_symbols: MapDoorTypeAddresses, dol_file: DolFile _type = r5 _out_color = r3 - dol_file.write_instructions("CTweakAutoMapper::GetDoorColor", [ - custom_ppc.load_unsigned_32bit(_colors, door_color_array), - addi(_type, _type, -door_min), - mulli(_type, _type, 4), - lwzx(r0, _colors, _type), - stw(r0, 0, _out_color), - blr() - ]) + dol_file.write_instructions( + "CTweakAutoMapper::GetDoorColor", + [ + custom_ppc.load_unsigned_32bit(_colors, door_color_array), + addi(_type, _type, -door_min), + mulli(_type, _type, 4), + lwzx(r0, _colors, _type), + stw(r0, 0, _out_color), + blr(), + ], + ) dol_file.symbols["CTweakAutoMapper::GetDoorColor::DoorColorArray"] = door_color_array dol_file.write("CTweakAutoMapper::GetDoorColor::DoorColorArray", DoorMapIcon.get_surface_colors_as_bytes()) diff --git a/src/open_prime_rando/dol_patching/echoes/dol_versions.py b/src/open_prime_rando/dol_patching/echoes/dol_versions.py index f68ecfb..d54c323 100644 --- a/src/open_prime_rando/dol_patching/echoes/dol_versions.py +++ b/src/open_prime_rando/dol_patching/echoes/dol_versions.py @@ -19,141 +19,141 @@ EchoesDolVersion( game=Game.ECHOES, description="Gamecube NTSC", - build_string_address=0x803ac3b0, + build_string_address=0x803AC3B0, build_string=b"!#$MetroidBuildInfo!#$Build v1.028 10/18/2004 10:44:32", - sda2_base=0x804223c0, - sda13_base=0x8041fd80, - game_state_pointer=0x80418eb8, - cplayer_vtable=0x803b15d0, - cstate_manager_global=0x803db6e0, + sda2_base=0x804223C0, + sda13_base=0x8041FD80, + game_state_pointer=0x80418EB8, + cplayer_vtable=0x803B15D0, + cstate_manager_global=0x803DB6E0, string_display=StringDisplayPatchAddresses( update_hint_state=0x80038020, - message_receiver_string_ref=0x803bd118, - wstring_constructor=0x802ff3dc, - display_hud_memo=0x8006b3c8, + message_receiver_string_ref=0x803BD118, + wstring_constructor=0x802FF3DC, + display_hud_memo=0x8006B3C8, max_message_size=200, ), powerup_functions=PowerupFunctionsAddresses( - add_power_up=0x800858f0, + add_power_up=0x800858F0, incr_pickup=0x80085760, - decr_pickup=0x800856c4, + decr_pickup=0x800856C4, ), health_capacity=HealthCapacityAddresses( - base_health_capacity=0x8041abe4, - energy_tank_capacity=0x8041abe0, + base_health_capacity=0x8041ABE4, + energy_tank_capacity=0x8041ABE0, ), dangerous_energy_tank=DangerousEnergyTankAddresses( - small_number_float=0x8041a4a8, + small_number_float=0x8041A4A8, incr_pickup=0x80085760, ), beam_cost_addresses=BeamCostAddresses( - uncharged_cost=0x803aa8c8, - charged_cost=0x803aa8d8, - charge_combo_ammo_cost=0x803aa8e8, - charge_combo_missile_cost=0x803a74ac, - get_beam_ammo_type_and_costs=0x801cccb0, - is_out_of_ammo_to_shoot=0x801c92e8, - gun_get_player=0x801dd758, + uncharged_cost=0x803AA8C8, + charged_cost=0x803AA8D8, + charge_combo_ammo_cost=0x803AA8E8, + charge_combo_missile_cost=0x803A74AC, + get_beam_ammo_type_and_costs=0x801CCCB0, + is_out_of_ammo_to_shoot=0x801C92E8, + gun_get_player=0x801DD758, get_item_amount=0x80085514, ), - game_options_constructor_address=0x80161b48, + game_options_constructor_address=0x80161B48, safe_zone=SafeZoneAddresses( - heal_per_frame_constant=0x8041a4fc, - increment_health_fmr=0x8000c710, + heal_per_frame_constant=0x8041A4FC, + increment_health_fmr=0x8000C710, ), starting_beam_visor=StartingBeamVisorAddresses( player_state_constructor_clean=0x80086008, - player_state_constructor_decode=0x80085c20, - health_info_constructor=0x801420c8, + player_state_constructor_decode=0x80085C20, + health_info_constructor=0x801420C8, enter_morph_ball_state=0x80184118, start_transition_to_visor=0x80085314, reset_visor=0x80085334, ), - anything_set_address=0x8010f084, - rs_debugger_printf_loop_address=0x8028c604, - unvisited_room_names_address=0x8008b714, - cworldtransmanager_sfxstart=0x80158e50, - powerup_should_persist=0x803a743c, + anything_set_address=0x8010F084, + rs_debugger_printf_loop_address=0x8028C604, + unvisited_room_names_address=0x8008B714, + cworldtransmanager_sfxstart=0x80158E50, + powerup_should_persist=0x803A743C, map_door_types=MapDoorTypeAddresses( - get_correct_transform=IsDoorAddr(0x800bb4d8, 0x800bb4e0, 3), - map_obj_draw=IsDoorAddr(0x800bb960, 0x800bb96c, 7), - is_visible_to_automapper=IsDoorAddr(0x800bb600, 0x800bb608, 3), - map_world_draw_areas=IsDoorAddr(0x8009458c, 0x80094594, 0), - map_area_commit_resources1=IsDoorAddr(0x8007f3b4, 0x8007f3bc, 3), - map_area_commit_resources2=IsDoorAddr(0x8007fab0, 0x8007fab8, 3), - get_door_color=0x802175b4, - map_icon_jumptable=0x803b3638, - ), - double_damage_vfx=0x80017f28, + get_correct_transform=IsDoorAddr(0x800BB4D8, 0x800BB4E0, 3), + map_obj_draw=IsDoorAddr(0x800BB960, 0x800BB96C, 7), + is_visible_to_automapper=IsDoorAddr(0x800BB600, 0x800BB608, 3), + map_world_draw_areas=IsDoorAddr(0x8009458C, 0x80094594, 0), + map_area_commit_resources1=IsDoorAddr(0x8007F3B4, 0x8007F3BC, 3), + map_area_commit_resources2=IsDoorAddr(0x8007FAB0, 0x8007FAB8, 3), + get_door_color=0x802175B4, + map_icon_jumptable=0x803B3638, + ), + double_damage_vfx=0x80017F28, ), EchoesDolVersion( game=Game.ECHOES, description="Gamecube PAL", - build_string_address=0x803ad710, + build_string_address=0x803AD710, build_string=b"!#$MetroidBuildInfo!#$Build v1.035 10/27/2004 19:48:17", - sda2_base=0x804236c0, + sda2_base=0x804236C0, sda13_base=0x80421060, game_state_pointer=0x8041A19C, - cplayer_vtable=0x803b2950, - cstate_manager_global=0x803dc900, + cplayer_vtable=0x803B2950, + cstate_manager_global=0x803DC900, string_display=StringDisplayPatchAddresses( update_hint_state=0x80038194, - message_receiver_string_ref=0x803be378, - wstring_constructor=0x802ff734, - display_hud_memo=0x8006b504, + message_receiver_string_ref=0x803BE378, + wstring_constructor=0x802FF734, + display_hud_memo=0x8006B504, max_message_size=200, ), powerup_functions=PowerupFunctionsAddresses( - add_power_up=0x80085a2c, - incr_pickup=0x8008589c, + add_power_up=0x80085A2C, + incr_pickup=0x8008589C, decr_pickup=0x80085800, ), health_capacity=HealthCapacityAddresses( - base_health_capacity=0x8041bedc, - energy_tank_capacity=0x8041bed8, + base_health_capacity=0x8041BEDC, + energy_tank_capacity=0x8041BED8, ), dangerous_energy_tank=DangerousEnergyTankAddresses( - small_number_float=0x8041b7a0, - incr_pickup=0x8008589c, + small_number_float=0x8041B7A0, + incr_pickup=0x8008589C, ), beam_cost_addresses=BeamCostAddresses( - uncharged_cost=0x803abc28, - charged_cost=0x803abc38, - charge_combo_ammo_cost=0x803abc48, - charge_combo_missile_cost=0x803a7c04, - get_beam_ammo_type_and_costs=0x801ccfe4, - is_out_of_ammo_to_shoot=0x801c961c, - gun_get_player=0x801dda8c, + uncharged_cost=0x803ABC28, + charged_cost=0x803ABC38, + charge_combo_ammo_cost=0x803ABC48, + charge_combo_missile_cost=0x803A7C04, + get_beam_ammo_type_and_costs=0x801CCFE4, + is_out_of_ammo_to_shoot=0x801C961C, + gun_get_player=0x801DDA8C, get_item_amount=0x80085650, ), - game_options_constructor_address=0x80161d9c, + game_options_constructor_address=0x80161D9C, safe_zone=SafeZoneAddresses( - heal_per_frame_constant=0x8041b7f4, - increment_health_fmr=0x8000c754, + heal_per_frame_constant=0x8041B7F4, + increment_health_fmr=0x8000C754, ), starting_beam_visor=StartingBeamVisorAddresses( player_state_constructor_clean=0x80086144, - player_state_constructor_decode=0x80085d5c, + player_state_constructor_decode=0x80085D5C, health_info_constructor=0x80142304, - enter_morph_ball_state=0x801843f8, + enter_morph_ball_state=0x801843F8, start_transition_to_visor=0x80085450, reset_visor=0x80085470, ), - anything_set_address=0x8010f238, - rs_debugger_printf_loop_address=0x8028ca0c, - unvisited_room_names_address=0x8008b850, - cworldtransmanager_sfxstart=0x801590a4, - powerup_should_persist=0x803a7b94, + anything_set_address=0x8010F238, + rs_debugger_printf_loop_address=0x8028CA0C, + unvisited_room_names_address=0x8008B850, + cworldtransmanager_sfxstart=0x801590A4, + powerup_should_persist=0x803A7B94, map_door_types=MapDoorTypeAddresses( - get_correct_transform=IsDoorAddr(0x800bb56c, 0x800bb574, 3), - map_obj_draw=IsDoorAddr(0x800bb9f0, 0x800bb9fc, 3), - is_visible_to_automapper=IsDoorAddr(0x800bb694, 0x800bb69c, 3), + get_correct_transform=IsDoorAddr(0x800BB56C, 0x800BB574, 3), + map_obj_draw=IsDoorAddr(0x800BB9F0, 0x800BB9FC, 3), + is_visible_to_automapper=IsDoorAddr(0x800BB694, 0x800BB69C, 3), map_world_draw_areas=IsDoorAddr(0x80094620, 0x80094628, 0), - map_area_commit_resources1=IsDoorAddr(0x8007f4f0, 0x8007f4f8, 3), - map_area_commit_resources2=IsDoorAddr(0x8007fbec, 0x8007fbf4, 3), - get_door_color=0x802178d4, - map_icon_jumptable=0x803b4a80, + map_area_commit_resources1=IsDoorAddr(0x8007F4F0, 0x8007F4F8, 3), + map_area_commit_resources2=IsDoorAddr(0x8007FBEC, 0x8007FBF4, 3), + get_door_color=0x802178D4, + map_icon_jumptable=0x803B4A80, ), - double_damage_vfx=0x80017fc4, + double_damage_vfx=0x80017FC4, ), ] diff --git a/src/open_prime_rando/dol_patching/echoes/user_preferences.py b/src/open_prime_rando/dol_patching/echoes/user_preferences.py index 259ad20..92c416c 100644 --- a/src/open_prime_rando/dol_patching/echoes/user_preferences.py +++ b/src/open_prime_rando/dol_patching/echoes/user_preferences.py @@ -3,8 +3,9 @@ def _int_field(default: int, min_value: int, max_value: int, display_as_percentage: bool = True): - return dataclasses.field(default=default, metadata={"min": min_value, "max": max_value, - "display_as_percentage": display_as_percentage}) + return dataclasses.field( + default=default, metadata={"min": min_value, "max": max_value, "display_as_percentage": display_as_percentage} + ) class SoundMode(Enum): @@ -17,13 +18,13 @@ class SoundMode(Enum): class OprEchoesUserPreferences: sound_mode: SoundMode = SoundMode.STEREO screen_brightness: int = _int_field(4, 0, 8) - screen_x_offset: int = _int_field(0, -0x1e, 0x1f, False) - screen_y_offset: int = _int_field(0, -0x1e, 0x1f, False) + screen_x_offset: int = _int_field(0, -0x1E, 0x1F, False) + screen_y_offset: int = _int_field(0, -0x1E, 0x1F, False) screen_stretch: int = _int_field(0, -10, 10, False) sfx_volume: int = _int_field(0x69, 0x00, 0x69) - music_volume: int = _int_field(0x4f, 0x00, 0x69) - hud_alpha: int = _int_field(0xff, 0x00, 0xff) - helmet_alpha: int = _int_field(0xff, 0x00, 0xff) + music_volume: int = _int_field(0x4F, 0x00, 0x69) + hud_alpha: int = _int_field(0xFF, 0x00, 0xFF) + helmet_alpha: int = _int_field(0xFF, 0x00, 0xFF) hud_lag: bool = True invert_y_axis: bool = False rumble: bool = True diff --git a/src/open_prime_rando/dol_patching/prime1/dol_patches.py b/src/open_prime_rando/dol_patching/prime1/dol_patches.py index 6d69bf3..f0b5b84 100644 --- a/src/open_prime_rando/dol_patching/prime1/dol_patches.py +++ b/src/open_prime_rando/dol_patching/prime1/dol_patches.py @@ -18,9 +18,17 @@ class Prime1DolVersion(BasePrimeDolVersion): state_for_world: int set_layer_active: int - def __init__(self, version: str, description: str, build_string_address: int, - build_string: bytes, sda2_base: int, sda13_base: int, cplayer_vtable: int, - message_receiver_string_ref: int): + def __init__( + self, + version: str, + description: str, + build_string_address: int, + build_string: bytes, + sda2_base: int, + sda13_base: int, + cplayer_vtable: int, + message_receiver_string_ref: int, + ): symbols = py_randomprime.symbols_for_version(version) super().__init__( @@ -50,8 +58,11 @@ def __init__(self, version: str, description: str, build_string_address: int, object.__setattr__(self, "set_layer_active", symbols["SetLayerActive__16CWorldLayerStateFiib"]) -def set_artifact_layer_active_patch(addresses: Prime1DolVersion, layer_id: int, active: bool, - ) -> list[assembler.BaseInstruction]: +def set_artifact_layer_active_patch( + addresses: Prime1DolVersion, + layer_id: int, + active: bool, +) -> list[assembler.BaseInstruction]: # g_GameState->StateForWorld(0x39F2DE28)->GetLayerState()->SetLayerActive(templeAreaIndex, artifactLayer, true) result = [] @@ -63,35 +74,31 @@ def set_artifact_layer_active_patch(addresses: Prime1DolVersion, layer_id: int, lwz(r3, 0x14, r3), # worldState->layerState ] - result.extend([ - # Get the LayerState of current db. We'll overwrite if it's another db, it's just 1 instruction bigger - lwz(r3, 0x8c8, r31), # mgr->worldLayerState - - # Tallon Overworld asset id - custom_ppc.load_unsigned_32bit(r4, 0x39f2de28), - - # Load current asset id in r5 - lwz(r5, 0x850, r31), # mgr->db - lwz(r5, 0x8, r5), # db->mlvlId - - cmpw(0, r4, r5), # compare asset ids - beq(4 + assembler.byte_count(for_another_world), relative=True), - *for_another_world, - lwz(r3, 0x0, r3), - - # Set layer - li(r4, 16), # Artifact Layer - stw(r4, 0x10, r1), - - # Set layer - li(r5, layer_id), # Artifact Layer - stw(r5, 0x14, r1), - - # Make the layer change via SetLayerActive - addi(r4, r1, 0x10), - addi(r5, r1, 0x14), - li(r6, int(active)), - bl(addresses.set_layer_active), # CWorldLayerState::SetLayerActive - ]) + result.extend( + [ + # Get the LayerState of current db. We'll overwrite if it's another db, it's just 1 instruction bigger + lwz(r3, 0x8C8, r31), # mgr->worldLayerState + # Tallon Overworld asset id + custom_ppc.load_unsigned_32bit(r4, 0x39F2DE28), + # Load current asset id in r5 + lwz(r5, 0x850, r31), # mgr->db + lwz(r5, 0x8, r5), # db->mlvlId + cmpw(0, r4, r5), # compare asset ids + beq(4 + assembler.byte_count(for_another_world), relative=True), + *for_another_world, + lwz(r3, 0x0, r3), + # Set layer + li(r4, 16), # Artifact Layer + stw(r4, 0x10, r1), + # Set layer + li(r5, layer_id), # Artifact Layer + stw(r5, 0x14, r1), + # Make the layer change via SetLayerActive + addi(r4, r1, 0x10), + addi(r5, r1, 0x14), + li(r6, int(active)), + bl(addresses.set_layer_active), # CWorldLayerState::SetLayerActive + ] + ) return result diff --git a/src/open_prime_rando/dol_patching/prime1/dol_versions.py b/src/open_prime_rando/dol_patching/prime1/dol_versions.py index 5c5c439..fbf4358 100644 --- a/src/open_prime_rando/dol_patching/prime1/dol_versions.py +++ b/src/open_prime_rando/dol_patching/prime1/dol_versions.py @@ -4,61 +4,61 @@ Prime1DolVersion( version="0-00", description="GC NTSC 0-00", - build_string_address=0x803cc588, + build_string_address=0x803CC588, build_string=b"!#$MetroidBuildInfo!#$Build v1.088 10/29/2002 2:21:25", - sda2_base=0x805b1d20, - sda13_base=0x805aebc0, - cplayer_vtable=0x803d96e8, - message_receiver_string_ref=0x803efb90, + sda2_base=0x805B1D20, + sda13_base=0x805AEBC0, + cplayer_vtable=0x803D96E8, + message_receiver_string_ref=0x803EFB90, ), Prime1DolVersion( version="0-01", description="GC NTSC 0-01", - build_string_address=0x803cc768, + build_string_address=0x803CC768, build_string=b"!#$MetroidBuildInfo!#$Build v1.093 11/5/2002 19:50:01", - sda2_base=0x805b1f00, - sda13_base=0x805aeda0, - cplayer_vtable=0x803d98c8, - message_receiver_string_ref=0x803efd70, + sda2_base=0x805B1F00, + sda13_base=0x805AEDA0, + cplayer_vtable=0x803D98C8, + message_receiver_string_ref=0x803EFD70, ), Prime1DolVersion( version="0-02", description="GC NTSC 0-02", - build_string_address=0x803cd648, + build_string_address=0x803CD648, build_string=b"!#$MetroidBuildInfo!#$Build v1.111 3/10/2003 17:56:21", - sda2_base=0x805b2de0, - sda13_base=0x805afc40, - cplayer_vtable=0x803da7a8, - message_receiver_string_ref=0x803f0ba8, + sda2_base=0x805B2DE0, + sda13_base=0x805AFC40, + cplayer_vtable=0x803DA7A8, + message_receiver_string_ref=0x803F0BA8, ), Prime1DolVersion( version="pal", description="GC PAL", - build_string_address=0x803b6924, + build_string_address=0x803B6924, build_string=b"!#$MetroidBuildInfo!#$Build v1.110 2/4/2003 22:16:07", - sda2_base=0x80473e60, - sda13_base=0x80470c60, - cplayer_vtable=0x803c4b88, - message_receiver_string_ref=0x803d7a28, + sda2_base=0x80473E60, + sda13_base=0x80470C60, + cplayer_vtable=0x803C4B88, + message_receiver_string_ref=0x803D7A28, ), Prime1DolVersion( version="kor", description="GC NTSC-K", - build_string_address=0x803cc688, + build_string_address=0x803CC688, build_string=b"!#$MetroidBuildInfo!#$Build v1.097 12/19/2002 16:03:43", - sda2_base=0x805b1a00, - sda13_base=0x805ae8a0, - cplayer_vtable=0x803d97e8, - message_receiver_string_ref=0x803efc90, + sda2_base=0x805B1A00, + sda13_base=0x805AE8A0, + cplayer_vtable=0x803D97E8, + message_receiver_string_ref=0x803EFC90, ), Prime1DolVersion( version="jpn", description="GC NTSC-J", - build_string_address=0x803b86cc, + build_string_address=0x803B86CC, build_string=b"!#$MetroidBuildInfo!#$Build v1.111 2/11/2003 10:03:22", - sda2_base=0x8059ae00, - sda13_base=0x80597c20, - cplayer_vtable=0x803c5b28, - message_receiver_string_ref=0x803d89c8, + sda2_base=0x8059AE00, + sda13_base=0x80597C20, + cplayer_vtable=0x803C5B28, + message_receiver_string_ref=0x803D89C8, ), ] diff --git a/src/open_prime_rando/dynamic_schema.py b/src/open_prime_rando/dynamic_schema.py index 833606a..cb9126d 100644 --- a/src/open_prime_rando/dynamic_schema.py +++ b/src/open_prime_rando/dynamic_schema.py @@ -17,10 +17,7 @@ def expand_schema(base_schema: dict, editor: PatcherEditor) -> dict: world_def["properties"]["areas"] = {"type": "object", "additionalProperties": False, "properties": area_props} world_details = open_prime_rando.echoes.asset_ids.world.load_dedicated_file(world) - mrea_to_name: dict[int, str] = { - mrea: name - for name, mrea in world_details.NAME_TO_ID_MREA.items() - } + mrea_to_name: dict[int, str] = {mrea: name for name, mrea in world_details.NAME_TO_ID_MREA.items()} for area in mlvl.areas: area_name = mrea_to_name[area.mrea_asset_id] @@ -39,10 +36,7 @@ def expand_schema(base_schema: dict, editor: PatcherEditor) -> dict: area_def["properties"]["layers"] = { "type": "object", - "properties": { - layer.name: {"type": "boolean"} - for layer in area.layers - }, + "properties": {layer.name: {"type": "boolean"} for layer in area.layers}, "default": {}, "additionalProperties": False, } diff --git a/src/open_prime_rando/echoes/asset_ids/world.py b/src/open_prime_rando/echoes/asset_ids/world.py index 9776331..7a8278b 100644 --- a/src/open_prime_rando/echoes/asset_ids/world.py +++ b/src/open_prime_rando/echoes/asset_ids/world.py @@ -40,6 +40,7 @@ def load_dedicated_file(world_name: str): import importlib + return importlib.import_module( _DEDICATED_FILES[world_name], ".".join(__name__.split(".")[:-1]), diff --git a/src/open_prime_rando/echoes/custom_assets/__init__.py b/src/open_prime_rando/echoes/custom_assets/__init__.py index ca59de0..e591a88 100644 --- a/src/open_prime_rando/echoes/custom_assets/__init__.py +++ b/src/open_prime_rando/echoes/custom_assets/__init__.py @@ -20,35 +20,35 @@ class TranslatorAssets: new_ancs: int -SCAN_VISOR_CMDL = 0xafc70004 -DARK_VISOR_ANCS = 0x851b526e +SCAN_VISOR_CMDL = 0xAFC70004 +DARK_VISOR_ANCS = 0x851B526E -VIOLET_TXTR = 0x4be5342e -AMBER_TXTR = 0xf5308558 -EMERALD_TXTR = 0xa9640fdf -COBALT_TXTR = 0x2c56d2d4 +VIOLET_TXTR = 0x4BE5342E +AMBER_TXTR = 0xF5308558 +EMERALD_TXTR = 0xA9640FDF +COBALT_TXTR = 0x2C56D2D4 VIOLET_TRANSLATOR = TranslatorAssets( name="violet_translator", - txtr=0x4be5342e, + txtr=0x4BE5342E, new_cmdl=1448365380, new_ancs=1448362318, ) AMBER_TRANSLATOR = TranslatorAssets( name="amber_translator", - txtr=0xf5308558, + txtr=0xF5308558, new_cmdl=1096043844, new_ancs=1096040782, ) EMERALD_TRANSLATOR = TranslatorAssets( name="emerald_translator", - txtr=0xa9640fdf, + txtr=0xA9640FDF, new_cmdl=1163152708, new_ancs=1163149646, ) COBALT_TRANSLATOR = TranslatorAssets( name="cobalt_translator", - txtr=0x2c56d2d4, + txtr=0x2C56D2D4, new_cmdl=1129598276, new_ancs=1129595214, ) @@ -84,14 +84,14 @@ class BeamAmmoAssets: new_ancs: int -BEAM_AMMO_EXPANSION_CMDL = 0x352c8b02 -BEAM_AMMO_EXPANSION_ANCS = 0x4e00188c +BEAM_AMMO_EXPANSION_CMDL = 0x352C8B02 +BEAM_AMMO_EXPANSION_ANCS = 0x4E00188C DARK_AMMO = BeamAmmoAssets( name="dark_ammo", txtr_a=1385637646, txtr_b=3277274054, - particle=0x8e0c499d, + particle=0x8E0C499D, new_cmdl=1631670273, new_ancs=0x61415002, ) @@ -99,7 +99,7 @@ class BeamAmmoAssets: name="light_ammo", txtr_a=1815959726, txtr_b=2738959665, - particle=0xa180bb7f, + particle=0xA180BB7F, new_cmdl=1631670275, new_ancs=1631670276, ) @@ -132,7 +132,7 @@ def _import_premade_assets(editor: AssetManager): assets = Path(__file__).parent.joinpath("custom_assets", "general") for f in assets.glob("*.*"): name = f.name - asset_type = f.suffix[1:].upper() # remove leading period, force uppercase + asset_type = f.suffix[1:].upper() # remove leading period, force uppercase raw = f.read_bytes() editor.add_new_asset(name, RawResource(asset_type, raw), ()) diff --git a/src/open_prime_rando/echoes/dock_lock_rando/__init__.py b/src/open_prime_rando/echoes/dock_lock_rando/__init__.py index 63a5bcd..9cca6f2 100644 --- a/src/open_prime_rando/echoes/dock_lock_rando/__init__.py +++ b/src/open_prime_rando/echoes/dock_lock_rando/__init__.py @@ -1,4 +1,3 @@ - from construct import Container from retro_data_structures.base_resource import AssetId, RawResource from retro_data_structures.formats.cmdl import Cmdl @@ -12,24 +11,19 @@ def add_custom_models(editor: PatcherEditor): assets = custom_asset_path().joinpath("doors") + def get_txtr(n: str, must_exist: bool = True) -> AssetId: f = assets.joinpath(n) if not must_exist and not f.exists(): return None - res = RawResource( - type="TXTR", - data=f.read_bytes() - ) + res = RawResource(type="TXTR", data=f.read_bytes()) return editor.add_new_asset(n, res) greyscale_emissive = get_txtr("custom_door_lock_greyscale_emissive.TXTR") template = editor.get_parsed_asset(0xF115F575, type_hint=Cmdl) for door_type in DOCK_TYPES.values(): - if not ( - isinstance(door_type, dock_type.BlastShieldDoorType) - and isinstance(door_type.shield_model, str) - ): + if not (isinstance(door_type, dock_type.BlastShieldDoorType) and isinstance(door_type.shield_model, str)): continue name = door_type.shield_model txtr = get_txtr(f"custom_door_lock_{name}.TXTR") @@ -43,8 +37,15 @@ def get_txtr(n: str, must_exist: bool = True) -> AssetId: editor.add_new_asset(f"custom_door_lock_{name}.CMDL", Cmdl(cmdl, Game.ECHOES, editor)) -def apply_door_rando(editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, - new_door_type: str, old_door_type: str | None, low_memory: bool): +def apply_door_rando( + editor: PatcherEditor, + world_name: str, + area_name: str, + dock_name: str, + new_door_type: str, + old_door_type: str | None, + low_memory: bool, +): if old_door_type is not None: old_door = DOCK_TYPES[old_door_type] diff --git a/src/open_prime_rando/echoes/dock_lock_rando/dock_type.py b/src/open_prime_rando/echoes/dock_lock_rando/dock_type.py index 732c783..19d7ead 100644 --- a/src/open_prime_rando/echoes/dock_lock_rando/dock_type.py +++ b/src/open_prime_rando/echoes/dock_lock_rando/dock_type.py @@ -74,11 +74,9 @@ def get_area(self, editor: PatcherEditor, world_name: str, area_name: str) -> Ar def get_door_from_dock_index(self, area: Area, dock_index: int) -> ScriptInstance: dock = next( - inst for inst in area.all_instances - if ( - inst.type == Dock and - inst.get_properties_as(Dock).dock_number == dock_index - ) + inst + for inst in area.all_instances + if (inst.type == Dock and inst.get_properties_as(Dock).dock_number == dock_index) ) for instance in area.all_instances: if instance.type != Door: @@ -162,8 +160,13 @@ class BlastShieldDoorType(DoorType): shield_collision_offset: Vector = dataclasses.field(default_factory=lambda: Vector(-2 / 3, 0, 2.0)) def find_attached_instance( - self, area: Area, source: ScriptInstance, state: State, message: Message, - target_type: type[BaseObjectType], target_name: str | None = None + self, + area: Area, + source: ScriptInstance, + state: State, + message: Message, + target_type: type[BaseObjectType], + target_name: str | None = None, ) -> ScriptInstance: for connection in source.connections: if connection.state == state and connection.message == message: @@ -175,42 +178,33 @@ def find_attached_instance( def get_spline(self) -> Spline: return Spline.from_bytes( - b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x02' - b'\x02A \x00\x00?\x80\x00\x00\x02\x02\x01\x00\x00\x00\x00?\x80\x00\x00' + b"\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x02" + b"\x02A \x00\x00?\x80\x00\x00\x02\x02\x01\x00\x00\x00\x00?\x80\x00\x00" ) - def create_trigger(self, - name: str, - door_xfm: Transform, - health: float, - visor_flags: VisorFlags = VisorFlags.Combat | VisorFlags.Dark, - active: bool = True, - seeker_lock_on: bool = True, - orbitable: bool = False, - ): - pos = Vector( - door_xfm.position.x, - door_xfm.position.y, - door_xfm.position.z + 1.8 - ) + def create_trigger( + self, + name: str, + door_xfm: Transform, + health: float, + visor_flags: VisorFlags = VisorFlags.Combat | VisorFlags.Dark, + active: bool = True, + seeker_lock_on: bool = True, + orbitable: bool = False, + ): + pos = Vector(door_xfm.position.x, door_xfm.position.y, door_xfm.position.z + 1.8) return DamageableTriggerOrientated( editor_properties=EditorProperties( name=name, - transform=Transform( - position=pos, - rotation=door_xfm.rotation, - scale=Vector(4.0, 4.0, 1.5) - ), - active=active + transform=Transform(position=pos, rotation=door_xfm.rotation, scale=Vector(4.0, 4.0, 1.5)), + active=active, ), health=HealthInfo(health=health), vulnerability=self.vulnerability, enable_seeker_lock_on=seeker_lock_on, orbitable=orbitable, - visor=VisorParameters( - visor_flags=visor_flags - ) + visor=VisorParameters(visor_flags=visor_flags), ) def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): @@ -243,21 +237,18 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc shield_model = f"custom_door_lock_{shield_model}.CMDL" model = editor._resolve_asset_id(shield_model) - lock = default.add_instance_with(Actor( - editor_properties=EditorProperties( - name=f"{self.name} Blast Shield Lock", - transform=door_transform - ), - collision_box=self.shield_collision_box, - collision_offset=self.shield_collision_offset, - vulnerability=self.vulnerability, - model=model, - actor_information=ActorParameters( - scannable=ScannableParameters( - scannable_info0=self.get_patched_scan(editor, world_name, area_name) - ) + lock = default.add_instance_with( + Actor( + editor_properties=EditorProperties(name=f"{self.name} Blast Shield Lock", transform=door_transform), + collision_box=self.shield_collision_box, + collision_offset=self.shield_collision_offset, + vulnerability=self.vulnerability, + model=model, + actor_information=ActorParameters( + scannable=ScannableParameters(scannable_info0=self.get_patched_scan(editor, world_name, area_name)) + ), ) - )) + ) relay = default.add_memory_relay("Lock Cleared") with relay.edit_properties(MemoryRelay) as _relay: @@ -278,37 +269,34 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc if not low_memory: particle = 0xCDCBDF04 - gibs = default.add_instance_with(Effect( - editor_properties=EditorProperties( - transform=door_transform, - active=False - ), - particle_effect=particle, - restart_on_activate=True, - motion_control_spline=self.get_spline(), - )) + gibs = default.add_instance_with( + Effect( + editor_properties=EditorProperties(transform=door_transform, active=False), + particle_effect=particle, + restart_on_activate=True, + motion_control_spline=self.get_spline(), + ) + ) - sound = default.add_instance_with(Sound( - editor_properties=EditorProperties( - name="Metal Door Lock Breaking", - transform=door_transform - ), - sound=948, - max_audible_distance=150.0, - surround_pan=SurroundPan(surround_pan=1.0) - )) + sound = default.add_instance_with( + Sound( + editor_properties=EditorProperties(name="Metal Door Lock Breaking", transform=door_transform), + sound=948, + max_audible_distance=150.0, + surround_pan=SurroundPan(surround_pan=1.0), + ) + ) - streamed = default.add_instance_with(StreamedAudio( - editor_properties=EditorProperties( - name="StreamedAudio - Event Jingle", - transform=door_transform - ), - song_file="/audio/evt_x_event_00.dsp", - fade_in_time=0.01, - volume=65, - software_channel=1, - unknown=False - )) + streamed = default.add_instance_with( + StreamedAudio( + editor_properties=EditorProperties(name="StreamedAudio - Event Jingle", transform=door_transform), + song_file="/audio/evt_x_event_00.dsp", + fade_in_time=0.01, + volume=65, + software_channel=1, + unknown=False, + ) + ) lock.add_connection(State.Dead, Message.Play, sound) lock.add_connection(State.Dead, Message.Play, streamed) @@ -348,40 +336,27 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc door_transform = actors.door.get_properties_as(Door).editor_properties.transform # TODO: try using a sequencetimer to squeeze one fewer instance out of this - timer = default.add_instance_with(Timer( - editor_properties=EditorProperties( - name="Button Control", - transform=door_transform - ), - time=0.75 - )) + timer = default.add_instance_with( + Timer(editor_properties=EditorProperties(name="Button Control", transform=door_transform), time=0.75) + ) - timer_reset = default.add_instance_with(Timer( - editor_properties=EditorProperties( - name="Button Reset", - transform=door_transform - ), - time=0.01 - )) + timer_reset = default.add_instance_with( + Timer(editor_properties=EditorProperties(name="Button Reset", transform=door_transform), time=0.01) + ) # create 5 triggers so that you can have 5 lock-ons # TODO: only use 2 triggers in low memory mode and just accept it being more jank? # 30.01 health because splash damage is inconsistent. missiles do 30 damage # so this guarantees you need at least 2 missiles at once to break it triggers = [ - default.add_instance_with(self.create_trigger( - f"Bridge Button {i}", - door_transform, 30.01) - ) for i in range(5) + default.add_instance_with(self.create_trigger(f"Bridge Button {i}", door_transform, 30.01)) + for i in range(5) ] main_trigger = triggers[0] # TODO: repurpose one of the big triggers for this to save an instance - mini_trigger = default.add_instance_with(self.create_trigger( - "Bridge Button Mini", - door_transform, - 1.0, - seeker_lock_on=False - )) + mini_trigger = default.add_instance_with( + self.create_trigger("Bridge Button Mini", door_transform, 1.0, seeker_lock_on=False) + ) # start a timer when the tiny trigger dies. stop it if the main trigger dies mini_trigger.add_connection(State.Dead, Message.ResetAndStart, timer) @@ -398,11 +373,7 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc # move the lock's connections to the trigger, since it's the thing that dies now for connection in actors.lock.connections: actors.lock.remove_connection(connection) - main_trigger.add_connection( - connection.state, - connection.message, - connection.target - ) + main_trigger.add_connection(connection.state, connection.message, connection.target) for trigger in triggers: actors.relay.add_connection(State.Active, Message.Deactivate, trigger) @@ -432,13 +403,13 @@ class PlanetaryEnergyDoorType(DoorType): planetary_energy_item_id: int def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): - raise NotImplementedError() + raise NotImplementedError @dataclasses.dataclass(kw_only=True) class GrappleDoorType(BlastShieldDoorType): def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): - raise NotImplementedError() + raise NotImplementedError @dataclasses.dataclass @@ -464,38 +435,32 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc with actors.lock.edit_properties(Actor) as lock: lock.vulnerability = resist_all_vuln - trigger = default.add_instance_with(self.create_trigger( - "Door Button", door_xfm, 1.0, self.visor_flags, - active=False, seeker_lock_on=False, orbitable=True - )) + trigger = default.add_instance_with( + self.create_trigger( + "Door Button", door_xfm, 1.0, self.visor_flags, active=False, seeker_lock_on=False, orbitable=True + ) + ) - visor_detector = default.add_instance_with(PlayerController( - editor_properties=EditorProperties( - name="Detect Visor", - transform=door_xfm - ), - unknown_0xe71de331=1, - proxy_type=self.player_controller_proxy - )) + visor_detector = default.add_instance_with( + PlayerController( + editor_properties=EditorProperties(name="Detect Visor", transform=door_xfm), + unknown_0xe71de331=1, + proxy_type=self.player_controller_proxy, + ) + ) visor_detector.add_connection(State.Entered, Message.Activate, trigger) visor_detector.add_connection(State.Exited, Message.Deactivate, trigger) for connection in actors.lock.connections: actors.lock.remove_connection(connection) - trigger.add_connection( - connection.state, - connection.message, - connection.target - ) + trigger.add_connection(connection.state, connection.message, connection.target) actors.relay.add_connection(State.Active, Message.Deactivate, trigger) actors.relay.add_connection(State.Active, Message.Deactivate, visor_detector) return VisorBlastShieldActors( - actors.door, actors.sound, actors.streamed, - actors.lock, actors.relay, actors.gibs, - trigger, visor_detector + actors.door, actors.sound, actors.streamed, actors.lock, actors.relay, actors.gibs, trigger, visor_detector ) @@ -519,28 +484,26 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc door_xfm = actors.door.get_properties_as(Door).editor_properties.transform - center_pos = Vector( - door_xfm.position.x, - door_xfm.position.y, - door_xfm.position.z + 1.8 - ) + center_pos = Vector(door_xfm.position.x, door_xfm.position.y, door_xfm.position.z + 1.8) - hud_hint = default.add_instance_with(HUDHint( - editor_properties=EditorProperties( - name="Echo Target Icon", - transform=Transform( - center_pos, - door_xfm.rotation, + hud_hint = default.add_instance_with( + HUDHint( + editor_properties=EditorProperties( + name="Echo Target Icon", + transform=Transform( + center_pos, + door_xfm.rotation, + ), + active=True, ), - active=True - ), - hud_texture=0x36B1CB06, - unknown_0x6078a651=24.0, - unknown_0xf00bb6bb=128.0, - animation_time=0.5, - animation_frames=4, - unknown_0xd993f97b=2 - )) + hud_texture=0x36B1CB06, + unknown_0x6078a651=24.0, + unknown_0xf00bb6bb=128.0, + animation_time=0.5, + animation_frames=4, + unknown_0xd993f97b=2, + ) + ) with actors.lock.edit_properties(Actor) as lock: lock.echo_information.is_echo_emitter = True @@ -548,58 +511,50 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc actors.relay.add_connection(State.Active, Message.Deactivate, hud_hint) if not low_memory: - beacon_loop = default.add_instance_with(Sound( - editor_properties=EditorProperties( - name="Echo Beacon Sound Loop", - transform=Transform( - position=center_pos + beacon_loop = default.add_instance_with( + Sound( + editor_properties=EditorProperties( + name="Echo Beacon Sound Loop", + transform=Transform(position=center_pos), ), - ), - sound=1003, - max_audible_distance=75.0, - min_volume=0, - max_volume=60, - surround_pan=SurroundPan( - pan=0.0, - surround_pan=1.0 - ), - loop=True, - play_always=True, - echo_visor_max_volume=127 - )) + sound=1003, + max_audible_distance=75.0, + min_volume=0, + max_volume=60, + surround_pan=SurroundPan(pan=0.0, surround_pan=1.0), + loop=True, + play_always=True, + echo_visor_max_volume=127, + ) + ) - disrupted = default.add_instance_with(Sound( - editor_properties=EditorProperties( - name="Echo Particle Disrupted", - transform=Transform( - position=center_pos + disrupted = default.add_instance_with( + Sound( + editor_properties=EditorProperties( + name="Echo Particle Disrupted", + transform=Transform(position=center_pos), ), - ), - sound=1004, - max_audible_distance=75.0, - min_volume=20, - max_volume=120, - surround_pan=SurroundPan( - pan=0.0, - surround_pan=1.0 - ), - )) + sound=1004, + max_audible_distance=75.0, + min_volume=20, + max_volume=120, + surround_pan=SurroundPan(pan=0.0, surround_pan=1.0), + ) + ) - timer = default.add_instance_with(Timer( - editor_properties=EditorProperties( - name="Open Echo Door", - transform=door_xfm, - ), - time=1.0 - )) + timer = default.add_instance_with( + Timer( + editor_properties=EditorProperties( + name="Open Echo Door", + transform=door_xfm, + ), + time=1.0, + ) + ) for connection in actors.trigger.connections: actors.trigger.remove_connection(connection) - timer.add_connection( - State.Zero, - connection.message, - connection.target - ) + timer.add_connection(State.Zero, connection.message, connection.target) actors.trigger.add_connection(State.Dead, Message.ResetAndStart, timer) actors.trigger.add_connection(State.Dead, Message.InternalMessage00, hud_hint) @@ -617,7 +572,7 @@ def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, doc @dataclasses.dataclass(kw_only=True) class ScanVisorDoorType(BlastShieldDoorType): def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): - raise NotImplementedError() + raise NotImplementedError @dataclasses.dataclass(kw_only=True) @@ -625,4 +580,4 @@ class TranslatorDoorType(ScanVisorDoorType): translator_item_id: int def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): - raise NotImplementedError() + raise NotImplementedError diff --git a/src/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py b/src/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py index 435dc00..221ec8c 100644 --- a/src/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py +++ b/src/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py @@ -9,8 +9,8 @@ from open_prime_rando.echoes.vulnerabilities import normal_vuln, resist_all_vuln, vulnerable, vulnerable_no_splash normal_door_model = 0x6B78FD92 -dark_door_model = 0xbbcf134d -annihilator_door_model = 0xa50c7238 +dark_door_model = 0xBBCF134D +annihilator_door_model = 0xA50C7238 blast_collision_box = Vector(0.35, 5.0, 4.0) blast_collision_offset = Vector(-2 / 3, 0, 2.0) @@ -24,49 +24,49 @@ ), "Dark": dock_type.NormalDoorType( name="Dark", - vulnerability=dataclasses.replace(resist_all_vuln, dark=vulnerable, entangler=vulnerable, - black_hole=vulnerable), + vulnerability=dataclasses.replace( + resist_all_vuln, dark=vulnerable, entangler=vulnerable, black_hole=vulnerable + ), shell_model=dark_door_model, shell_color=Color(1, 1, 1, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. Dark energy may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. Dark energy may damage it.", ), - map_icon=DoorMapIcon.Dark + map_icon=DoorMapIcon.Dark, ), "Light": dock_type.NormalDoorType( name="Light", - vulnerability=dataclasses.replace(resist_all_vuln, light=vulnerable, light_blast=vulnerable, - sunburst=vulnerable), + vulnerability=dataclasses.replace( + resist_all_vuln, light=vulnerable, light_blast=vulnerable, sunburst=vulnerable + ), shell_color=Color(1, 1, 1, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. Light energy may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. Light energy may damage it.", ), - map_icon=DoorMapIcon.Light + map_icon=DoorMapIcon.Light, ), "Annihilator": dock_type.NormalDoorType( name="Annihilator", - vulnerability=dataclasses.replace(resist_all_vuln, annihilator=vulnerable, sonic_boom=vulnerable, - imploder=vulnerable), + vulnerability=dataclasses.replace( + resist_all_vuln, annihilator=vulnerable, sonic_boom=vulnerable, imploder=vulnerable + ), shell_model=annihilator_door_model, shell_color=Color(1, 1, 1, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A mix of light and dark energy may damage it." + "A mix of light and dark energy may damage it.", ), - map_icon=DoorMapIcon.Annihilator + map_icon=DoorMapIcon.Annihilator, ), "Disabled": dock_type.NormalDoorType( name="Disabled", vulnerability=resist_all_vuln, shell_color=Color(0, 0, 0, 0), - scan_text=( - "Door system access denied.", - "Unable to bypass security codes. Seek an alternate exit." - ), - map_icon=DoorMapIcon.Disabled + scan_text=("Door system access denied.", "Unable to bypass security codes. Seek an alternate exit."), + map_icon=DoorMapIcon.Disabled, ), "Missile": dock_type.VanillaBlastShieldDoorType( name="Missile", @@ -74,7 +74,7 @@ shell_color=Color(1, 0, 0, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Missile blast may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Missile blast may damage it.", ), map_icon=DoorMapIcon.Missile, shield_model=0xBFB4A8EE, @@ -86,7 +86,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A Super Missile blast may damage it." + "A Super Missile blast may damage it.", ), map_icon=DoorMapIcon.SuperMissile, shield_model=0xF115F575, @@ -97,7 +97,7 @@ shell_color=Color(1, 0.94, 0, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Power Bomb may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Power Bomb may damage it.", ), map_icon=DoorMapIcon.PowerBomb, shield_model=0xC2E4F075, @@ -109,7 +109,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "Five simultaneous Missile blasts may damage it." + "Five simultaneous Missile blasts may damage it.", ), map_icon=DoorMapIcon.SeekerMissile, shield_model=0x56F4208B, @@ -121,7 +121,7 @@ shell_color=Color(0.93, 0.58, 0.83, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Screw Attack may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Screw Attack may damage it.", ), map_icon=DoorMapIcon.ScrewAttack, shield_model="screw_attack", @@ -130,27 +130,23 @@ name="Bomb", vulnerability=dataclasses.replace(resist_all_vuln, bomb=vulnerable), shell_model=normal_door_model, - shell_color=Color(1/3, 1/3, 0.5, 1), + shell_color=Color(1 / 3, 1 / 3, 0.5, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A Morph Ball Bomb blast may damage it." + "A Morph Ball Bomb blast may damage it.", ), map_icon=DoorMapIcon.Bomb, shield_model="morph_ball_bombs", ), "Boost": dock_type.BlastShieldDoorType( name="Boost", - vulnerability=dataclasses.replace( - resist_all_vuln, - boost_ball=vulnerable, - cannon_ball=vulnerable_no_splash - ), + vulnerability=dataclasses.replace(resist_all_vuln, boost_ball=vulnerable, cannon_ball=vulnerable_no_splash), shell_model=normal_door_model, - shell_color=Color(1, 1/3, 0, 1), + shell_color=Color(1, 1 / 3, 0, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Boost Ball may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Boost Ball may damage it.", ), map_icon=DoorMapIcon.Boost, shield_model="boost_ball", @@ -163,7 +159,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to weapon fire, but is weakly secured. " - "The Grapple Beam may be able to remove it." + "The Grapple Beam may be able to remove it.", ), map_icon=DoorMapIcon.Grapple, shield_model=0x56F4208B, @@ -176,7 +172,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A massive burst of dark energy may damage it." + "A massive burst of dark energy may damage it.", ), map_icon=DoorMapIcon.Dark, shield_model="darkburst", @@ -189,7 +185,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A massive burst of light energy may damage it." + "A massive burst of light energy may damage it.", ), map_icon=DoorMapIcon.Light, shield_model="sunburst", @@ -202,7 +198,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to most weapons. " - "A massive burst of light and dark energy may damage it." + "A massive burst of light and dark energy may damage it.", ), map_icon=DoorMapIcon.Annihilator, shield_model="sonic_boom", @@ -213,10 +209,11 @@ shell_color=Color(0.64, 0.34, 0, 1), scan_text=( "There is a Luminoth barrier on the door blocking access. ", - "Analysis indicates that the barrier is linked to the energy of Agon. Return the energy to the Agon Temple." + "Analysis indicates that the barrier is linked to the energy of Agon. " + "Return the energy to the Agon Temple.", ), map_icon=DoorMapIcon.AgonEnergy, - planetary_energy_item_id=-1 # TODO + planetary_energy_item_id=-1, # TODO ), "TorvusEnergy": dock_type.PlanetaryEnergyDoorType( name="TorvusEnergy", @@ -225,10 +222,10 @@ scan_text=( "There is a Luminoth barrier on the door blocking access. ", "Analysis indicates that the barrier is linked to the energy of Torvus. " - "Return the energy to the Torvus Temple." + "Return the energy to the Torvus Temple.", ), map_icon=DoorMapIcon.TorvusEnergy, - planetary_energy_item_id=-1 # TODO + planetary_energy_item_id=-1, # TODO ), "SanctuaryEnergy": dock_type.PlanetaryEnergyDoorType( name="SanctuaryEnergy", @@ -237,20 +234,20 @@ scan_text=( "There is a Luminoth barrier on the door blocking access. ", "Analysis indicates that the barrier is linked to the energy of Sanctuary. " - "Return the energy to the Sanctuary Temple." + "Return the energy to the Sanctuary Temple.", ), map_icon=DoorMapIcon.AgonEnergy, - planetary_energy_item_id=-1 # TODO + planetary_energy_item_id=-1, # TODO ), "EchoVisor": dock_type.EchoVisorDoorType( name="EchoVisor", vulnerability=normal_vuln, shell_model=dark_door_model, - shell_color=Color(1/16, 1/16, 1/16, 1), + shell_color=Color(1 / 16, 1 / 16, 1 / 16, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", "Sonic detection gear needed to interface with this system. " - "Neutralizing the control emitter may disable it." + "Neutralizing the control emitter may disable it.", ), map_icon=DoorMapIcon.EchoVisor, shield_model="echo_visor", @@ -265,7 +262,7 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Scans indicate presence of a control system. " - "Interface method unknown. Control units not present in the visible spectrum or current timespace. " + "Interface method unknown. Control units not present in the visible spectrum or current timespace. ", ), map_icon=DoorMapIcon.DarkVisor, shield_model="dark_visor", @@ -274,15 +271,12 @@ ), "Cannon": dock_type.BlastShieldDoorType( name="Cannon", - vulnerability=dataclasses.replace( - resist_all_vuln, - cannon_ball=vulnerable_no_splash - ), + vulnerability=dataclasses.replace(resist_all_vuln, cannon_ball=vulnerable_no_splash), shell_model=normal_door_model, - shell_color=Color(1, 1/3, 0, 1), + shell_color=Color(1, 1 / 3, 0, 1), scan_text=( "There is a Blast Shield on the door blocking access. ", - "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Cannon Ball may damage it." + "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Cannon Ball may damage it.", ), map_icon=DoorMapIcon.Boost, shield_model="cannon_ball", @@ -301,10 +295,10 @@ scan_text=( "There is a Blast Shield on the door blocing access. ", "Analysis indicates that the Blast Shield is invulnerable to normal beam fire. " - "The Charge Beam may damage it." + "The Charge Beam may damage it.", ), map_icon=DoorMapIcon.Normal, - shield_model="charge_beam" + shield_model="charge_beam", ), "Power": dock_type.NormalDoorType( name="Power", @@ -313,8 +307,8 @@ scan_text=( "There is a Blast Shield on the door blocking access. ", "Analysis indicates that the Blast Shield is invulnerable to light and dark energy. " - "The Power Beam may damage it." + "The Power Beam may damage it.", ), - map_icon=DoorMapIcon.PowerBomb # TODO: give it its own? - ) + map_icon=DoorMapIcon.PowerBomb, # TODO: give it its own? + ), } diff --git a/src/open_prime_rando/echoes/dock_lock_rando/map_icons.py b/src/open_prime_rando/echoes/dock_lock_rando/map_icons.py index 3524121..fa68604 100644 --- a/src/open_prime_rando/echoes/dock_lock_rando/map_icons.py +++ b/src/open_prime_rando/echoes/dock_lock_rando/map_icons.py @@ -38,7 +38,7 @@ class DoorMapIcon(IntEnum): @property def colors(self) -> DoorIconColors: - return _ALL_COLORS.get(self, DoorIconColors(0xff00ffff)) + return _ALL_COLORS.get(self, DoorIconColors(0xFF00FFFF)) @staticmethod def get_surface_colors_as_bytes() -> bytes: @@ -51,27 +51,23 @@ def get_door_index_bounds() -> tuple[int, int]: _ALL_COLORS = { - DoorMapIcon.Normal: DoorIconColors(0x3379bfff), - DoorMapIcon.Missile: DoorIconColors(0xd63333ff), - DoorMapIcon.Dark: DoorIconColors(0x000000ff, 0x898989ff), - DoorMapIcon.Annihilator: DoorIconColors(0x4b4b4bff), - DoorMapIcon.Light: DoorIconColors(0xffffffff), - DoorMapIcon.SuperMissile: DoorIconColors(0x50a148ff), - DoorMapIcon.SeekerMissile: DoorIconColors(0x794f77ff), - DoorMapIcon.PowerBomb: DoorIconColors(0xeae50bff), - - DoorMapIcon.AgonEnergy: DoorIconColors(0xa45600ff), - DoorMapIcon.TorvusEnergy: DoorIconColors(0x4e9761ff), - DoorMapIcon.SanctuaryEnergy: DoorIconColors(0x56789dff), - - DoorMapIcon.ScanVisor: DoorIconColors(0x007f7fff), - DoorMapIcon.DarkVisor: DoorIconColors(0x660000ff), - DoorMapIcon.EchoVisor: DoorIconColors(0xcc7a00ff), - - DoorMapIcon.ScrewAttack: DoorIconColors(0xed94d4ff), - DoorMapIcon.Bomb: DoorIconColors(0x55557fff), - DoorMapIcon.Boost: DoorIconColors(0xff5500ff), + DoorMapIcon.Normal: DoorIconColors(0x3379BFFF), + DoorMapIcon.Missile: DoorIconColors(0xD63333FF), + DoorMapIcon.Dark: DoorIconColors(0x000000FF, 0x898989FF), + DoorMapIcon.Annihilator: DoorIconColors(0x4B4B4BFF), + DoorMapIcon.Light: DoorIconColors(0xFFFFFFFF), + DoorMapIcon.SuperMissile: DoorIconColors(0x50A148FF), + DoorMapIcon.SeekerMissile: DoorIconColors(0x794F77FF), + DoorMapIcon.PowerBomb: DoorIconColors(0xEAE50BFF), + DoorMapIcon.AgonEnergy: DoorIconColors(0xA45600FF), + DoorMapIcon.TorvusEnergy: DoorIconColors(0x4E9761FF), + DoorMapIcon.SanctuaryEnergy: DoorIconColors(0x56789DFF), + DoorMapIcon.ScanVisor: DoorIconColors(0x007F7FFF), + DoorMapIcon.DarkVisor: DoorIconColors(0x660000FF), + DoorMapIcon.EchoVisor: DoorIconColors(0xCC7A00FF), + DoorMapIcon.ScrewAttack: DoorIconColors(0xED94D4FF), + DoorMapIcon.Bomb: DoorIconColors(0x55557FFF), + DoorMapIcon.Boost: DoorIconColors(0xFF5500FF), # DoorMapIcon.Grapple: None, - - DoorMapIcon.Disabled: DoorIconColors(0x202020ff), + DoorMapIcon.Disabled: DoorIconColors(0x202020FF), } diff --git a/src/open_prime_rando/echoes/elevators/auto_enabled_elevator_patches.py b/src/open_prime_rando/echoes/elevators/auto_enabled_elevator_patches.py index 20ec85c..3bf671b 100644 --- a/src/open_prime_rando/echoes/elevators/auto_enabled_elevator_patches.py +++ b/src/open_prime_rando/echoes/elevators/auto_enabled_elevator_patches.py @@ -2,44 +2,42 @@ from retro_data_structures.properties.echoes.archetypes.EditorProperties import EditorProperties from retro_data_structures.properties.echoes.objects.Timer import Timer -import open_prime_rando.echoes.asset_ids.agon_wastes as agon_wastes -import open_prime_rando.echoes.asset_ids.great_temple as great_temple -import open_prime_rando.echoes.asset_ids.sanctuary_fortress as sanctuary_fortress -import open_prime_rando.echoes.asset_ids.temple_grounds as temple_grounds -import open_prime_rando.echoes.asset_ids.torvus_bog as torvus_bog -import open_prime_rando.echoes.asset_ids.world as world +from open_prime_rando.echoes.asset_ids import ( + agon_wastes, + great_temple, + sanctuary_fortress, + temple_grounds, + torvus_bog, + world, +) from open_prime_rando.patcher_editor import PatcherEditor ELEVATOR_MEMORY_RELAY_PER_MREA = { world.GREAT_TEMPLE_MLVL: { great_temple.TEMPLE_TRANSPORT_A_MREA: 0x00000107, - great_temple.TEMPLE_TRANSPORT_B_MREA: 0x0008002f, + great_temple.TEMPLE_TRANSPORT_B_MREA: 0x0008002F, great_temple.TEMPLE_TRANSPORT_C_MREA: 0x00060046, }, - world.TEMPLE_GROUNDS_MLVL: { temple_grounds.TRANSPORT_TO_AGON_WASTES_MREA: 0x00180040, temple_grounds.TRANSPORT_TO_SANCTUARY_FORTRESS_MREA: 0x00330058, - temple_grounds.TRANSPORT_TO_TORVUS_BOG_MREA: 0x001e0061, + temple_grounds.TRANSPORT_TO_TORVUS_BOG_MREA: 0x001E0061, }, - world.AGON_WASTES_MLVL: { - agon_wastes.TRANSPORT_TO_SANCTUARY_FORTRESS_MREA: 0x002d0064, - agon_wastes.TRANSPORT_TO_TEMPLE_GROUNDS_MREA: 0x0000003f, - agon_wastes.TRANSPORT_TO_TORVUS_BOG_MREA: 0x0013004d, + agon_wastes.TRANSPORT_TO_SANCTUARY_FORTRESS_MREA: 0x002D0064, + agon_wastes.TRANSPORT_TO_TEMPLE_GROUNDS_MREA: 0x0000003F, + agon_wastes.TRANSPORT_TO_TORVUS_BOG_MREA: 0x0013004D, }, - world.TORVUS_BOG_MLVL: { torvus_bog.TRANSPORT_TO_AGON_WASTES_MREA: 0x00210038, - torvus_bog.TRANSPORT_TO_SANCTUARY_FORTRESS_MREA: 0x0045004b, - torvus_bog.TRANSPORT_TO_TEMPLE_GROUNDS_MREA: 0x0000002e, + torvus_bog.TRANSPORT_TO_SANCTUARY_FORTRESS_MREA: 0x0045004B, + torvus_bog.TRANSPORT_TO_TEMPLE_GROUNDS_MREA: 0x0000002E, }, - world.SANCTUARY_FORTRESS_MLVL: { sanctuary_fortress.TRANSPORT_TO_AGON_WASTES_MREA: 0x00130088, sanctuary_fortress.TRANSPORT_TO_TEMPLE_GROUNDS_MREA: 0x00000035, sanctuary_fortress.TRANSPORT_TO_TORVUS_BOG_MREA: 0x00190036, - } + }, } @@ -50,13 +48,12 @@ def apply_auto_enabled_elevators_patch(editor: PatcherEditor): for mlvl_id, areas in ELEVATOR_MEMORY_RELAY_PER_MREA.items(): for mrea_id, memory_relay_id in areas.items(): area = editor.get_area(mlvl_id, mrea_id) - timer = area.get_layer("Default").add_instance_with(Timer( - editor_properties=EditorProperties( - name="Timer - Auto enable elevator", - active=False - ), - time=0.001, - auto_start=True - )) + timer = area.get_layer("Default").add_instance_with( + Timer( + editor_properties=EditorProperties(name="Timer - Auto enable elevator", active=False), + time=0.001, + auto_start=True, + ) + ) relay = area.get_instance(memory_relay_id) timer.add_connection(State.Zero, Message.Activate, relay) diff --git a/src/open_prime_rando/echoes/elevators/elevator_rando.py b/src/open_prime_rando/echoes/elevators/elevator_rando.py index 982a335..6d3f6a3 100644 --- a/src/open_prime_rando/echoes/elevators/elevator_rando.py +++ b/src/open_prime_rando/echoes/elevators/elevator_rando.py @@ -5,9 +5,15 @@ from open_prime_rando.patcher_editor import PatcherEditor -def patch_elevator(editor: PatcherEditor, area: Area, elevator_id: int, - target_mlvl: int, target_mrea: int, target_strg: int, target_name: str): - +def patch_elevator( + editor: PatcherEditor, + area: Area, + elevator_id: int, + target_mlvl: int, + target_mrea: int, + target_strg: int, + target_name: str, +): elevator = area.get_instance(elevator_id) with elevator.edit_properties(WorldTeleporter) as props: props.world = target_mlvl @@ -15,5 +21,8 @@ def patch_elevator(editor: PatcherEditor, area: Area, elevator_id: int, if target_strg is not None: strg = editor.get_file(target_strg, Strg) - strg.set_string(1, f"Access to &push;&main-color=#FF3333;{target_name} &pop;granted. " - f"Step into the hologram to activate elevator.") + strg.set_string( + 1, + f"Access to &push;&main-color=#FF3333;{target_name} &pop;granted. " + f"Step into the hologram to activate elevator.", + ) diff --git a/src/open_prime_rando/echoes/inverted/__init__.py b/src/open_prime_rando/echoes/inverted/__init__.py index abfa996..f7f8321 100644 --- a/src/open_prime_rando/echoes/inverted/__init__.py +++ b/src/open_prime_rando/echoes/inverted/__init__.py @@ -1,4 +1,3 @@ - from retro_data_structures.formats import Mapa, Mlvl from retro_data_structures.formats.mrea import Area from retro_data_structures.formats.script_layer import ScriptLayer @@ -12,17 +11,20 @@ from open_prime_rando.patcher_editor import PatcherEditor _WORLDS = [ - world_ids.TEMPLE_GROUNDS_MLVL, world_ids.AGON_WASTES_MLVL, world_ids.TORVUS_BOG_MLVL, - world_ids.SANCTUARY_FORTRESS_MLVL, world_ids.GREAT_TEMPLE_MLVL, + world_ids.TEMPLE_GROUNDS_MLVL, + world_ids.AGON_WASTES_MLVL, + world_ids.TORVUS_BOG_MLVL, + world_ids.SANCTUARY_FORTRESS_MLVL, + world_ids.GREAT_TEMPLE_MLVL, ] _AREAS_TO_SKIP = { - 0x775664a5, # 00_scandummy - 0x752b2850, # game_end_part1 - 0x9d221f4a, # game_end_part2 - 0xc5250dbc, # game_end_part3 - 0x9641773f, # game_end_part4 - 0xce4665c9, # game_end_part5 - 0x4d4e5400, # Menu + 0x775664A5, # 00_scandummy + 0x752B2850, # game_end_part1 + 0x9D221F4A, # game_end_part2 + 0xC5250DBC, # game_end_part3 + 0x9641773F, # game_end_part4 + 0xCE4665C9, # game_end_part5 + 0x4D4E5400, # Menu } @@ -48,8 +50,7 @@ def _swap_dark_world(editor: PatcherEditor): mapa.raw.type = not is_dark_world -def _copy_safe_zones(dark: Area, dark_layer: ScriptLayer, light_layer: ScriptLayer, - special_ids: set): +def _copy_safe_zones(dark: Area, dark_layer: ScriptLayer, light_layer: ScriptLayer, special_ids: set): copied_safe_zones = {} for instance in list(dark_layer.instances): @@ -68,11 +69,13 @@ def _copy_safe_zones(dark: Area, dark_layer: ScriptLayer, light_layer: ScriptLay return copied_safe_zones -def _copy_safe_zone_crystal(copied_crystal: ScriptInstance, - instance: ScriptInstance, - copied_safe_zones: dict[InstanceId, ScriptInstance], - dark: Area, special_ids: set[int], - ): +def _copy_safe_zone_crystal( + copied_crystal: ScriptInstance, + instance: ScriptInstance, + copied_safe_zones: dict[InstanceId, ScriptInstance], + dark: Area, + special_ids: set[int], +): targets_special = False for conn in instance.connections: @@ -86,7 +89,8 @@ def _copy_safe_zone_crystal(copied_crystal: ScriptInstance, weird_target = dark.get_instance(conn.target) print( f"Crystal {instance.name} at {dark.name} has unexpected connections " - f"to {weird_target} ({weird_target.name})") + f"to {weird_target} ({weird_target.name})" + ) continue copied_crystal.add_connection(conn.state, conn.message, target) diff --git a/src/open_prime_rando/echoes/small_randomizations/echo_locks.py b/src/open_prime_rando/echoes/small_randomizations/echo_locks.py index 9b77635..90692bb 100644 --- a/src/open_prime_rando/echoes/small_randomizations/echo_locks.py +++ b/src/open_prime_rando/echoes/small_randomizations/echo_locks.py @@ -16,7 +16,7 @@ ECHO_LOCK_MREAS = [ (SANCTUARY_FORTRESS_MLVL, MAIN_GYRO_CHAMBER_MREA), (SANCTUARY_FORTRESS_MLVL, SENTINELS_PATH_MREA), - (TEMPLE_GROUNDS_MLVL, PROFANE_PATH_MREA) + (TEMPLE_GROUNDS_MLVL, PROFANE_PATH_MREA), ] ECHO_LOCK_STATES = [State.Zero, State.InternalState00, State.InternalState01, State.InternalState02] ECHO_LOCK_SOUNDS = [1005, 1006, 1007] @@ -34,7 +34,7 @@ def randomize_echo_locks(editor: PatcherEditor, rng: random.Random): 1, "Sonic detection gear needed to interface with this system." " Shoot the Echo Key Beam emitter with a sonic pulse to activate it." - f" It will then fire a blast of &push;&main-color=#FF3333;{pitch}-pitched&pop; sound at an Echo Gate lock." + f" It will then fire a blast of &push;&main-color=#FF3333;{pitch}-pitched&pop; sound at an Echo Gate lock.", ) strg_id = editor.add_new_asset(f"accessible_echo_lock_{pitch}.STRG", key_strg) @@ -80,8 +80,9 @@ def randomize_echo_locks(editor: PatcherEditor, rng: random.Random): props.is_open = i == solution[0] # edit scan to indicate the solution - solution_text = ("Sonic detection gear needed to interface with this system. " - "The combination of its sonic locks is:\n") + solution_text = ( + "Sonic detection gear needed to interface with this system. " "The combination of its sonic locks is:\n" + ) solution_text += ", ".join(["Low", "Medium", "High"][key] for key in solution) gate_strg.set_string(1, solution_text) strg_id = editor.add_new_asset(f"accessible_echo_gate_{mrea_id}.STRG", gate_strg) @@ -94,4 +95,3 @@ def randomize_echo_locks(editor: PatcherEditor, rng: random.Random): poi.scan_info.scannable_info0 = scan_id area.update_all_dependencies() - diff --git a/src/open_prime_rando/echoes/small_randomizations/minigyro_chamber.py b/src/open_prime_rando/echoes/small_randomizations/minigyro_chamber.py index 958b0e9..533835e 100644 --- a/src/open_prime_rando/echoes/small_randomizations/minigyro_chamber.py +++ b/src/open_prime_rando/echoes/small_randomizations/minigyro_chamber.py @@ -30,6 +30,7 @@ def color(self) -> str: def text(self) -> str: return f"&push;&main-color={self.color};{self.name}&pop;" + GYRO_STATES = [ State.InternalState00, State.InternalState01, @@ -60,7 +61,7 @@ def randomize_minigyro_chamber(editor: PatcherEditor, rng: random.Random): stop_gyros[solution[3].value].add_connection(State.Zero, Message.Play, jingle) scan = editor.get_file(0xFBFF349D, Strg) - solution_text = '\n'.join(gyro.text for gyro in solution) + solution_text = "\n".join(gyro.text for gyro in solution) scan.set_string(1, f"Safety lockdown code is as follows:\n\n\n{solution_text}") area.update_all_dependencies() diff --git a/src/open_prime_rando/echoes/small_randomizations/rubiks.py b/src/open_prime_rando/echoes/small_randomizations/rubiks.py index 35947c1..101ca0b 100644 --- a/src/open_prime_rando/echoes/small_randomizations/rubiks.py +++ b/src/open_prime_rando/echoes/small_randomizations/rubiks.py @@ -17,15 +17,27 @@ RUBIKS_CUBES = { "Puzzle 1": [ - 0x240013, 0x2401A4, 0x240145, - 0x24010A, 0x24000E, 0x240192, - 0x240139, 0x24013A, 0x240148, + 0x240013, + 0x2401A4, + 0x240145, + 0x24010A, + 0x24000E, + 0x240192, + 0x240139, + 0x24013A, + 0x240148, ], "Puzzle 2": [ - 0x2402BA, 0x2402AD, 0x2402A6, - 0x2401C6, 0x2401BE, 0x2401C0, - 0x24028B, 0x2401C3, 0x240290, - ] + 0x2402BA, + 0x2402AD, + 0x2402A6, + 0x2401C6, + 0x2401BE, + 0x2401C0, + 0x24028B, + 0x2401C3, + 0x240290, + ], } @@ -66,7 +78,7 @@ def txtr(self) -> RawResource: state=State.InternalState02, cmdl=0x8F25F715, old_txtr=0xFED23B81, - ) + ), } @@ -88,9 +100,15 @@ def randomize_rubiks_puzzles(editor: PatcherEditor, rng: random.Random): for puzzle_name, cubes in RUBIKS_CUBES.items(): solution = [ - COLORS["RED"], COLORS["RED"], COLORS["RED"], - COLORS["GREEN"], COLORS["GREEN"], COLORS["GREEN"], - COLORS["BLUE"], COLORS["BLUE"], COLORS["BLUE"], + COLORS["RED"], + COLORS["RED"], + COLORS["RED"], + COLORS["GREEN"], + COLORS["GREEN"], + COLORS["GREEN"], + COLORS["BLUE"], + COLORS["BLUE"], + COLORS["BLUE"], ] rng.shuffle(solution) @@ -150,40 +168,28 @@ def patch_upstairs_puzzle_transform(editor: PatcherEditor): big_lasers = { # red in 0x2401C1: Transform( - position=Vector(60.9426, 250.63, 12.0627), - rotation=Vector(0.0, 0.1, 164.48), - scale=Vector(0.9, 1.0, 1.0) + position=Vector(60.9426, 250.63, 12.0627), rotation=Vector(0.0, 0.1, 164.48), scale=Vector(0.9, 1.0, 1.0) ), # red out 0x24028A: Transform( - position=Vector(76.1287, 250.63, 12.0627), - rotation=Vector(0.0, -0.7, -164.08), - scale=Vector(0.9, 1.0, 1.0) + position=Vector(76.1287, 250.63, 12.0627), rotation=Vector(0.0, -0.7, -164.08), scale=Vector(0.9, 1.0, 1.0) ), # green in 0x2401C2: Transform( - position=Vector(60.342598, 250.13, 12.3027), - rotation=Vector(0.0, 0.5, -187.5), - scale=Vector(1.0, 1.0, 1.0) + position=Vector(60.342598, 250.13, 12.3027), rotation=Vector(0.0, 0.5, -187.5), scale=Vector(1.0, 1.0, 1.0) ), # green out 0x2402BB: Transform( - position=Vector(76.62867, 250.11, 12.2827), - rotation=Vector(0.0, -0.5, 188.0), - scale=Vector(0.9, 1.0, 1.0) + position=Vector(76.62867, 250.11, 12.2827), rotation=Vector(0.0, -0.5, 188.0), scale=Vector(0.9, 1.0, 1.0) ), # blue in 0x24028F: Transform( - position=Vector(60.142597, 249.51, 12.5127), - rotation=Vector(0.0, 0.5, -180.0), - scale=Vector(1.0, 1.0, 1.0) + position=Vector(60.142597, 249.51, 12.5127), rotation=Vector(0.0, 0.5, -180.0), scale=Vector(1.0, 1.0, 1.0) ), # blue out 0x2402B6: Transform( - position=Vector(77.1287, 249.51, 12.5127), - rotation=Vector(0.0, -0.5, -180.0), - scale=Vector(1.0, 1.0, 1.0) - ) + position=Vector(77.1287, 249.51, 12.5127), rotation=Vector(0.0, -0.5, -180.0), scale=Vector(1.0, 1.0, 1.0) + ), } for laser_id, transform in big_lasers.items(): diff --git a/src/open_prime_rando/echoes/specific_area_patches/rebalance_patches.py b/src/open_prime_rando/echoes/specific_area_patches/rebalance_patches.py index 1d4c225..2069a18 100644 --- a/src/open_prime_rando/echoes/specific_area_patches/rebalance_patches.py +++ b/src/open_prime_rando/echoes/specific_area_patches/rebalance_patches.py @@ -55,24 +55,12 @@ def landing_site(editor: PatcherEditor): # FIXME: use layers API area.get_layer("1st Pass - Intro Cinematic").active = not remove_intro - for layer_name in ( - "Save Station Load", - "Ship Repair", - "Luminoth Key Bearer", - "WAR CHEST" - ): + for layer_name in ("Save Station Load", "Ship Repair", "Luminoth Key Bearer", "WAR CHEST"): area.get_layer(layer_name).active = remove_intro if remove_intro: - timer = area.get_layer("Default").add_instance_with(Timer( - time=0.01, - auto_start=True - )) - for inst in ( - "Keep Samus Ship", - "Savestation Recharge Always Plays", - "Ambient Music Memory Relay" - ): + timer = area.get_layer("Default").add_instance_with(Timer(time=0.01, auto_start=True)) + for inst in ("Keep Samus Ship", "Savestation Recharge Always Plays", "Ambient Music Memory Relay"): # TODO: unclear whether the timer is necessary, or if we can just activate these immediately timer.add_connection(State.Zero, Message.Activate, area.get_instance(inst)) @@ -84,8 +72,7 @@ def bionergy_production(editor: PatcherEditor): area = editor.get_area(AGON_WASTES_MLVL, agon_wastes.BIOENERGY_PRODUCTION_MREA) counter = area.get_instance("Dead Pirates") - counter.add_connection(State.MaxReached, Message.Deactivate, - area.get_instance("Turn On Flying Pirates")) + counter.add_connection(State.MaxReached, Message.Deactivate, area.get_instance("Turn On Flying Pirates")) def _disable_layer_controllers(area: Area, layer_controllers: Iterable[InstanceRef]): @@ -112,18 +99,20 @@ def remove_luminoth_barriers(editor: PatcherEditor): # Dark Torvus Energy Controller dark_torvus = editor.get_area(TORVUS_BOG_MLVL, torvus_bog.DARK_TORVUS_ENERGY_CONTROLLER_MREA) - _disable_layer_controllers(dark_torvus, ( - "Increment - 0A_Swamp - Luminoth Barriers", - "Increment - 04_Swamp - Luminoth Barriers" - )) + _disable_layer_controllers( + dark_torvus, ("Increment - 0A_Swamp - Luminoth Barriers", "Increment - 04_Swamp - Luminoth Barriers") + ) # Hive Energy Controller hive = editor.get_area(SANCTUARY_FORTRESS_MLVL, sanctuary_fortress.HIVE_ENERGY_CONTROLLER_MREA) - _disable_layer_controllers(hive, ( - "Increment - 0P_Cliff - Luminoth Barriers", - "Increment - 0A_Cliff - Luminoth Barriers", - "Increment - 0Q_Cliff - Luminoth Barriers", - )) + _disable_layer_controllers( + hive, + ( + "Increment - 0P_Cliff - Luminoth Barriers", + "Increment - 0A_Cliff - Luminoth Barriers", + "Increment - 0Q_Cliff - Luminoth Barriers", + ), + ) def torvus_energy_controller(editor: PatcherEditor): @@ -131,11 +120,14 @@ def torvus_energy_controller(editor: PatcherEditor): Don't remove the Torvus Temple fight. """ area = editor.get_area(TORVUS_BOG_MLVL, torvus_bog.TORVUS_ENERGY_CONTROLLER_MREA) - _disable_layer_controllers(area, ( - "DECREMENT 04_Swamp_Temple 1st Pass", - "Decrement - 04_Swamp_Temple - 1st Pass", - "Increment - 04_Swamp_Temple - 2ndPass" - )) + _disable_layer_controllers( + area, + ( + "DECREMENT 04_Swamp_Temple 1st Pass", + "Decrement - 04_Swamp_Temple - 1st Pass", + "Increment - 04_Swamp_Temple - 2ndPass", + ), + ) def hive_tunnel(editor: PatcherEditor): @@ -156,7 +148,7 @@ def agon_temple(editor: PatcherEditor): """ area = editor.get_area(AGON_WASTES_MLVL, agon_wastes.AGON_TEMPLE_MREA) - area.get_layer("Lock Doors").active = False # FIXME: use layers API + area.get_layer("Lock Doors").active = False # FIXME: use layers API def temple_sanctuary(editor: PatcherEditor): @@ -186,12 +178,14 @@ def main_reactor(editor: PatcherEditor): area = editor.get_area(AGON_WASTES_MLVL, agon_wastes.MAIN_REACTOR_MREA) layer_switcher = area.get_instance("Switch Layers To Post-Dark Samus") - layer_controller = area.get_layer("Default").add_instance_with(ScriptLayerController( - layer=LayerSwitch( - area_id=agon_wastes.BIOSTORAGE_STATION_INTERNAL_ID, - layer_number=3 # 1st Pass + layer_controller = area.get_layer("Default").add_instance_with( + ScriptLayerController( + layer=LayerSwitch( + area_id=agon_wastes.BIOSTORAGE_STATION_INTERNAL_ID, + layer_number=3, # 1st Pass + ) ) - )) + ) layer_switcher.add_connection(State.Zero, Message.Decrement, layer_controller) @@ -224,10 +218,16 @@ def gfmc_compound(editor: PatcherEditor): area = editor.get_area(TEMPLE_GROUNDS_MLVL, temple_grounds.GFMC_COMPOUND_MREA) gate_instances = ( - 0x2b00db, 0x2b00dc, 0x2b00ff, 0x2b0101, - 0x2b013c, 0x2b0238, 0x2b0288, 0x2b02e9, + 0x2B00DB, + 0x2B00DC, + 0x2B00FF, + 0x2B0101, + 0x2B013C, + 0x2B0238, + 0x2B0288, + 0x2B02E9, ) - gate_instances += tuple(range(0x2b0277, 0x2b0287)) + gate_instances += tuple(range(0x2B0277, 0x2B0287)) for raw_id in gate_instances: # TODO: implement a function to move an instance from one layer to another in RDS inst_id = InstanceId(raw_id) diff --git a/src/open_prime_rando/echoes/specific_area_patches/required_fixes.py b/src/open_prime_rando/echoes/specific_area_patches/required_fixes.py index 41ce018..f6ee7a1 100644 --- a/src/open_prime_rando/echoes/specific_area_patches/required_fixes.py +++ b/src/open_prime_rando/echoes/specific_area_patches/required_fixes.py @@ -91,9 +91,7 @@ def sacrificial_chamber(editor: PatcherEditor): def _patch_echo_gate_softlock( - area: Area, - counter: InstanceIdRef, - relays: Iterable[tuple[InstanceIdRef, InstanceIdRef]] + area: Area, counter: InstanceIdRef, relays: Iterable[tuple[InstanceIdRef, InstanceIdRef]] ): for shot_relay_id, memory_relay_id in relays: shot_relay = area.get_instance(shot_relay_id) @@ -109,11 +107,7 @@ def aerie(editor: PatcherEditor): """ area = editor.get_area(SANCTUARY_FORTRESS_MLVL, sanctuary_fortress.AERIE_MREA) - relays = ( - (0x410094, 0x41008D), - (0x410077, 0x41007F), - (0x4100B5, 0x4100B6) - ) + relays = ((0x410094, 0x41008D), (0x410077, 0x41007F), (0x4100B5, 0x4100B6)) _patch_echo_gate_softlock(area, 0x4100BE, relays) @@ -125,39 +119,32 @@ def main_research(editor: PatcherEditor): """ area = editor.get_area(SANCTUARY_FORTRESS_MLVL, sanctuary_fortress.MAIN_RESEARCH_MREA) - relays = ( - (0x0B02E6, 0x0B02DE), - (0x0B0303, 0x0B030D), - (0x0B02F6, 0x0B02FA) - ) + relays = ((0x0B02E6, 0x0B02DE), (0x0B0303, 0x0B030D), (0x0B02F6, 0x0B02FA)) _patch_echo_gate_softlock(area, 0x0B0315, relays) area.get_layer("Contraption").active = False portal_spawn = area.get_instance(0x0B0056) spawn_xfm = portal_spawn.get_properties_as(SpawnPoint).editor_properties.transform - contraption_controller = area.get_layer("Default").add_instance_with(ScriptLayerController( - editor_properties=EditorProperties( - name="Dynamic Increment Contraption", - transform=spawn_xfm - ), - layer=LayerSwitch( - area_id=sanctuary_fortress.MAIN_RESEARCH_INTERNAL_ID, - layer_number=area.get_layer("Contraption").index - ), - is_dynamic=True - )) - spider_controller = area.get_layer("Default").add_instance_with(ScriptLayerController( - editor_properties=EditorProperties( - name="Dynamic Increment Column Spiderball", - transform=spawn_xfm - ), - layer=LayerSwitch( - area_id=sanctuary_fortress.MAIN_RESEARCH_INTERNAL_ID, - layer_number=area.get_layer("Column Spiderball").index - ), - is_dynamic=True - )) + contraption_controller = area.get_layer("Default").add_instance_with( + ScriptLayerController( + editor_properties=EditorProperties(name="Dynamic Increment Contraption", transform=spawn_xfm), + layer=LayerSwitch( + area_id=sanctuary_fortress.MAIN_RESEARCH_INTERNAL_ID, layer_number=area.get_layer("Contraption").index + ), + is_dynamic=True, + ) + ) + spider_controller = area.get_layer("Default").add_instance_with( + ScriptLayerController( + editor_properties=EditorProperties(name="Dynamic Increment Column Spiderball", transform=spawn_xfm), + layer=LayerSwitch( + area_id=sanctuary_fortress.MAIN_RESEARCH_INTERNAL_ID, + layer_number=area.get_layer("Column Spiderball").index, + ), + is_dynamic=True, + ) + ) for controller in (contraption_controller, spider_controller): portal_spawn.add_connection(State.Arrived, Message.Load, controller) @@ -185,40 +172,37 @@ def gfmc_compound(editor: PatcherEditor): area = editor.get_area(TEMPLE_GROUNDS_MLVL, temple_grounds.GFMC_COMPOUND_MREA) pickup_xfm = area.get_instance(0x2B0324).get_properties_as(Pickup).editor_properties.transform - ship_trigger = area.get_layer("Default").add_instance_with(Trigger( - editor_properties=EditorProperties( - name="Show Ship Missile HudMemo", - transform=Transform( - position=pickup_xfm.position, - rotation=Vector(0.0, 0.0, 45.0), - scale=Vector(50.0, 50.0, 10.0) - ) - ), - deactivate_on_enter=True - )) + ship_trigger = area.get_layer("Default").add_instance_with( + Trigger( + editor_properties=EditorProperties( + name="Show Ship Missile HudMemo", + transform=Transform( + position=pickup_xfm.position, rotation=Vector(0.0, 0.0, 45.0), scale=Vector(50.0, 50.0, 10.0) + ), + ), + deactivate_on_enter=True, + ) + ) strg_id, _ = editor.create_strg( - "gfmc_jump_hudmemo.STRG", - ["Defeating Jump Guardian is required for this item to appear."] + "gfmc_jump_hudmemo.STRG", ["Defeating Jump Guardian is required for this item to appear."] + ) + hud_memo = area.get_layer("Default").add_instance_with( + HUDMemo( + editor_properties=EditorProperties(name="Ship Missile HudMemo", transform=pickup_xfm), + display_time=6.0, + display_type=0, + string=strg_id, + ) ) - hud_memo = area.get_layer("Default").add_instance_with(HUDMemo( - editor_properties=EditorProperties( - name="Ship Missile HudMemo", - transform=pickup_xfm - ), - display_time=6.0, - display_type=0, - string=strg_id - )) ship_trigger.add_connection(State.Entered, Message.SetToZero, hud_memo) - timer = area.get_layer("Space Jump").add_instance_with(Timer( - editor_properties=EditorProperties( - name="Disable Ship Missile Trigger", - transform=pickup_xfm - ), - time=0.1, - auto_start=True - )) + timer = area.get_layer("Space Jump").add_instance_with( + Timer( + editor_properties=EditorProperties(name="Disable Ship Missile Trigger", transform=pickup_xfm), + time=0.1, + auto_start=True, + ) + ) timer.add_connection(State.Zero, Message.Deactivate, ship_trigger) @@ -251,22 +235,24 @@ def command_center_door(editor: PatcherEditor): area = editor.get_area(AGON_WASTES_MLVL, COMMAND_CENTER_MREA) default = area.get_layer("Default") # committing a crime until RDS supports unsigned ints - internal_area_id = struct.unpack('>l', struct.pack('>L', 0xAA657163))[0] + internal_area_id = struct.unpack(">l", struct.pack(">L", 0xAA657163))[0] poi = default.get_instance("Blast Door Activation") # Deactivate layers for layer in ("1st Pass Scripting", "1st pass parts"): - controller = default.add_instance_with(ScriptLayerController( - editor_properties=EditorProperties( - name=f"DYNAMIC Decrement {layer}", - ), - layer=LayerSwitch( - area_id=internal_area_id, - layer_number=area.get_layer(layer).index, - ), - is_dynamic=True, - )) + controller = default.add_instance_with( + ScriptLayerController( + editor_properties=EditorProperties( + name=f"DYNAMIC Decrement {layer}", + ), + layer=LayerSwitch( + area_id=internal_area_id, + layer_number=area.get_layer(layer).index, + ), + is_dynamic=True, + ) + ) poi.add_connection(State.ScanDone, Message.Decrement, controller) # Ensure the blast door instances are active for the cutscene diff --git a/src/open_prime_rando/echoes/specific_area_patches/version_differences.py b/src/open_prime_rando/echoes/specific_area_patches/version_differences.py index bfcc92e..636e29d 100644 --- a/src/open_prime_rando/echoes/specific_area_patches/version_differences.py +++ b/src/open_prime_rando/echoes/specific_area_patches/version_differences.py @@ -60,8 +60,7 @@ def path_of_eyes(editor: PatcherEditor, version: EchoesVersion): return LOG.warning( - "Path of Eyes version differences have not been patched! " - "Area collision edits are not yet implemented." + "Path of Eyes version differences have not been patched! " "Area collision edits are not yet implemented." ) @@ -78,7 +77,7 @@ def venomous_pond(editor: PatcherEditor, version: EchoesVersion): piggyplant.editor_properties.transform = Transform( position=Vector(17.589201, -148.844849, 37.929966), scale=Vector(1.5, 1.5, 1.5), - rotation=piggyplant.editor_properties.transform.rotation + rotation=piggyplant.editor_properties.transform.rotation, ) piggyplant.collision_model = 0x8E4170D5 diff --git a/src/open_prime_rando/echoes/suit_cosmetics/__init__.py b/src/open_prime_rando/echoes/suit_cosmetics/__init__.py index d2f1292..c939d52 100644 --- a/src/open_prime_rando/echoes/suit_cosmetics/__init__.py +++ b/src/open_prime_rando/echoes/suit_cosmetics/__init__.py @@ -14,7 +14,7 @@ def apply_custom_suits(editor: PatcherEditor, configuration: dict): for asset_id, filename in assets.items(): asset = custom_suit_assets.joinpath(filename) if not asset.exists(): - continue # some skins leave a few assets vanilla + continue # some skins leave a few assets vanilla res = RawResource( type="TXTR", diff --git a/src/open_prime_rando/echoes/vulnerabilities.py b/src/open_prime_rando/echoes/vulnerabilities.py index 0625dd6..e1ae274 100644 --- a/src/open_prime_rando/echoes/vulnerabilities.py +++ b/src/open_prime_rando/echoes/vulnerabilities.py @@ -11,21 +11,52 @@ resist_all_vuln = DamageVulnerability( - power=reflect, dark=reflect, light=reflect, annihilator=reflect, - power_charge=reflect, entangler=reflect, light_blast=reflect, sonic_boom=reflect, - super_missle=reflect, black_hole=reflect, sunburst=reflect, imploder=reflect, - - boost_ball=immune, cannon_ball=immune, screw_attack=immune, bomb=immune, power_bomb=immune, - missile=reflect, phazon=reflect, ai=immune, poison_water=immune, dark_water=immune, lava=immune, - area_damage_hot=immune, area_damage_cold=immune, area_damage_dark=immune, area_damage_light=immune, - weapon_vulnerability=immune, normal_safe_zone=immune, + power=reflect, + dark=reflect, + light=reflect, + annihilator=reflect, + power_charge=reflect, + entangler=reflect, + light_blast=reflect, + sonic_boom=reflect, + super_missle=reflect, + black_hole=reflect, + sunburst=reflect, + imploder=reflect, + boost_ball=immune, + cannon_ball=immune, + screw_attack=immune, + bomb=immune, + power_bomb=immune, + missile=reflect, + phazon=reflect, + ai=immune, + poison_water=immune, + dark_water=immune, + lava=immune, + area_damage_hot=immune, + area_damage_cold=immune, + area_damage_dark=immune, + area_damage_light=immune, + weapon_vulnerability=immune, + normal_safe_zone=immune, ) normal_vuln = dataclasses.replace( resist_all_vuln, - power=vulnerable, dark=vulnerable, light=vulnerable, annihilator=vulnerable, - power_charge=vulnerable, entangler=vulnerable, light_blast=vulnerable, sonic_boom=vulnerable, - super_missle=vulnerable, black_hole=vulnerable, sunburst=vulnerable, imploder=vulnerable, - - missile=vulnerable, bomb=vulnerable, power_bomb=vulnerable, + power=vulnerable, + dark=vulnerable, + light=vulnerable, + annihilator=vulnerable, + power_charge=vulnerable, + entangler=vulnerable, + light_blast=vulnerable, + sonic_boom=vulnerable, + super_missle=vulnerable, + black_hole=vulnerable, + sunburst=vulnerable, + imploder=vulnerable, + missile=vulnerable, + bomb=vulnerable, + power_bomb=vulnerable, ) diff --git a/src/open_prime_rando/echoes_patcher.py b/src/open_prime_rando/echoes_patcher.py index dbaf3d0..c1a9eab 100644 --- a/src/open_prime_rando/echoes_patcher.py +++ b/src/open_prime_rando/echoes_patcher.py @@ -1,11 +1,10 @@ - import json import logging from collections.abc import Callable from pathlib import Path +from typing import TYPE_CHECKING from retro_data_structures.asset_manager import FileProvider -from retro_data_structures.formats.mrea import Area from retro_data_structures.formats.strg import Strg from retro_data_structures.game_check import Game @@ -19,6 +18,9 @@ from open_prime_rando.patcher_editor import PatcherEditor from open_prime_rando.validator_with_default import DefaultValidatingDraft7Validator +if TYPE_CHECKING: + from retro_data_structures.formats.mrea import Area + LOG = logging.getLogger("echoes_patcher") @@ -27,8 +29,9 @@ def _read_schema(): return json.load(f) -def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dict], - status_update: Callable[[str, float], None]): +def apply_area_modifications( + editor: PatcherEditor, configuration: dict[str, dict], status_update: Callable[[str, float], None] +): num_areas = sum(len(world_config["areas"]) for world_config in configuration.values()) areas_processed = 0.0 @@ -36,21 +39,15 @@ def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dic world_meta = asset_ids.world.load_dedicated_file(world_name) mlvl = editor.get_mlvl(asset_ids.world.NAME_TO_ID_MLVL[world_name]) - mrea_to_name: dict[int, str] = { - mrea: name - for name, mrea in world_meta.NAME_TO_ID_MREA.items() - } + mrea_to_name: dict[int, str] = {mrea: name for name, mrea in world_meta.NAME_TO_ID_MREA.items()} - areas_by_name: dict[str, Area] = { - mrea_to_name[area.mrea_asset_id]: area - for area in mlvl.areas - } + areas_by_name: dict[str, Area] = {mrea_to_name[area.mrea_asset_id]: area for area in mlvl.areas} for i, (area_name, area) in enumerate(areas_by_name.items()): if area_name not in world_config["areas"]: continue - status_update(f"Processing {world_name} - {area_name}...", areas_processed/num_areas) + status_update(f"Processing {world_name} - {area_name}...", areas_processed / num_areas) areas_processed += 1 area_config = world_config["areas"][area_name] @@ -67,15 +64,24 @@ def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dic dock_name, dock_config["new_door_type"], dock_config.get("old_door_type"), - low_memory + low_memory, ) if "connect_to" in dock_config: dock_target = dock_config["connect_to"] - LOG.debug("Connecting dock %s of %s - %s to %s - %s", - dock_name, world_name, area_name, dock_target["area"], dock_target["dock"]) - area.connect_dock_to(dock_number, areas_by_name[dock_target["area"]], - world_meta.DOCK_NAMES[dock_target["area"]][dock_target["dock"]]) + LOG.debug( + "Connecting dock %s of %s - %s to %s - %s", + dock_name, + world_name, + area_name, + dock_target["area"], + dock_target["dock"], + ) + area.connect_dock_to( + dock_number, + areas_by_name[dock_target["area"]], + world_meta.DOCK_NAMES[dock_target["area"]][dock_target["dock"]], + ) for layer_name, layer_state in area_config["layers"].items(): LOG.debug("Setting layer %s of %s - %s to %s", layer_name, world_name, area_name, str(layer_state)) @@ -89,7 +95,7 @@ def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dic elevator["target_assets"]["world_asset_id"], elevator["target_assets"]["area_asset_id"], elevator["target_strg"], - elevator["target_name"] + elevator["target_name"], ) if area_config["new_name"] is not None: @@ -107,25 +113,23 @@ def apply_corrupted_memory_card_change(editor: PatcherEditor): table = editor.get_file(0x88E242D6, Strg) name_to_index = { - table.raw.name_table.name_array[entry.offset].string: entry.index - for entry in table.raw.name_table.name_entries + table.raw.name_table.name_array[entry.offset].string: entry.index for entry in table.raw.name_table.name_entries } table.set_string( name_to_index["CorruptedFile"], """The save file was created using a different -Randomizer ISO and must be deleted.""" - ) - table.set_string( - name_to_index["ChoiceDeleteCorruptedFile"], - "Delete Incompatible File" +Randomizer ISO and must be deleted.""", ) + table.set_string(name_to_index["ChoiceDeleteCorruptedFile"], "Delete Incompatible File") -def patch_paks(file_provider: FileProvider, - output_path: Path, - configuration: dict, - status_update: Callable[[str, float], None] = lambda s, _: LOG.info(s)): +def patch_paks( + file_provider: FileProvider, + output_path: Path, + configuration: dict, + status_update: Callable[[str, float], None] = lambda s, _: LOG.info(s), +): status_update(f"Will patch files at {file_provider}", 0) output_path.joinpath("files").mkdir(parents=True, exist_ok=True) output_path.joinpath("files", "opr_patcher_data.json").write_text(json.dumps(configuration)) diff --git a/src/open_prime_rando/patcher_editor.py b/src/open_prime_rando/patcher_editor.py index 03ba6a4..36b2146 100644 --- a/src/open_prime_rando/patcher_editor.py +++ b/src/open_prime_rando/patcher_editor.py @@ -58,10 +58,11 @@ def flush_modified_assets(self): executor.submit(self.replace_asset, name, resource) self.memory_files = {} - def add_file(self, - name: str, - asset: Resource, - ) -> AssetId: + def add_file( + self, + name: str, + asset: Resource, + ) -> AssetId: asset_id = crc32(name) self.register_custom_asset_name(name, asset_id) self.add_new_asset(name, asset, ()) @@ -85,24 +86,27 @@ def add_or_replace_custom_asset(self, name: str, new_data: Resource) -> AssetId: asset_id = self.add_file(name, new_data) return asset_id - def create_strg(self, - name: str, - strings: str | typing.Iterable[str] = (), - ) -> tuple[AssetId, Strg]: + def create_strg( + self, + name: str, + strings: str | typing.Iterable[str] = (), + ) -> tuple[AssetId, Strg]: template_id = None if self.target_game == Game.ECHOES: # Strings/Worlds/TempleHub/01_Temple_LandingSite.STRG template_id = 0x2E681FEF if template_id is None: - raise NotImplementedError() + raise NotImplementedError asset_id = self.duplicate_file(name, template_id) strg = self.get_file(asset_id, Strg) - if isinstance(strings, str): - strings = strings + # TODO: ??? + # if isinstance(strings, str): + # strings = strings + strings = list(strings) for lang in strg.languages: diff --git a/src/open_prime_rando/validator_with_default.py b/src/open_prime_rando/validator_with_default.py index 257b6b5..db540bd 100644 --- a/src/open_prime_rando/validator_with_default.py +++ b/src/open_prime_rando/validator_with_default.py @@ -10,11 +10,15 @@ def set_defaults(validator, properties, instance, schema): instance.setdefault(prop, subschema["default"]) yield from validate_properties( - validator, properties, instance, schema, + validator, + properties, + instance, + schema, ) return validators.extend( - validator_class, {"properties": set_defaults}, + validator_class, + {"properties": set_defaults}, ) diff --git a/tests/conftest.py b/tests/conftest.py index 395eda7..f27f687 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,7 @@ _FAIL_INSTEAD_OF_SKIP = True -def get_env_or_skip(env_name, override_fail: bool | None = None): +def get_env_or_skip(env_name: str, override_fail: bool | None = None) -> str: if override_fail is None: fail_or_skip = _FAIL_INSTEAD_OF_SKIP else: @@ -42,22 +42,22 @@ def test_files_dir() -> TestFilesDir: @pytest.fixture(scope="module") -def prime2_iso_provider(): +def prime2_iso_provider() -> IsoFileProvider: return IsoFileProvider(Path(get_env_or_skip("PRIME2_ISO"))) @pytest.fixture(scope="module") -def pal_prime2_iso_provider(): +def pal_prime2_iso_provider() -> IsoFileProvider: return IsoFileProvider(Path(get_env_or_skip("PRIME2_PAL_ISO", override_fail=False))) @pytest.fixture(scope="module") -def raw_prime2_editor(prime2_iso_provider): +def raw_prime2_editor(prime2_iso_provider: IsoFileProvider) -> PatcherEditor: return PatcherEditor(prime2_iso_provider, game=Game.ECHOES) -@pytest.fixture() -def prime2_editor(raw_prime2_editor): +@pytest.fixture +def prime2_editor(raw_prime2_editor: PatcherEditor): editor = raw_prime2_editor yield editor editor.memory_files = {} @@ -67,11 +67,16 @@ def prime2_editor(raw_prime2_editor): editor._modified_resources = {} -def pytest_addoption(parser): - parser.addoption('--skip-if-missing', action='store_false', dest="fail_if_missing", - default=True, help="Skip tests instead of missing, in case any asset is missing") +def pytest_addoption(parser) -> None: + parser.addoption( + "--skip-if-missing", + action="store_false", + dest="fail_if_missing", + default=True, + help="Skip tests instead of missing, in case any asset is missing", + ) -def pytest_configure(config: pytest.Config): - global _FAIL_INSTEAD_OF_SKIP +def pytest_configure(config: pytest.Config) -> None: + global _FAIL_INSTEAD_OF_SKIP # noqa: PLW0603 _FAIL_INSTEAD_OF_SKIP = config.option.fail_if_missing diff --git a/tests/dol_patching/conftest.py b/tests/dol_patching/conftest.py index 2b92bfa..5585162 100644 --- a/tests/dol_patching/conftest.py +++ b/tests/dol_patching/conftest.py @@ -2,7 +2,7 @@ from ppc_asm.dol_file import _NUM_SECTIONS, DolFile, DolHeader, Section -@pytest.fixture() +@pytest.fixture def dol_file(tmp_path): section_size = 0x1C2 sections = [Section(0, 0, 0)] * _NUM_SECTIONS diff --git a/tests/dol_patching/test_all_prime_dol_patches.py b/tests/dol_patching/test_all_prime_dol_patches.py index 639f60d..af3a101 100644 --- a/tests/dol_patching/test_all_prime_dol_patches.py +++ b/tests/dol_patching/test_all_prime_dol_patches.py @@ -7,34 +7,37 @@ from open_prime_rando.dol_patching import all_prime_dol_patches -@pytest.fixture(name="string_display") -def _string_display(): +@pytest.fixture +def string_display() -> all_prime_dol_patches.StringDisplayPatchAddresses: return all_prime_dol_patches.StringDisplayPatchAddresses( update_hint_state=0x80038020, message_receiver_string_ref=0x9000, - wstring_constructor=0x802ff3dc, - display_hud_memo=0x8006b3c8, + wstring_constructor=0x802FF3DC, + display_hud_memo=0x8006B3C8, max_message_size=200, ) -@pytest.fixture(name="powerup_address") -def _powerup_address(): +@pytest.fixture +def powerup_address() -> all_prime_dol_patches.PowerupFunctionsAddresses: return all_prime_dol_patches.PowerupFunctionsAddresses( - add_power_up=0x800758f0, + add_power_up=0x800758F0, incr_pickup=0x80075760, - decr_pickup=0x800756c4, + decr_pickup=0x800756C4, ) -@pytest.mark.parametrize('game', [Game.PRIME, Game.ECHOES]) -def test_apply_remote_execution_patch(game: Game, dol_file, - string_display: all_prime_dol_patches.StringDisplayPatchAddresses): +@pytest.mark.parametrize("game", [Game.PRIME, Game.ECHOES]) +def test_apply_remote_execution_patch( + game: Game, dol_file, string_display: all_prime_dol_patches.StringDisplayPatchAddresses +): dol_file.header = dataclasses.replace( dol_file.header, - sections=tuple([dataclasses.replace(dol_file.header.sections[0], - base_address=0x80038020), - *dol_file.header.sections[1:]])) + sections=( + dataclasses.replace(dol_file.header.sections[0], base_address=0x80038020), + *dol_file.header.sections[1:], + ), + ) # Run dol_file.set_editable(True) @@ -44,113 +47,131 @@ def test_apply_remote_execution_patch(game: Game, dol_file, # Assert results = dol_file.dol_path.read_bytes()[0x100:] for i in range(50): - print("b'" + "".join(f"\\x{x:02x}" for x in results[i * 20:(i + 1) * 20]) + "'") + print("b'" + "".join(f"\\x{x:02x}" for x in results[i * 20 : (i + 1) * 20]) + "'") if game == Game.PRIME: - assert results == (b'\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78' - b'\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38' - b'\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x7f\x08\x70\x2c\x03\x00\x00' - b'\x41\x82\xff\xe4\x80\x03\x00\x08\x2c\x00\x00\x00\x41\x81\xff\xd8\x3f\xc0\x80\x03' - b'\x63\xde\x80\x8c\x38\x80\x01\x60\x7c\x04\xf7\xac\x2c\x04\x00\x00\x38\x84\xff\xe0' - b'\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c\x38\xc0\x00\x00\x98\xdf\x00\x02' - b'\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') + assert results == ( + b"\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78" + b"\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38" + b"\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x7f\x08\x70\x2c\x03\x00\x00" + b"\x41\x82\xff\xe4\x80\x03\x00\x08\x2c\x00\x00\x00\x41\x81\xff\xd8\x3f\xc0\x80\x03" + b"\x63\xde\x80\x8c\x38\x80\x01\x60\x7c\x04\xf7\xac\x2c\x04\x00\x00\x38\x84\xff\xe0" + b"\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c\x38\xc0\x00\x00\x98\xdf\x00\x02" + b"\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) elif game == Game.ECHOES: - assert results == (b'\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78' - b'\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38' - b'\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x1f\x16\xfc\x2c\x00\x00\x00' - b'\x41\x81\xff\xe4\x3f\xc0\x80\x03\x63\xde\x80\x80\x38\x80\x01\x80\x7c\x04\xf7\xac' - b'\x2c\x04\x00\x00\x38\x84\xff\xe0\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c' - b'\x38\xc0\x00\x00\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6' - b'\x38\x21\x00\x34\x4e\x80\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') - - -@pytest.mark.parametrize('game', [Game.PRIME, Game.ECHOES]) -def test_create_remote_execution_body(game: Game, - string_display: all_prime_dol_patches.StringDisplayPatchAddresses, - powerup_address: all_prime_dol_patches.PowerupFunctionsAddresses): + assert results == ( + b"\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78" + b"\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38" + b"\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x1f\x16\xfc\x2c\x00\x00\x00" + b"\x41\x81\xff\xe4\x3f\xc0\x80\x03\x63\xde\x80\x80\x38\x80\x01\x80\x7c\x04\xf7\xac" + b"\x2c\x04\x00\x00\x38\x84\xff\xe0\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c" + b"\x38\xc0\x00\x00\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6" + b"\x38\x21\x00\x34\x4e\x80\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + + +@pytest.mark.parametrize("game", [Game.PRIME, Game.ECHOES]) +def test_create_remote_execution_body( + game: Game, + string_display: all_prime_dol_patches.StringDisplayPatchAddresses, + powerup_address: all_prime_dol_patches.PowerupFunctionsAddresses, +): # Run - patch_address, patch_bytes = all_prime_dol_patches.create_remote_execution_body(game, string_display, [ - *all_prime_dol_patches.call_display_hud_patch(string_display), - *all_prime_dol_patches.adjust_item_amount_and_capacity_patch(powerup_address, game, 3, 12), - ]) + patch_address, patch_bytes = all_prime_dol_patches.create_remote_execution_body( + game, + string_display, + [ + *all_prime_dol_patches.call_display_hud_patch(string_display), + *all_prime_dol_patches.adjust_item_amount_and_capacity_patch(powerup_address, game, 3, 12), + ], + ) # Assert if game == Game.PRIME: assert patch_address == string_display.update_hint_state + 0x70 - assert patch_bytes == (b'\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10' - b'\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18' - b'\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x19\x38\x81\x00\x10' - b'\x48\x03\x32\xfd\x80\x7f\x08\xb8\x80\x63\x00\x00\x38\x80\x00\x03\x38\xa0\x00\x0c' - b'\x48\x03\xd8\x11\x80\x7f\x08\xb8\x80\x63\x00\x00\x38\x80\x00\x03\x38\xa0\x00\x0c' - b'\x48\x03\xd6\x6d\x38\xc0\x00\x00\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38' - b'\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20') + assert patch_bytes == ( + b"\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10" + b"\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18" + b"\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x19\x38\x81\x00\x10" + b"\x48\x03\x32\xfd\x80\x7f\x08\xb8\x80\x63\x00\x00\x38\x80\x00\x03\x38\xa0\x00\x0c" + b"\x48\x03\xd8\x11\x80\x7f\x08\xb8\x80\x63\x00\x00\x38\x80\x00\x03\x38\xa0\x00\x0c" + b"\x48\x03\xd6\x6d\x38\xc0\x00\x00\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38" + b"\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20" + ) elif game == Game.ECHOES: assert patch_address == string_display.update_hint_state + 0x64 - assert patch_bytes == (b'\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10' - b'\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18' - b'\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x25\x38\x81\x00\x10' - b'\x48\x03\x33\x09\x80\x7f\x15\x0c\x38\x80\x00\x03\x38\xa0\x00\x0c\x48\x03\xd8\x21' - b'\x80\x7f\x15\x0c\x38\x80\x00\x03\x38\xa0\x00\x0c\x48\x03\xd6\x81\x38\xc0\x00\x00' - b'\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34' - b'\x4e\x80\x00\x20') + assert patch_bytes == ( + b"\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10" + b"\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18" + b"\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x25\x38\x81\x00\x10" + b"\x48\x03\x33\x09\x80\x7f\x15\x0c\x38\x80\x00\x03\x38\xa0\x00\x0c\x48\x03\xd8\x21" + b"\x80\x7f\x15\x0c\x38\x80\x00\x03\x38\xa0\x00\x0c\x48\x03\xd6\x81\x38\xc0\x00\x00" + b"\x98\xdf\x00\x02\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34" + b"\x4e\x80\x00\x20" + ) -@pytest.mark.parametrize('game', [Game.PRIME, Game.ECHOES]) +@pytest.mark.parametrize("game", [Game.PRIME, Game.ECHOES]) def test_remote_execution_patch_start(game: Game): # Run patch = all_prime_dol_patches.remote_execution_patch_start(game) data = bytes(assembler.assemble_instructions(0x80008020, patch)) for i in range(50): - print("b'" + "".join(f"\\x{x:02x}" for x in data[i * 20:(i + 1) * 20]) + "'") + print("b'" + "".join(f"\\x{x:02x}" for x in data[i * 20 : (i + 1) * 20]) + "'") # Assert if game == Game.PRIME: - assert data == (b'\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78' - b'\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38' - b'\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x7f\x08\x70\x2c\x03\x00\x00' - b'\x41\x82\xff\xe4\x80\x03\x00\x08\x2c\x00\x00\x00\x41\x81\xff\xd8\x3f\xc0\x80\x00' - b'\x63\xde\x80\x8c\x38\x80\x01\x60\x7c\x04\xf7\xac\x2c\x04\x00\x00\x38\x84\xff\xe0' - b'\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c') + assert data == ( + b"\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78" + b"\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38" + b"\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x7f\x08\x70\x2c\x03\x00\x00" + b"\x41\x82\xff\xe4\x80\x03\x00\x08\x2c\x00\x00\x00\x41\x81\xff\xd8\x3f\xc0\x80\x00" + b"\x63\xde\x80\x8c\x38\x80\x01\x60\x7c\x04\xf7\xac\x2c\x04\x00\x00\x38\x84\xff\xe0" + b"\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c" + ) elif game == Game.ECHOES: - assert data == (b'\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78' - b'\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38' - b'\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x1f\x16\xfc\x2c\x00\x00\x00' - b'\x41\x81\xff\xe4\x3f\xc0\x80\x00\x63\xde\x80\x80\x38\x80\x01\x80\x7c\x04\xf7\xac' - b'\x2c\x04\x00\x00\x38\x84\xff\xe0\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c') + assert data == ( + b"\x94\x21\xff\xcc\x7c\x08\x02\xa6\x90\x01\x00\x38\xbf\xc1\x00\x2c\x7c\x7f\x1b\x78" + b"\x88\x9f\x00\x02\x2c\x04\x00\x00\x40\x82\x00\x18\xbb\xc1\x00\x2c\x80\x01\x00\x38" + b"\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20\x80\x1f\x16\xfc\x2c\x00\x00\x00" + b"\x41\x81\xff\xe4\x3f\xc0\x80\x00\x63\xde\x80\x80\x38\x80\x01\x80\x7c\x04\xf7\xac" + b"\x2c\x04\x00\x00\x38\x84\xff\xe0\x40\x82\xff\xf4\x7c\x00\x04\xac\x4c\x00\x01\x2c" + ) def test_remote_execution_clear_pending_op(): @@ -159,7 +180,7 @@ def test_remote_execution_clear_pending_op(): data = bytes(assembler.assemble_instructions(1000, patch)) # Assert - assert data == b'\x38\xc0\x00\x00\x98\xdf\x00\x02' + assert data == b"\x38\xc0\x00\x00\x98\xdf\x00\x02" def test_remote_execution_cleanup_and_return(): @@ -168,7 +189,7 @@ def test_remote_execution_cleanup_and_return(): data = bytes(assembler.assemble_instructions(1000, patch)) # Assert - assert data == b'\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20' + assert data == b"\xbb\xc1\x00\x2c\x80\x01\x00\x38\x7c\x08\x03\xa6\x38\x21\x00\x34\x4e\x80\x00\x20" def test_call_display_hud_patch(string_display: all_prime_dol_patches.StringDisplayPatchAddresses): @@ -177,21 +198,24 @@ def test_call_display_hud_patch(string_display: all_prime_dol_patches.StringDisp data = bytes(assembler.assemble_instructions(string_display.update_hint_state, patch)) # Assert - assert data == (b"\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10" - b"\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18" - b"\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x89\x38\x81\x00\x10" - b"\x48\x03\x33\x6d") + assert data == ( + b"\x3c\xa0\x41\x00\x38\xc0\x00\x00\x38\xe0\x00\x01\x39\x20\x00\x09\x90\xa1\x00\x10" + b"\x98\xe1\x00\x14\x98\xc1\x00\x15\x98\xc1\x00\x16\x98\xe1\x00\x17\x91\x21\x00\x18" + b"\x38\x61\x00\x1c\x3c\x80\x00\x00\x60\x84\x90\x00\x48\x2c\x73\x89\x38\x81\x00\x10" + b"\x48\x03\x33\x6d" + ) def test_give_item_patch(powerup_address: all_prime_dol_patches.PowerupFunctionsAddresses): # Run - patch = all_prime_dol_patches.adjust_item_amount_and_capacity_patch(powerup_address, - Game.ECHOES, 10, 5) + patch = all_prime_dol_patches.adjust_item_amount_and_capacity_patch(powerup_address, Game.ECHOES, 10, 5) data = bytes(assembler.assemble_instructions(0x80008020, patch)) # Assert - assert data == (b"\x80\x7f\x15\x0c\x38\x80\x00\x0a\x38\xa0\x00\x05\x48\x06\xd8\xc5\x80\x7f\x15\x0c" - b"\x38\x80\x00\x0a\x38\xa0\x00\x05\x48\x06\xd7\x25") + assert data == ( + b"\x80\x7f\x15\x0c\x38\x80\x00\x0a\x38\xa0\x00\x05\x48\x06\xd8\xc5\x80\x7f\x15\x0c" + b"\x38\x80\x00\x0a\x38\xa0\x00\x05\x48\x06\xd7\x25" + ) def test_apply_reverse_energy_tank_heal_patch_active(dol_file): @@ -209,30 +233,31 @@ def test_apply_reverse_energy_tank_heal_patch_active(dol_file): # Assert results = dol_file.dol_path.read_bytes()[0x100:] - assert results == (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\xc0\x02\x01\x00\xd0\x1e\x00\x14\x60\x00\x00\x00\x60\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - ) + assert results == ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\xc0\x02\x01\x00\xd0\x1e\x00\x14\x60\x00\x00\x00\x60\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) def test_apply_reverse_energy_tank_heal_patch_inactive(dol_file): @@ -250,27 +275,28 @@ def test_apply_reverse_energy_tank_heal_patch_inactive(dol_file): # Assert results = dol_file.dol_path.read_bytes()[0x100:] - assert results == (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x7f\xc3\xf3\x78\x38\x80\x00\x29\x38\xa0\x27\x0f\x4b\xff\xff\x65' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - ) + assert results == ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x7f\xc3\xf3\x78\x38\x80\x00\x29\x38\xa0\x27\x0f\x4b\xff\xff\x65" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) diff --git a/tests/dol_patching/test_dol_version.py b/tests/dol_patching/test_dol_version.py index 478ddd8..6e00497 100644 --- a/tests/dol_patching/test_dol_version.py +++ b/tests/dol_patching/test_dol_version.py @@ -9,17 +9,17 @@ dol_version.DolVersion( game=Game.ECHOES, description="Gamecube NTSC", - build_string_address=0x803ac3b0, + build_string_address=0x803AC3B0, build_string=b"!#$MetroidBuildInfo!#$Build v1.028 10/18/2004 10:44:32", - sda2_base=0x804223c0, - sda13_base=0x8041fd80, + sda2_base=0x804223C0, + sda13_base=0x8041FD80, ), dol_version.DolVersion( game=Game.ECHOES, description="Gamecube PAL", - build_string_address=0x803ad710, + build_string_address=0x803AD710, build_string=b"!#$MetroidBuildInfo!#$Build v1.035 10/27/2004 19:48:17", - sda2_base=0x804223c0, + sda2_base=0x804223C0, sda13_base=0x80421060, ), ] diff --git a/tests/dol_patching/test_echoes_dol_patcher.py b/tests/dol_patching/test_echoes_dol_patcher.py index 97fe217..e2c715e 100644 --- a/tests/dol_patching/test_echoes_dol_patcher.py +++ b/tests/dol_patching/test_echoes_dol_patcher.py @@ -12,27 +12,36 @@ def test_apply_patches(mocker: pytest_mock.MockerFixture): version_patches = dol_versions.ALL_VERSIONS[0] mock_find_version_for_dol = mocker.patch( "open_prime_rando.dol_patching.dol_version.find_version_for_dol", - return_value=version_patches, autospec=True, + return_value=version_patches, + autospec=True, ) dol_file = MagicMock() mock_apply_remote_exec = mocker.patch( - "open_prime_rando.dol_patching.all_prime_dol_patches.apply_remote_execution_patch", autospec=True) + "open_prime_rando.dol_patching.all_prime_dol_patches.apply_remote_execution_patch", autospec=True + ) mock_apply_etank_capacity: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.all_prime_dol_patches.apply_energy_tank_capacity_patch", autospec=True) + "open_prime_rando.dol_patching.all_prime_dol_patches.apply_energy_tank_capacity_patch", autospec=True + ) mock_apply_game_options: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_game_options_patch", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_game_options_patch", autospec=True + ) mock_apply_beam_cost_patch: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_beam_cost_patch", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_beam_cost_patch", autospec=True + ) mock_apply_starting_visor_patch: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_starting_visor_patch", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_starting_visor_patch", autospec=True + ) mock_apply_fixes: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_fixes", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_fixes", autospec=True + ) mock_apply_unvisited_room_names: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_unvisited_room_names", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_unvisited_room_names", autospec=True + ) mock_apply_teleporter_sounds: MagicMock = mocker.patch( - "open_prime_rando.dol_patching.echoes.dol_patches.apply_teleporter_sounds", autospec=True) + "open_prime_rando.dol_patching.echoes.dol_patches.apply_teleporter_sounds", autospec=True + ) # Run dol_patcher.apply_patches(dol_file, patches_data) @@ -41,22 +50,21 @@ def test_apply_patches(mocker: pytest_mock.MockerFixture): mock_find_version_for_dol.assert_called_once_with(dol_file, dol_versions.ALL_VERSIONS) mock_apply_remote_exec.assert_called_once_with(Game.ECHOES, version_patches.string_display, dol_file) mock_apply_game_options.assert_called_once_with( - version_patches.game_options_constructor_address, - patches_data.user_preferences, dol_file + version_patches.game_options_constructor_address, patches_data.user_preferences, dol_file ) mock_apply_etank_capacity.assert_called_once_with( - version_patches.health_capacity, - patches_data.energy_per_tank, dol_file + version_patches.health_capacity, patches_data.energy_per_tank, dol_file ) mock_apply_beam_cost_patch.assert_called_once_with( - version_patches.beam_cost_addresses, - patches_data.beam_configurations, dol_file + version_patches.beam_cost_addresses, patches_data.beam_configurations, dol_file ) mock_apply_starting_visor_patch.assert_called_once_with( version_patches.starting_beam_visor, - patches_data.default_items, dol_file, + patches_data.default_items, + dol_file, ) mock_apply_fixes.assert_called_once_with(version_patches, dol_file) - mock_apply_unvisited_room_names.assert_called_once_with(version_patches, dol_file, - patches_data.unvisited_room_names) + mock_apply_unvisited_room_names.assert_called_once_with( + version_patches, dol_file, patches_data.unvisited_room_names + ) mock_apply_teleporter_sounds.assert_called_once_with(version_patches, dol_file, patches_data.teleporter_sounds) diff --git a/tests/dol_patching/test_echoes_dol_patches.py b/tests/dol_patching/test_echoes_dol_patches.py index f3731e9..deb1e9c 100644 --- a/tests/dol_patching/test_echoes_dol_patches.py +++ b/tests/dol_patching/test_echoes_dol_patches.py @@ -7,46 +7,62 @@ from open_prime_rando.dol_patching.echoes.user_preferences import OprEchoesUserPreferences DOLS = [ - (DolHeader(sections=(Section(offset=256, base_address=2147496192, size=1344), - Section(offset=1600, base_address=2147498048, size=3808352), - Section(offset=3969024, base_address=2147491840, size=352), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=3809952, base_address=2147497536, size=288), - Section(offset=3810240, base_address=2147497824, size=224), - Section(offset=3810464, base_address=2151306400, size=512), - Section(offset=3810976, base_address=2151306912, size=32), - Section(offset=3811008, base_address=2151306944, size=46400), - Section(offset=3857408, base_address=2151353344, size=85536), - Section(offset=3942944, base_address=2151775616, size=4384), - Section(offset=3947328, base_address=2151785408, size=21696), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0)), - bss_address=2151438880, bss_size=368356, entry_point=2147496588), - dol_versions.ALL_VERSIONS[0]), - (DolHeader(sections=(Section(offset=256, base_address=2147496192, size=1344), - Section(offset=1600, base_address=2147498048, size=3809312), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=3810912, base_address=2147497536, size=288), - Section(offset=3811200, base_address=2147497824, size=224), - Section(offset=3811424, base_address=2151307360, size=512), - Section(offset=3811936, base_address=2151307872, size=32), - Section(offset=3811968, base_address=2151307904, size=50432), - Section(offset=3862400, base_address=2151358336, size=85184), - Section(offset=3947584, base_address=2151780448, size=4384), - Section(offset=3951968, base_address=2151790272, size=21664), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0), - Section(offset=0, base_address=0, size=0)), - bss_address=2151443520, bss_size=368548, entry_point=2147496588), - dol_versions.ALL_VERSIONS[1]), + ( + DolHeader( + sections=( + Section(offset=256, base_address=2147496192, size=1344), + Section(offset=1600, base_address=2147498048, size=3808352), + Section(offset=3969024, base_address=2147491840, size=352), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=3809952, base_address=2147497536, size=288), + Section(offset=3810240, base_address=2147497824, size=224), + Section(offset=3810464, base_address=2151306400, size=512), + Section(offset=3810976, base_address=2151306912, size=32), + Section(offset=3811008, base_address=2151306944, size=46400), + Section(offset=3857408, base_address=2151353344, size=85536), + Section(offset=3942944, base_address=2151775616, size=4384), + Section(offset=3947328, base_address=2151785408, size=21696), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + ), + bss_address=2151438880, + bss_size=368356, + entry_point=2147496588, + ), + dol_versions.ALL_VERSIONS[0], + ), + ( + DolHeader( + sections=( + Section(offset=256, base_address=2147496192, size=1344), + Section(offset=1600, base_address=2147498048, size=3809312), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=3810912, base_address=2147497536, size=288), + Section(offset=3811200, base_address=2147497824, size=224), + Section(offset=3811424, base_address=2151307360, size=512), + Section(offset=3811936, base_address=2151307872, size=32), + Section(offset=3811968, base_address=2151307904, size=50432), + Section(offset=3862400, base_address=2151358336, size=85184), + Section(offset=3947584, base_address=2151780448, size=4384), + Section(offset=3951968, base_address=2151790272, size=21664), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + Section(offset=0, base_address=0, size=0), + ), + bss_address=2151443520, + bss_size=368548, + entry_point=2147496588, + ), + dol_versions.ALL_VERSIONS[1], + ), ] @@ -61,30 +77,31 @@ def test_apply_game_options_patch(dol_file): # Assert results = dol_file.dol_path.read_bytes()[0x100:] - assert results == (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x93\xe1\x00\x1c\x7c\x7f\x1b\x78' - b'\x38\x61\x00\x08\x38\x00\x00\x01\x90\x1f\x00\x00\x38\x00\x00\x04\x90\x1f\x00\x04' - b'\x38\x00\x00\x00\x90\x1f\x00\x08\x38\x00\x00\x00\x90\x1f\x00\x0c\x38\x00\x00\x00' - b'\x90\x1f\x00\x10\x38\x00\x00\x69\x90\x1f\x00\x14\x38\x00\x00\x4f\x90\x1f\x00\x18' - b'\x38\x00\x00\xff\x90\x1f\x00\x1c\x38\x00\x00\xff\x90\x1f\x00\x20\x38\x00\x00\xa0' - b'\x98\x1f\x00\x24\x38\x00\x00\x00\x90\x1f\x00\x2c\x90\x1f\x00\x30\x90\x1f\x00\x34' - b'\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00' - b'\x60\x00\x00\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - ) + assert results == ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x93\xe1\x00\x1c\x7c\x7f\x1b\x78" + b"\x38\x61\x00\x08\x38\x00\x00\x01\x90\x1f\x00\x00\x38\x00\x00\x04\x90\x1f\x00\x04" + b"\x38\x00\x00\x00\x90\x1f\x00\x08\x38\x00\x00\x00\x90\x1f\x00\x0c\x38\x00\x00\x00" + b"\x90\x1f\x00\x10\x38\x00\x00\x69\x90\x1f\x00\x14\x38\x00\x00\x4f\x90\x1f\x00\x18" + b"\x38\x00\x00\xff\x90\x1f\x00\x1c\x38\x00\x00\xff\x90\x1f\x00\x20\x38\x00\x00\xa0" + b"\x98\x1f\x00\x24\x38\x00\x00\x00\x90\x1f\x00\x2c\x90\x1f\x00\x30\x90\x1f\x00\x34" + b"\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00\x60\x00\x00\x00" + b"\x60\x00\x00\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) def test_apply_beam_cost_patch(dol_file): @@ -101,27 +118,39 @@ def test_apply_beam_cost_patch(dol_file): beam_configurations = [ BeamAmmoConfiguration( item_index=0, - ammo_a=-1, ammo_b=-1, - uncharged_cost=0, charged_cost=0, - combo_missile_cost=5, combo_ammo_cost=0 + ammo_a=-1, + ammo_b=-1, + uncharged_cost=0, + charged_cost=0, + combo_missile_cost=5, + combo_ammo_cost=0, ), BeamAmmoConfiguration( item_index=1, - ammo_a=45, ammo_b=-1, - uncharged_cost=1, charged_cost=5, - combo_missile_cost=5, combo_ammo_cost=30 + ammo_a=45, + ammo_b=-1, + uncharged_cost=1, + charged_cost=5, + combo_missile_cost=5, + combo_ammo_cost=30, ), BeamAmmoConfiguration( item_index=2, - ammo_a=46, ammo_b=-1, - uncharged_cost=1, charged_cost=5, - combo_missile_cost=5, combo_ammo_cost=30 + ammo_a=46, + ammo_b=-1, + uncharged_cost=1, + charged_cost=5, + combo_missile_cost=5, + combo_ammo_cost=30, ), BeamAmmoConfiguration( item_index=3, - ammo_a=46, ammo_b=45, - uncharged_cost=1, charged_cost=5, - combo_missile_cost=5, combo_ammo_cost=30 + ammo_a=46, + ammo_b=45, + uncharged_cost=1, + charged_cost=5, + combo_missile_cost=5, + combo_ammo_cost=30, ), ] @@ -138,42 +167,43 @@ def test_apply_beam_cost_patch(dol_file): # a = i * 20 # print("b'" + "".join([f"\\x{i:02x}" for i in results[a:a + 20]]) + "'") - assert results == (b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00' - b'\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x1e' - b'\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x05' - b'\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x81\x59\x07\x74\x55\x4a\x10\x3a\x7c\x03\x50\x2e' - b'\x90\x1d\x00\x00\x81\x59\x07\x74\x39\x4a\x00\x01\x7d\x49\x03\xa6\x42\x00\x00\x10' - b'\x38\x60\xff\xff\x39\x20\xff\xff\x48\x00\x00\x2c\x42\x00\x00\x10\x38\x60\x00\x2d' - b'\x39\x20\xff\xff\x48\x00\x00\x1c\x42\x00\x00\x10\x38\x60\x00\x2e\x39\x20\xff\xff' - b'\x48\x00\x00\x0c\x38\x60\x00\x2e\x39\x20\x00\x2d\x90\x7b\x00\x00\x91\x3c\x00\x00' - b'\x48\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x21\xff\xf0' - b'\x7c\x08\x02\xa6\x90\x01\x00\x14\x93\xe1\x00\x0c\x93\xc1\x00\x08\x7c\x7e\x1b\x78' - b'\x48\x00\x1e\xe9\x83\xe3\x13\x14\x80\xbe\x07\x74\x38\xa5\x00\x01\x7c\xa9\x03\xa6' - b'\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\xc4\x42\x00\x00\x0c\x38\x80\x00\x2d' - b'\x48\x00\x00\x18\x42\x00\x00\x0c\x38\x80\x00\x2e\x48\x00\x00\x0c\x38\x80\x00\x2e' - b'\x48\x00\x00\x04\x7f\xe3\xfb\x78\x38\xa0\x00\x01\x48\x00\x2e\xa1\x3c\x80\x00\x00' - b'\x60\x84\x20\x00\x80\xbe\x07\x74\x54\xa5\x10\x3a\x7c\x84\x28\x2e\x7c\x03\x20\x00' - b'\x40\x80\x00\x0c\x38\x60\x00\x01\x48\x00\x00\x74\x80\xbe\x07\x74\x38\xa5\x00\x01' - b'\x7c\xa9\x03\xa6\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\x5c\x42\x00\x00\x0c' - b'\x38\x60\x00\x00\x48\x00\x00\x50\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\x44' - b'\x38\x80\x00\x2d\x48\x00\x00\x04\x7f\xe3\xfb\x78\x38\xa0\x00\x01\x48\x00\x2e\x39' - b'\x3c\x80\x00\x00\x60\x84\x20\x00\x80\xbe\x07\x74\x54\xa5\x10\x3a\x7c\x84\x28\x2e' - b'\x7c\x03\x20\x00\x40\x80\x00\x0c\x38\x60\x00\x01\x48\x00\x00\x0c\x38\x60\x00\x00' - b'\x48\x00\x00\x04\x80\x01\x00\x14\x83\xe1\x00\x0c\x83\xc1\x00\x08\x7c\x08\x03\xa6' - b'\x38\x21\x00\x10\x4e\x80\x00\x20' - ) + assert results == ( + b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00" + b"\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x1e" + b"\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x05" + b"\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x81\x59\x07\x74\x55\x4a\x10\x3a\x7c\x03\x50\x2e" + b"\x90\x1d\x00\x00\x81\x59\x07\x74\x39\x4a\x00\x01\x7d\x49\x03\xa6\x42\x00\x00\x10" + b"\x38\x60\xff\xff\x39\x20\xff\xff\x48\x00\x00\x2c\x42\x00\x00\x10\x38\x60\x00\x2d" + b"\x39\x20\xff\xff\x48\x00\x00\x1c\x42\x00\x00\x10\x38\x60\x00\x2e\x39\x20\xff\xff" + b"\x48\x00\x00\x0c\x38\x60\x00\x2e\x39\x20\x00\x2d\x90\x7b\x00\x00\x91\x3c\x00\x00" + b"\x48\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x21\xff\xf0" + b"\x7c\x08\x02\xa6\x90\x01\x00\x14\x93\xe1\x00\x0c\x93\xc1\x00\x08\x7c\x7e\x1b\x78" + b"\x48\x00\x1e\xe9\x83\xe3\x13\x14\x80\xbe\x07\x74\x38\xa5\x00\x01\x7c\xa9\x03\xa6" + b"\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\xc4\x42\x00\x00\x0c\x38\x80\x00\x2d" + b"\x48\x00\x00\x18\x42\x00\x00\x0c\x38\x80\x00\x2e\x48\x00\x00\x0c\x38\x80\x00\x2e" + b"\x48\x00\x00\x04\x7f\xe3\xfb\x78\x38\xa0\x00\x01\x48\x00\x2e\xa1\x3c\x80\x00\x00" + b"\x60\x84\x20\x00\x80\xbe\x07\x74\x54\xa5\x10\x3a\x7c\x84\x28\x2e\x7c\x03\x20\x00" + b"\x40\x80\x00\x0c\x38\x60\x00\x01\x48\x00\x00\x74\x80\xbe\x07\x74\x38\xa5\x00\x01" + b"\x7c\xa9\x03\xa6\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\x5c\x42\x00\x00\x0c" + b"\x38\x60\x00\x00\x48\x00\x00\x50\x42\x00\x00\x0c\x38\x60\x00\x00\x48\x00\x00\x44" + b"\x38\x80\x00\x2d\x48\x00\x00\x04\x7f\xe3\xfb\x78\x38\xa0\x00\x01\x48\x00\x2e\x39" + b"\x3c\x80\x00\x00\x60\x84\x20\x00\x80\xbe\x07\x74\x54\xa5\x10\x3a\x7c\x84\x28\x2e" + b"\x7c\x03\x20\x00\x40\x80\x00\x0c\x38\x60\x00\x01\x48\x00\x00\x0c\x38\x60\x00\x00" + b"\x48\x00\x00\x04\x80\x01\x00\x14\x83\xe1\x00\x0c\x83\xc1\x00\x08\x7c\x08\x03\xa6" + b"\x38\x21\x00\x10\x4e\x80\x00\x20" + ) _bytes_for_item = { - "Combat Visor": b'\x00', - "Echo Visor": b'\x01', - "Dark Visor": b'\x03', - "Power Beam": b'\x00', - "Light Beam": b'\x02', + "Combat Visor": b"\x00", + "Echo Visor": b"\x01", + "Dark Visor": b"\x03", + "Power Beam": b"\x00", + "Light Beam": b"\x02", } @@ -205,31 +235,34 @@ def test_apply_starting_visor_patch(dol_file, starting_beam, starting_visor): # print("b'" + "".join([f"\\x{i:02x}" for i in results[a:a + 20]]) + "'") expected = ( - b'\x00\x00\x00\x00\x48\x00\x2f\xfd\x38\x00\x00{be}\x90\x1e\x00\x0c\x38\x00\x00{vi}' - b'\x90\x1e\x00\x30\x90\x1e\x00\x34\x38\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x00{vi}' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x00{vi}\x90\x1e\x00\x30\x90\x1e\x00\x34' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x38\x80\x00{vi}\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - ).replace(b"{vi}", _bytes_for_item[starting_visor] - ).replace(b"{be}", _bytes_for_item[starting_beam]) + ( + b"\x00\x00\x00\x00\x48\x00\x2f\xfd\x38\x00\x00{be}\x90\x1e\x00\x0c\x38\x00\x00{vi}" + b"\x90\x1e\x00\x30\x90\x1e\x00\x34\x38\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x00{vi}" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x38\x00\x00{vi}\x90\x1e\x00\x30\x90\x1e\x00\x34" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x38\x80\x00{vi}\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) + .replace(b"{vi}", _bytes_for_item[starting_visor]) + .replace(b"{be}", _bytes_for_item[starting_beam]) + ) assert results == expected @@ -244,40 +277,40 @@ def test_apply_safe_zone_heal_patch(dol_file): # Run dol_file.set_editable(True) with dol_file: - dol_patches.apply_safe_zone_heal_patch(addresses, sda2_base, 1.0, - dol_file) + dol_patches.apply_safe_zone_heal_patch(addresses, sda2_base, 1.0, dol_file) # Assert results = dol_file.dol_path.read_bytes()[0x100:] - expected = (b'\x3c\x88\x88\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x22\x0f\x50\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - ) + expected = ( + b"\x3c\x88\x88\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x22\x0f\x50\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ) assert results == expected -@pytest.mark.parametrize(["header", "version"], DOLS) +@pytest.mark.parametrize(("header", "version"), DOLS) def test_apply_fixes(dol_file, header, version): dol_file.header = header @@ -287,7 +320,7 @@ def test_apply_fixes(dol_file, header, version): dol_patches.apply_fixes(version, dol_file) -@pytest.mark.parametrize(["header", "version"], DOLS) +@pytest.mark.parametrize(("header", "version"), DOLS) @pytest.mark.parametrize("enabled", [False, True]) def test_apply_unvisited_room_names(dol_file, header, version, enabled): dol_file.header = header @@ -298,7 +331,7 @@ def test_apply_unvisited_room_names(dol_file, header, version, enabled): dol_patches.apply_unvisited_room_names(version, dol_file, enabled) -@pytest.mark.parametrize(["header", "version"], DOLS) +@pytest.mark.parametrize(("header", "version"), DOLS) @pytest.mark.parametrize("enabled", [False, True]) def test_apply_teleporter_sounds(dol_file, header, version, enabled): dol_file.header = header diff --git a/tests/echoes/test_echoes_dock_lock_rando.py b/tests/echoes/test_echoes_dock_lock_rando.py index 8c9f4fd..5b21e24 100644 --- a/tests/echoes/test_echoes_dock_lock_rando.py +++ b/tests/echoes/test_echoes_dock_lock_rando.py @@ -8,37 +8,37 @@ from open_prime_rando.patcher_editor import PatcherEditor _custom_asset_ids = { - 'custom_door_lock_darkburst.CMDL': 3171862285, - 'custom_door_lock_darkburst.TXTR': 3672172162, - 'custom_door_lock_darkburst_emissive.TXTR': 234173793, - 'custom_door_lock_echo_visor.CMDL': 2853277507, - 'custom_door_lock_echo_visor.TXTR': 3456085708, - 'custom_door_lock_echo_visor_emissive.TXTR': 1855515304, - 'custom_door_lock_greyscale_emissive.TXTR': 3597936245, - 'custom_door_lock_boost_ball.CMDL': 3448653349, - 'custom_door_lock_boost_ball.TXTR': 2858444714, - 'custom_door_lock_boost_ball_emissive.TXTR': 679049133, - 'custom_door_lock_cannon_ball.CMDL': 3063077393, - 'custom_door_lock_cannon_ball.TXTR': 3514629022, - 'custom_door_lock_cannon_ball_emissive.TXTR': 3792718965, - 'custom_door_lock_charge_beam.CMDL': 3005557644, - 'custom_door_lock_charge_beam.TXTR': 3570076163, - 'custom_door_lock_charge_beam_emissive.TXTR': 1595153749, - 'custom_door_lock_dark_visor.CMDL': 2145020805, - 'custom_door_lock_dark_visor.TXTR': 406080010, - 'custom_door_lock_dark_visor_emissive.TXTR': 775355676, - 'custom_door_lock_morph_ball_bombs.CMDL': 3083075990, - 'custom_door_lock_morph_ball_bombs.TXTR': 3492421657, - 'custom_door_lock_morph_ball_bombs_emissive.TXTR': 805979281, - 'custom_door_lock_screw_attack.CMDL': 1760304179, - 'custom_door_lock_screw_attack.TXTR': 251805116, - 'custom_door_lock_screw_attack_emissive.TXTR': 1723694793, - 'custom_door_lock_sonic_boom.CMDL': 1667093720, - 'custom_door_lock_sonic_boom.TXTR': 78902615, - 'custom_door_lock_sonic_boom_emissive.TXTR': 2606270890, - 'custom_door_lock_sunburst.CMDL': 987291923, - 'custom_door_lock_sunburst.TXTR': 1563869340, - 'custom_door_lock_sunburst_emissive.TXTR': 2638796108, + "custom_door_lock_darkburst.CMDL": 3171862285, + "custom_door_lock_darkburst.TXTR": 3672172162, + "custom_door_lock_darkburst_emissive.TXTR": 234173793, + "custom_door_lock_echo_visor.CMDL": 2853277507, + "custom_door_lock_echo_visor.TXTR": 3456085708, + "custom_door_lock_echo_visor_emissive.TXTR": 1855515304, + "custom_door_lock_greyscale_emissive.TXTR": 3597936245, + "custom_door_lock_boost_ball.CMDL": 3448653349, + "custom_door_lock_boost_ball.TXTR": 2858444714, + "custom_door_lock_boost_ball_emissive.TXTR": 679049133, + "custom_door_lock_cannon_ball.CMDL": 3063077393, + "custom_door_lock_cannon_ball.TXTR": 3514629022, + "custom_door_lock_cannon_ball_emissive.TXTR": 3792718965, + "custom_door_lock_charge_beam.CMDL": 3005557644, + "custom_door_lock_charge_beam.TXTR": 3570076163, + "custom_door_lock_charge_beam_emissive.TXTR": 1595153749, + "custom_door_lock_dark_visor.CMDL": 2145020805, + "custom_door_lock_dark_visor.TXTR": 406080010, + "custom_door_lock_dark_visor_emissive.TXTR": 775355676, + "custom_door_lock_morph_ball_bombs.CMDL": 3083075990, + "custom_door_lock_morph_ball_bombs.TXTR": 3492421657, + "custom_door_lock_morph_ball_bombs_emissive.TXTR": 805979281, + "custom_door_lock_screw_attack.CMDL": 1760304179, + "custom_door_lock_screw_attack.TXTR": 251805116, + "custom_door_lock_screw_attack_emissive.TXTR": 1723694793, + "custom_door_lock_sonic_boom.CMDL": 1667093720, + "custom_door_lock_sonic_boom.TXTR": 78902615, + "custom_door_lock_sonic_boom_emissive.TXTR": 2606270890, + "custom_door_lock_sunburst.CMDL": 987291923, + "custom_door_lock_sunburst.TXTR": 1563869340, + "custom_door_lock_sunburst_emissive.TXTR": 2638796108, } @@ -53,7 +53,6 @@ def test_add_custom_models(prime2_editor: PatcherEditor): "Dark": ("Torvus Bog", "Torvus Temple", "EastTop"), "Light": ("Torvus Bog", "Underground Tunnel", "North"), "Annihilator": ("Torvus Bog", "Gathering Hall", "East_0P"), - "Missile": ("Temple Grounds", "GFMC Compound", "WestTop"), "SuperMissile": ("Torvus Bog", "Torvus Temple", "WestGenerator"), "SeekerMissile": ("Temple Grounds", "Path of Honor", "North"), @@ -74,5 +73,6 @@ def test_apply_door_rando(prime2_editor, new_door_type, old_door_type, low_memor world_name, area_name, dock_name = vanilla_doors[old_door_type] with expectation: - dock_lock_rando.apply_door_rando(prime2_editor, world_name, area_name, dock_name, - new_door_type, old_door_type, low_memory) + dock_lock_rando.apply_door_rando( + prime2_editor, world_name, area_name, dock_name, new_door_type, old_door_type, low_memory + ) diff --git a/tests/echoes/test_full_patch.py b/tests/echoes/test_full_patch.py index b25f340..21eeccf 100644 --- a/tests/echoes/test_full_patch.py +++ b/tests/echoes/test_full_patch.py @@ -27,52 +27,50 @@ def test_ntsc_paks(prime2_iso_provider, tmp_path, test_files_dir, is_legacy: boo expected_hashes = { False: { - 'AudioGrp.pak': b'U\xc6\xfe\\\xac\xcd\xdc\x02\x0c\xed\xdd\xb3\xfarbP' - b'\x90\xac\x8d\x1b=\xd4\x9fl\x82\xc8\x12\xd2\x85X{+', - 'LogBook.pak': b'\x05\xdf2\xb5\xf4\xe5\xce\xa3"\tk_\x91\x05\xe9\xa9' - b'\xab\x8c.\x9c\xe0\xee\x89\xb7\x04\xa1\x7fI\x0bU,\xb4', - 'Metroid1.pak': b'\x11%\x80\xe4t\x9e?\x1d\xaa\xf2\xc9\xc3\x1d&\xa6v\xfc?E\xcf' - b'\xc4\x06\x13\x83"A#\xfb\xa1\x84\xcf\xca', - 'Metroid2.pak': b'\x96lH\x85\xae\xddX\xceo\xd1\x0cfnk\xbex\xaa\x94Y\x08' - b'\x05\\-\xf9?\xa4\x9a]\xa8\x0ez\x93', - 'Metroid3.pak': b'\xe8\xee\x80\xf6\\\xd5\x1c4\xf5\x1b\xe1*\xfbY\x9a\xe0' - b'\x8c\xb9\xd1\xe0(\x96\xb4R-\xe0\xf1\x86y\x88T\xa9', - 'Metroid4.pak': b'\xce\xc5\x838\x19q\xd0\xf7\xf4\x86hQ\xb3\xf5\xf5p\xf9\xd3}8' - b'\xfemx\xb5x\tQ\xf8\xc6\xdc.\xb6', - 'Metroid5.pak': b'\xa9n\xb5\xf5\x04U\x13\xc0 \xcf\xcfe\x11\xbd\x04\xad' - b'c\xd2\xc6\x8b\xb4\xc5\xc0\xd0P \xb1f\xf1\xdc\x90T', - 'MiscData.pak': b'\x0bM\xedR\xb7+\xaf+#\x06\x87\x88\xe5\xec\xbbxg@i\xbc' - b'\xa3n\xb1\xabi\xfajb\x00\xf3\xd1\xaa', - 'SamusGun.pak': b'\xdd\xa99\x0b\x14b\x97&\xee\xdf\nE\xd0GR`c\x85\xf7m;\xdbX^' - b'\xe4\x87;\\?\xc9\x08\x1f', - 'SamusGunLow.pak': b'o8\xb8\xab^\xc3y\xc7\xd8v\xf1?\xe7a\x9enIE\xb7\xa5' - b'q\x9f\xa2\xf6\xc2\xc6m&\x80G\x8f\x83', - 'TestAnim.pak': b'\xc0n\xfa2\x87\x96o\xd9\xf8\nL\xc8\xd5vVJi\xe2\xa0\xdb' - b'\xdbq\t\x7f\xaf\x96\x8d\x02\x1f\xe7\x07\x12' + "AudioGrp.pak": b"U\xc6\xfe\\\xac\xcd\xdc\x02\x0c\xed\xdd\xb3\xfarbP" + b"\x90\xac\x8d\x1b=\xd4\x9fl\x82\xc8\x12\xd2\x85X{+", + "LogBook.pak": b'\x05\xdf2\xb5\xf4\xe5\xce\xa3"\tk_\x91\x05\xe9\xa9' + b"\xab\x8c.\x9c\xe0\xee\x89\xb7\x04\xa1\x7fI\x0bU,\xb4", + "Metroid1.pak": b"\x11%\x80\xe4t\x9e?\x1d\xaa\xf2\xc9\xc3\x1d&\xa6v\xfc?E\xcf" + b'\xc4\x06\x13\x83"A#\xfb\xa1\x84\xcf\xca', + "Metroid2.pak": b"\x96lH\x85\xae\xddX\xceo\xd1\x0cfnk\xbex\xaa\x94Y\x08" + b"\x05\\-\xf9?\xa4\x9a]\xa8\x0ez\x93", + "Metroid3.pak": b"\xe8\xee\x80\xf6\\\xd5\x1c4\xf5\x1b\xe1*\xfbY\x9a\xe0" + b"\x8c\xb9\xd1\xe0(\x96\xb4R-\xe0\xf1\x86y\x88T\xa9", + "Metroid4.pak": b"\xce\xc5\x838\x19q\xd0\xf7\xf4\x86hQ\xb3\xf5\xf5p\xf9\xd3}8" + b"\xfemx\xb5x\tQ\xf8\xc6\xdc.\xb6", + "Metroid5.pak": b"\xa9n\xb5\xf5\x04U\x13\xc0 \xcf\xcfe\x11\xbd\x04\xad" + b"c\xd2\xc6\x8b\xb4\xc5\xc0\xd0P \xb1f\xf1\xdc\x90T", + "MiscData.pak": b"\x0bM\xedR\xb7+\xaf+#\x06\x87\x88\xe5\xec\xbbxg@i\xbc" + b"\xa3n\xb1\xabi\xfajb\x00\xf3\xd1\xaa", + "SamusGun.pak": b"\xdd\xa99\x0b\x14b\x97&\xee\xdf\nE\xd0GR`c\x85\xf7m;\xdbX^" b"\xe4\x87;\\?\xc9\x08\x1f", + "SamusGunLow.pak": b"o8\xb8\xab^\xc3y\xc7\xd8v\xf1?\xe7a\x9enIE\xb7\xa5" + b"q\x9f\xa2\xf6\xc2\xc6m&\x80G\x8f\x83", + "TestAnim.pak": b"\xc0n\xfa2\x87\x96o\xd9\xf8\nL\xc8\xd5vVJi\xe2\xa0\xdb" + b"\xdbq\t\x7f\xaf\x96\x8d\x02\x1f\xe7\x07\x12", }, True: { - 'AudioGrp.pak': b'U\xc6\xfe\\\xac\xcd\xdc\x02\x0c\xed\xdd\xb3\xfarbP' - b'\x90\xac\x8d\x1b=\xd4\x9fl\x82\xc8\x12\xd2\x85X{+', - 'LogBook.pak': b'K=z\x02\x9c>\x8a\xfbN\xbe\xb6\xb5\xc8\xad\xba\xb8' - b'\x99\xae\x85_\xab55\xfe3\x15\x0b\xdc\xd1\xc7\x02^', - 'Metroid1.pak': b'7\x18\xe7+\xdbR\xbf6\x0e\xa2=\x8b\xc8\xcc\x98-' - b'\x8f\xdc\xe5\x8d\x17\xedz\xf9c\xe1\xd5\xfc\xf2\x18\xf2$', - 'Metroid2.pak': b'U\x9b\xa7;\xbf\xad\xac\xf7\x05\xe9hKC\xcd\xa5\x0f' - b'\xc9C\xc7\xf2\xe7A$\xcc\x1c&0A\xcb\xaa(\xf1', - 'Metroid3.pak': b'\xeaO\x17\x0e\xaa\xbd\xaa\xabJvO\x9d\x08\x15Y\xca' - b'\xe7\x97z\x0c\xcb\xf6],\xf9\xfa\xee*\x17\xc2\xf6(', - 'Metroid4.pak': b'9\xc2\x87\x91\x91%\x82\x0f>\x7f;Q\xacLP\xca-\xb6\xc1\x1b' - b'+\xc7\xecy{\xc0\x12\x19\xa7\x04\x7ft', - 'Metroid5.pak': b'\xaf\xfe\xec3\xf1\xfeh\xbb\x05\x00' - b'\xb5\x0fP\xd7\xa2\xf0\xb9\x81\xa7\xa1\x84\xbe', - 'MiscData.pak': b'\x0bM\xedR\xb7+\xaf+#\x06\x87\x88\xe5\xec\xbbxg@i\xbc' - b'\xa3n\xb1\xabi\xfajb\x00\xf3\xd1\xaa', - 'SamusGun.pak': b'\xdd\xa99\x0b\x14b\x97&\xee\xdf\nE\xd0GR`c\x85\xf7m;\xdbX^' - b'\xe4\x87;\\?\xc9\x08\x1f', - 'SamusGunLow.pak': b'o8\xb8\xab^\xc3y\xc7\xd8v\xf1?\xe7a\x9enIE\xb7\xa5' - b'q\x9f\xa2\xf6\xc2\xc6m&\x80G\x8f\x83', - 'TestAnim.pak': b'\xc0n\xfa2\x87\x96o\xd9\xf8\nL\xc8\xd5vVJi\xe2\xa0\xdb' - b'\xdbq\t\x7f\xaf\x96\x8d\x02\x1f\xe7\x07\x12' + "AudioGrp.pak": b"U\xc6\xfe\\\xac\xcd\xdc\x02\x0c\xed\xdd\xb3\xfarbP" + b"\x90\xac\x8d\x1b=\xd4\x9fl\x82\xc8\x12\xd2\x85X{+", + "LogBook.pak": b"K=z\x02\x9c>\x8a\xfbN\xbe\xb6\xb5\xc8\xad\xba\xb8" + b"\x99\xae\x85_\xab55\xfe3\x15\x0b\xdc\xd1\xc7\x02^", + "Metroid1.pak": b"7\x18\xe7+\xdbR\xbf6\x0e\xa2=\x8b\xc8\xcc\x98-" + b"\x8f\xdc\xe5\x8d\x17\xedz\xf9c\xe1\xd5\xfc\xf2\x18\xf2$", + "Metroid2.pak": b"U\x9b\xa7;\xbf\xad\xac\xf7\x05\xe9hKC\xcd\xa5\x0f" + b"\xc9C\xc7\xf2\xe7A$\xcc\x1c&0A\xcb\xaa(\xf1", + "Metroid3.pak": b"\xeaO\x17\x0e\xaa\xbd\xaa\xabJvO\x9d\x08\x15Y\xca" + b"\xe7\x97z\x0c\xcb\xf6],\xf9\xfa\xee*\x17\xc2\xf6(", + "Metroid4.pak": b"9\xc2\x87\x91\x91%\x82\x0f>\x7f;Q\xacLP\xca-\xb6\xc1\x1b" + b"+\xc7\xecy{\xc0\x12\x19\xa7\x04\x7ft", + "Metroid5.pak": b"\xaf\xfe\xec3\xf1\xfeh\xbb\x05\x00" + b"\xb5\x0fP\xd7\xa2\xf0\xb9\x81\xa7\xa1\x84\xbe", + "MiscData.pak": b"\x0bM\xedR\xb7+\xaf+#\x06\x87\x88\xe5\xec\xbbxg@i\xbc" + b"\xa3n\xb1\xabi\xfajb\x00\xf3\xd1\xaa", + "SamusGun.pak": b"\xdd\xa99\x0b\x14b\x97&\xee\xdf\nE\xd0GR`c\x85\xf7m;\xdbX^" b"\xe4\x87;\\?\xc9\x08\x1f", + "SamusGunLow.pak": b"o8\xb8\xab^\xc3y\xc7\xd8v\xf1?\xe7a\x9enIE\xb7\xa5" + b"q\x9f\xa2\xf6\xc2\xc6m&\x80G\x8f\x83", + "TestAnim.pak": b"\xc0n\xfa2\x87\x96o\xd9\xf8\nL\xc8\xd5vVJi\xe2\xa0\xdb" + b"\xdbq\t\x7f\xaf\x96\x8d\x02\x1f\xe7\x07\x12", }, } diff --git a/tests/test_echoes_custom_Assets.py b/tests/test_echoes_custom_Assets.py index c5391fe..278321c 100644 --- a/tests/test_echoes_custom_Assets.py +++ b/tests/test_echoes_custom_Assets.py @@ -11,9 +11,11 @@ def test_create_split_ammo(): custom_assets._create_split_ammo(manager) # Assert - manager.add_new_asset.assert_has_calls([ - call('dark_ammo_cmdl', asset, find_paks), - call('dark_ammo_ancs', asset, find_paks), - call('light_ammo_cmdl', asset, find_paks), - call('light_ammo_ancs', asset, find_paks) - ]) + manager.add_new_asset.assert_has_calls( + [ + call("dark_ammo_cmdl", asset, find_paks), + call("dark_ammo_ancs", asset, find_paks), + call("light_ammo_cmdl", asset, find_paks), + call("light_ammo_ancs", asset, find_paks), + ] + ) diff --git a/tools/asset_id_files.py b/tools/asset_id_files.py index 48adbdc..43501c2 100644 --- a/tools/asset_id_files.py +++ b/tools/asset_id_files.py @@ -1,5 +1,4 @@ import argparse -import os.path import shutil from pathlib import Path @@ -32,9 +31,11 @@ # Complexity # ruff: noqa: C901 + def filter_name(s: str) -> str: - result = s.replace("!", "").replace(" ", "_").replace("'", "").replace( - '"', "").replace("(", "").replace(")", "").upper() + result = ( + s.replace("!", "").replace(" ", "_").replace("'", "").replace('"', "").replace("(", "").replace(")", "").upper() + ) while result and not result[0].isalpha(): result = result[1:] return result @@ -47,9 +48,9 @@ def filter_and_lower_name(s: str) -> str: def dock_name_templates(dock_names: dict[str, dict[str, int]]) -> str: template = "\nDOCK_NAMES = {\n" for name in sorted(dock_names.keys()): - template += f" \"{name}\": {{\n" + template += f' "{name}": {{\n' for dock_name, dock_number in sorted(dock_names[name].items(), key=lambda it: it[1]): - template += f" \"{dock_name}\": {dock_number},\n" + template += f' "{dock_name}": {dock_number},\n' template += " },\n" template += "}\n" @@ -57,16 +58,13 @@ def dock_name_templates(dock_names: dict[str, dict[str, int]]) -> str: def generate_template(items: dict[str, int], suffix: str) -> str: - template = f"# Generated by {os.path.basename(__file__)}\n\n" - template += "\n".join( - f"{filter_name(key)}{suffix} = 0x{items[key]:08X}" - for key in sorted(items) - ) + template = f"# Generated by {Path(__file__).name}\n\n" + template += "\n".join(f"{filter_name(key)}{suffix} = 0x{items[key]:08X}" for key in sorted(items)) template += "\n" template += f"\nNAME_TO_ID{suffix}" + " = {\n" for name in sorted(items): - template += f" \"{name}\": 0x{items[name]:08X},\n" + template += f' "{name}": 0x{items[name]:08X},\n' template += "}\n" return template @@ -147,7 +145,7 @@ def create_asset_id_files(editor: PatcherEditor, output_path: Path): global_file_body += "\n_DEDICATED_FILES = {\n" for name in sorted(world_names.keys()): - global_file_body += f" \"{name}\": \".{filter_and_lower_name(name)}\",\n" + global_file_body += f' "{name}": ".{filter_and_lower_name(name)}",\n' global_file_body += """} @@ -164,22 +162,25 @@ def load_dedicated_file(world_name: str): all_imports = list(map(filter_and_lower_name, world_names.keys())) all_imports.append("world") - output_path.joinpath("__init__.py").write_text("\n".join([ - "from . import (\n " + ",\n ".join(all_imports), - ")", - "", - "__all__ = [", - *[f' "{it}",' for it in all_imports], - "]", - "", - ])) + output_path.joinpath("__init__.py").write_text( + "\n".join( + [ + "from . import (\n " + ",\n ".join(all_imports), + ")", + "", + "__all__ = [", + *[f' "{it}",' for it in all_imports], + "]", + "", + ] + ) + ) def main(): parser = argparse.ArgumentParser() parser.add_argument("--game", required=True, choices=["echoes"]) - parser.add_argument("--iso", required=True, type=Path, - help="Path to where the ISO.") + parser.add_argument("--iso", required=True, type=Path, help="Path to where the ISO.") args = parser.parse_args() create_asset_id_files( @@ -188,5 +189,5 @@ def main(): ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tools/draw_objects.py b/tools/draw_objects.py index ae8f4e7..62a43ee 100644 --- a/tools/draw_objects.py +++ b/tools/draw_objects.py @@ -14,8 +14,9 @@ from open_prime_rando.patcher_editor import PatcherEditor -def _draw_component(dot: graphviz.Digraph, instances: list[ScriptInstance], - namer: typing.Callable[[ScriptInstance], str]): +def _draw_component( + dot: graphviz.Digraph, instances: list[ScriptInstance], namer: typing.Callable[[ScriptInstance], str] +): for instance in instances: dot.node(name=str(instance.id), label=namer(instance)) @@ -25,6 +26,7 @@ def _draw_component(dot: graphviz.Digraph, instances: list[ScriptInstance], dot.render(cleanup=True, format="png") + def draw_objects(editor: PatcherEditor, world_name: str, area_name: str): mlvl = editor.get_mlvl(world.NAME_TO_ID_MLVL[world_name]) area = mlvl.get_area(world.load_dedicated_file(world_name).NAME_TO_ID_MREA[area_name]) @@ -68,17 +70,17 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument("--game", required=False, default="echoes", choices=["echoes"]) - parser.add_argument("--iso", required=iso is None, type=Path, default=iso, - help="Path to where the ISO.") + parser.add_argument("--iso", required=iso is None, type=Path, default=iso, help="Path to where the ISO.") parser.add_argument("world", help="Name of the world to check, as used in the schema") parser.add_argument("area", help="Name of the area to check, as used in the schema") args = parser.parse_args() draw_objects( PatcherEditor(IsoFileProvider(args.iso), Game.ECHOES), - args.world, args.area, + args.world, + args.area, ) -if __name__ == '__main__': +if __name__ == "__main__": main()