From 764e7e4faed2bfc74b2134ba14d38964bceb82b3 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 21 Apr 2023 01:39:41 -0400 Subject: [PATCH 1/3] Update progressive sword patch - No longer rely on dynamic offset; instead, specify the full offsets for the sword flags from the dev sheet - Add Phantom Sword Blade as a part of the sword progression --- base/code/progressive_sword_check.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/base/code/progressive_sword_check.c b/base/code/progressive_sword_check.c index 153e35db..5482f08e 100644 --- a/base/code/progressive_sword_check.c +++ b/base/code/progressive_sword_check.c @@ -1,25 +1,36 @@ #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) { +__attribute__((target("thumb"))) void progressive_sword_check() { // Address of oshus sword flag - uint8_t *oshus_sword = (uint8_t *)(base_address + OSHUS_SWORD_FLAG_OFFSET); + uint8_t *oshus_sword = (uint8_t *)(OSHUS_SWORD_FLAG_OFFSET); + + // Address of phantom sword blade flag + 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); + uint8_t *phantom_sword = (uint8_t *)(PHANTOM_SWORD_FLAG_OFFSET); // Check if player has the oshus sword bool has_oshus_sword = (*oshus_sword & OSHUS_SWORD_FLAG_BIT) == OSHUS_SWORD_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) { + 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 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) { *phantom_sword |= PHANTOM_SWORD_FLAG_BIT; + } else if (has_oshus_sword) { + *phantom_sword_blade |= PHANTOM_SWORD_BLADE_FLAG_BIT; } else { *oshus_sword |= OSHUS_SWORD_FLAG_BIT; } From 3e7586e0dca67ed0c0f1ba9fa40d68f868d2a8cd Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 21 Apr 2023 01:40:45 -0400 Subject: [PATCH 2/3] Add test for progressive sword patch --- .../test_data/test_progressive_sword.dsv | Bin 0 -> 524410 bytes tests/desmume/test_progressive_sword.py | 94 ++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 tests/desmume/test_data/test_progressive_sword.dsv create mode 100644 tests/desmume/test_progressive_sword.py 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 0000000000000000000000000000000000000000..d5de19e090aac87603811e0d45fb252d63024342 GIT binary patch literal 524410 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 literal 0 HcmV?d00001 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 + ) From 834ffe162d0f3aeef00251e112b1e85e33a31a02 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 23 Apr 2023 00:01:55 -0400 Subject: [PATCH 3/3] Hook into progressive item check sooner --- base/code/main.asm | 26 +++++++++++++++++++------- base/code/progressive_sword_check.c | 18 ++++++------------ 2 files changed, 25 insertions(+), 19 deletions(-) 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 5482f08e..36b2f1b3 100644 --- a/base/code/progressive_sword_check.c +++ b/base/code/progressive_sword_check.c @@ -8,19 +8,13 @@ #define PHANTOM_SWORD_FLAG_OFFSET (0x21BA608) #define PHANTOM_SWORD_FLAG_BIT (0x20) -__attribute__((target("thumb"))) void progressive_sword_check() { - // Address of oshus sword flag +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); - - // Address of phantom sword blade flag uint8_t *phantom_sword_blade = (uint8_t *)(PHANTOM_SWORD_BLADE_FLAG_OFFSET); - // Address of phantom sword flag - uint8_t *phantom_sword = (uint8_t *)(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; @@ -28,10 +22,10 @@ __attribute__((target("thumb"))) void progressive_sword_check() { // 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) { - *phantom_sword |= PHANTOM_SWORD_FLAG_BIT; + return 0x45; } else if (has_oshus_sword) { - *phantom_sword_blade |= PHANTOM_SWORD_BLADE_FLAG_BIT; + return 0x44; } else { - *oshus_sword |= OSHUS_SWORD_FLAG_BIT; + return 0x3; } }