diff --git a/base/code/main.asm b/base/code/main.asm index 6ba6330f..e980a850 100644 --- a/base/code/main.asm +++ b/base/code/main.asm @@ -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 @@ -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 diff --git a/base/code/progressive_sword_check.c b/base/code/progressive_sword_check.c index 153e35db..36b2f1b3 100644 --- a/base/code/progressive_sword_check.c +++ b/base/code/progressive_sword_check.c @@ -1,26 +1,31 @@ #include #include -#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; } } diff --git a/tests/desmume/test_data/test_progressive_sword.dsv b/tests/desmume/test_data/test_progressive_sword.dsv new file mode 100644 index 00000000..d5de19e0 Binary files /dev/null and b/tests/desmume/test_data/test_progressive_sword.dsv differ diff --git a/tests/desmume/test_progressive_sword.py b/tests/desmume/test_progressive_sword.py new file mode 100644 index 00000000..d3ad4ea0 --- /dev/null +++ b/tests/desmume/test_progressive_sword.py @@ -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 + )