Skip to content

Commit

Permalink
Merge branch 'main' into WidescreenHUDPatch
Browse files Browse the repository at this point in the history
  • Loading branch information
Nystrata authored Oct 12, 2024
2 parents 1ae075a + d7b30a4 commit 432fc18
Show file tree
Hide file tree
Showing 54 changed files with 1,634 additions and 1,412 deletions.
7 changes: 7 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
changelog:
exclude:
labels:
- ignore-for-release
authors:
- pre-commit-ci
- dependabot
20 changes: 20 additions & 0 deletions .github/workflows/dependency.yml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 5 additions & 2 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ name: Python Package

on:
pull_request:
merge_group:
push:
branches:
- '*'
- main
tags:
- '*'

Expand Down Expand Up @@ -49,6 +50,8 @@ jobs:
python:
- {version: '3.10', wheel: 'cp310-cp310'}
- {version: '3.11', wheel: 'cp311-cp311'}
- {version: '3.12', wheel: 'cp312-cp312'}
- {version: '3.13', wheel: 'cp313-cp313'}

steps:
- name: Checkout
Expand Down Expand Up @@ -102,7 +105,7 @@ jobs:

- name: Run Tests
run:
venv/bin/python -m pytest --cov src --cov-report=xml --durations=100
venv/bin/python -m pytest --cov src --cov-report=xml --durations=100 -n 4

- name: codecov
uses: codecov/codecov-action@v4
Expand Down
5 changes: 3 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.5.5
rev: v0.6.9
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
args: [ --fix, --exit-non-zero-on-fix ]
- id: ruff-format
30 changes: 26 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.10"
dynamic = ["version"]

dependencies = [
"retro-data-structures>=0.23.0",
"retro-data-structures>=0.28.0",
"jsonschema>=4.0.0",
"ppc-asm",
"py_randomprime", # for Prime 1 symbols
Expand All @@ -35,6 +37,7 @@ test = [
"pytest",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"pre-commit",
]

Expand Down Expand Up @@ -65,8 +68,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
]
6 changes: 4 additions & 2 deletions src/open_prime_rando/__pyinstaller/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from pathlib import Path

# Functions
# =========
Expand All @@ -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)]
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
62 changes: 31 additions & 31 deletions src/open_prime_rando/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.")


Expand Down
59 changes: 32 additions & 27 deletions src/open_prime_rando/dol_patching/all_prime_dol_patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")
Expand All @@ -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,
Expand All @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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:
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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.
"""
Expand All @@ -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.
"""
Expand All @@ -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

Expand Down
Loading

0 comments on commit 432fc18

Please sign in to comment.