Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions base/code/main.asm
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@

pop pc

.arm
@progressive_sword_check:
; If this item is a progressive sword (id 0x3),
; branch to function that handles that.
; Otherwise, continue as normal.
cmp r0, 0x3
bne @@end

push r1, r2, r3
bl progressive_sword_check
pop r1, r2, r3

@@end:
pop r3-r5, r15

.pool
.endarea
.close
Expand All @@ -113,14 +128,11 @@
b @init_flags
.endarea

.thumb
.org 0x20ade1c
.arm
.org 0x2085848
; Overwrite code that gives the player the oshus sword to be progressive
.area 0xC, 0x00
; Save scratch registers, since gcc doesn't do it
push r0, r1, r2, r3
bl progressive_sword_check
pop r0, r1, r2, r3
.area 0x4, 0x00
b @progressive_sword_check
.endarea

.thumb
Expand Down
33 changes: 19 additions & 14 deletions base/code/progressive_sword_check.c
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
#include <stdbool.h>
#include <stdint.h>

#define OSHUS_SWORD_FLAG_OFFSET (0x128)
#define OSHUS_SWORD_FLAG_OFFSET (0x21BA604)
#define OSHUS_SWORD_FLAG_BIT (0x1)
#define PHANTOM_SWORD_FLAG_OFFSET (0x12C)
#define PHANTOM_SWORD_BLADE_FLAG_OFFSET (0x21B5550)
#define PHANTOM_SWORD_BLADE_FLAG_BIT (0x20)
#define PHANTOM_SWORD_FLAG_OFFSET (0x21BA608)
#define PHANTOM_SWORD_FLAG_BIT (0x20)

__attribute__((target("thumb"))) void progressive_sword_check(const uint32_t base_address) {
// Address of oshus sword flag
uint8_t *oshus_sword = (uint8_t *)(base_address + OSHUS_SWORD_FLAG_OFFSET);
uint8_t progressive_sword_check(uint8_t item_id) {
// Addresses of oshus sword and phantom sword blade flags
uint8_t *oshus_sword = (uint8_t *)(OSHUS_SWORD_FLAG_OFFSET);
uint8_t *phantom_sword_blade = (uint8_t *)(PHANTOM_SWORD_BLADE_FLAG_OFFSET);

// Address of phantom sword flag
uint8_t *phantom_sword = (uint8_t *)(base_address + PHANTOM_SWORD_FLAG_OFFSET);

// Check if player has the oshus sword
// Check if player has the oshus sword or phantom sword blade
bool has_oshus_sword = (*oshus_sword & OSHUS_SWORD_FLAG_BIT) == OSHUS_SWORD_FLAG_BIT;
bool has_phantom_sword_blade =
(*phantom_sword_blade & PHANTOM_SWORD_BLADE_FLAG_BIT) == PHANTOM_SWORD_BLADE_FLAG_BIT;

// If the player has the oshus sword already, give them the phantom sword.
// Otherwise, give them the oshus sword
if (has_oshus_sword) {
*phantom_sword |= PHANTOM_SWORD_FLAG_BIT;
// If the player has the oshus sword already, give them the phantom sword blade.
// If they have the phantom sword blade already, give them the phantom sword.
// Otherwise, give them the oshus sword.
if (has_oshus_sword && has_phantom_sword_blade) {
return 0x45;
} else if (has_oshus_sword) {
return 0x44;
} else {
*oshus_sword |= OSHUS_SWORD_FLAG_BIT;
return 0x3;
}
}
Binary file not shown.
94 changes: 94 additions & 0 deletions tests/desmume/test_progressive_sword.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from collections import namedtuple
from pathlib import Path

from desmume.emulator import SCREEN_WIDTH
from ndspy.rom import NintendoDSRom
import pytest

from ph_rando.common import ShufflerAuxData
from ph_rando.patcher.main import _patch_zmb_map_objects
from ph_rando.shuffler.aux_models import Chest

from .conftest import DeSmuMEWrapper
from .desmume_utils import start_first_file

SwordProgressionItem = namedtuple('SwordProgressionItem', 'memory_address bit')

SWORD_PROGRESSION_ITEMS = [
SwordProgressionItem(0x21BA604, 0x01), # Oshus sword
SwordProgressionItem(0x21B5550, 0x20), # Phantom sword (blade only)
SwordProgressionItem(0x21BA608, 0x20), # Phantom sword
]


@pytest.fixture
def progressive_sword_test_emu(
rom_path: Path,
desmume_emulator: DeSmuMEWrapper,
aux_data: ShufflerAuxData,
):
"""Generate and run a rom with a custom chest item set."""
rom = NintendoDSRom.fromFile(rom_path)

chests = [
chest
for area in aux_data.areas.values()
for room in area.rooms
for chest in room.chests
if type(chest) == Chest
and chest.zmb_file_path == 'Map/isle_main/map19.bin/zmb/isle_main_19.zmb'
]
for chest in chests:
chest.contents = 'ProgressiveSword'

_patch_zmb_map_objects(aux_data.areas.values(), rom)

rom.saveToFile(rom_path)

desmume_emulator.open(str(rom_path))

return desmume_emulator


@pytest.mark.parametrize(
'idx',
range(len(SWORD_PROGRESSION_ITEMS)),
ids=['oshus sword (1)', 'phantom blade (2)', 'phantom sword (3)'],
)
def test_progressive_sword(progressive_sword_test_emu: DeSmuMEWrapper, idx: int):
"""Ensure progressive sword patch works."""
start_first_file(progressive_sword_test_emu)

# Set flags for earlier sword if needed
for item in SWORD_PROGRESSION_ITEMS[:idx]:
progressive_sword_test_emu.memory.unsigned[item.memory_address] |= item.bit

# Make sure the current "next sword" is not in inventory
assert (
progressive_sword_test_emu.memory.unsigned[SWORD_PROGRESSION_ITEMS[idx].memory_address]
& SWORD_PROGRESSION_ITEMS[idx].bit
!= SWORD_PROGRESSION_ITEMS[idx].bit
)

# Walk up to chest
progressive_sword_test_emu.input.touch_set_pos(SCREEN_WIDTH // 2, 0)
progressive_sword_test_emu.wait(320)
progressive_sword_test_emu.input.touch_release()
progressive_sword_test_emu.wait(10)

# Open chest
progressive_sword_test_emu.touch_input((SCREEN_WIDTH // 2, 100), 2)

# Wait for "Got item" text and skip through it
progressive_sword_test_emu.wait(200)
progressive_sword_test_emu.touch_input((0, 0), 2)
progressive_sword_test_emu.wait(200)
progressive_sword_test_emu.touch_input((0, 0), 2)
progressive_sword_test_emu.wait(100)

# Make sure the "next sword" was obtained
assert (
progressive_sword_test_emu.memory.unsigned[SWORD_PROGRESSION_ITEMS[idx].memory_address]
& SWORD_PROGRESSION_ITEMS[idx].bit
== SWORD_PROGRESSION_ITEMS[idx].bit
)