├── compile.bat ├── interact ├── interact_overlay0000.asm └── interact_arm9_freespace.asm ├── movement ├── movement_overlay0000.asm ├── movement_overlay0029.asm ├── movement_overlay0021.asm └── movement_arm9_freespace.asm ├── attack ├── attack_overlay0000.asm └── attack_arm9_freespace.asm ├── dialog └── dialog_arm9_freespace.asm ├── util.asm ├── .gitignore ├── unpack └── readme.txt ├── README.md ├── compile.asm └── macros.asm /compile.bat: -------------------------------------------------------------------------------- 1 | python build.py 2 | pause -------------------------------------------------------------------------------- /interact/interact_overlay0000.asm: -------------------------------------------------------------------------------- 1 | .rorg 0x020B7938,0x020B7998 2 | bl interact_check_keys 3 | ;eof -------------------------------------------------------------------------------- /movement/movement_overlay0000.asm: -------------------------------------------------------------------------------- 1 | .rorg 0x020B79B0, 0x020B7A10 2 | bl movement_check_keys 3 | ;eof -------------------------------------------------------------------------------- /movement/movement_overlay0029.asm: -------------------------------------------------------------------------------- 1 | .rorg 0x02144148, 0x021441EC 2 | ldrh r2,[r0, 0x00] 3 | bl quickitemmenu_check_buttons 4 | ;eof -------------------------------------------------------------------------------- /attack/attack_overlay0000.asm: -------------------------------------------------------------------------------- 1 | .rorg 0x020A9480,0x020A94E0 2 | bl attack_spin_check_keys 3 | 4 | .rorg 0x020AEC04,0x020AEC64 5 | bl attack_roll_check_keys 6 | 7 | .rorg 0x020B0D74,0x020B0DD4 8 | bl attack_slash_check_keys 9 | ;eof -------------------------------------------------------------------------------- /dialog/dialog_arm9_freespace.asm: -------------------------------------------------------------------------------- 1 | dialog_advance_check_keys: 2 | stmfd r13!, r3-r7, r14 3 | ldrh r1, [r0, 0x34] 4 | bl util_get_keys_down 5 | tst r6, (GBAKEY_A | GBAKEY_B) 6 | orrne r1, r1, 0x01 7 | ldmfd r13!, r3-r7, r15 8 | ;eof -------------------------------------------------------------------------------- /util.asm: -------------------------------------------------------------------------------- 1 | ;gets keys held in r6 2 | util_get_keys_held: 3 | rldr r6, 0x027E05F8, 0x027E05F8 4 | ldrh r6, [r6, 0x00] 5 | bx r14 6 | 7 | util_get_keys_down: 8 | rldr r6, 0x027E05F8, 0x027E05F8 9 | ldrh r6, [r6, 0x02] 10 | bx r14 11 | 12 | util_get_keys_up: 13 | rldr r6, 0x027E05F8, 0x027E05F8 14 | ldrh r6, [r6, 0x04] 15 | bx r14 16 | .pool 17 | ;eof -------------------------------------------------------------------------------- /movement/movement_overlay0021.asm: -------------------------------------------------------------------------------- 1 | ;fix quickmap key 2 | .rorg 0x0211B130, 0x0211B1B0 3 | ldrh r2, [r0, 0x00] 4 | bl quickmap_check_buttons 5 | ;bne 0x0211B150 6 | 7 | .rorg 0x0211B214, 0x0211B294 8 | bl quickmenu_check_buttons 9 | ;beq 0x0211B298 10 | 11 | .rorg 0x0211B2B0, 0x0211B330 12 | ldrh r0, [r0, 0x00] 13 | bl quickstatus_check_buttons 14 | rbne 0x0211B2CC, 0x0211B34C 15 | ;eof -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #backups 2 | backup 3 | #unpacked game 4 | #ignore all but build.bat 5 | unpack/data 6 | unpack/overlay 7 | unpack/arm9.bin 8 | unpack/arm7.bin 9 | unpack/banner.bin 10 | unpack/header.bin 11 | unpack/y7.bin 12 | unpack/y9.bin 13 | #sym files 14 | *.sym 15 | #NDS Games 16 | *.nds 17 | #temp build files 18 | *_compressed.bin 19 | #ignore patches 20 | *.xdelta 21 | #ignore temp files 22 | arm9/ 23 | temp/ -------------------------------------------------------------------------------- /attack/attack_arm9_freespace.asm: -------------------------------------------------------------------------------- 1 | attack_roll_check_keys: 2 | stmfd r13!, r1-r7, r14 3 | rbl 0x020B1270, 0x020B12D0 4 | bl util_get_keys_down 5 | tst r6, GBAKEY_A 6 | beq @@endroutine 7 | bl util_get_keys_held 8 | tst r6, GBAKEY_DIRECTION 9 | beq @@endroutine 10 | mov r0, 0x01 11 | @@endroutine: 12 | ldmfd r13!, r1-r7, r15 13 | 14 | attack_spin_check_keys: 15 | stmfd r13!, r3-r7, r14 16 | strb r1, [r0, 0x04] 17 | bl util_get_keys_held 18 | ldr r7, =(DSKEY_Y | GBAKEY_B) 19 | ands r6, r6, r7 20 | cmp r7, r6 21 | bne @@endroutine 22 | mov r3, 0x01 23 | str r3, [r0, 0x04] 24 | @@endroutine: 25 | ldmfd r13!, r3-r7, r15 26 | 27 | attack_slash_check_keys: 28 | stmfd r13!, r3-r7, r14 29 | mov r0, 0x00 30 | bl util_get_keys_down 31 | tst r6, GBAKEY_B 32 | movne r4, 0x02 33 | beq @@endroutine 34 | bl util_get_keys_held 35 | tst r6, GBAKEY_DIRECTION 36 | movne r4, 0x03 37 | str r4, [r5, 0x00] 38 | mov r0, 0x01 39 | @@endroutine: 40 | ldmfd r13!, r3-r7, r15 41 | ;eof -------------------------------------------------------------------------------- /unpack/readme.txt: -------------------------------------------------------------------------------- 1 | Legend of Zelda Phantom Hourglass D-pad Patch 2 | 3 | 1. Introduction 4 | 5 | This patch adds D-pad controls for essential actions such as walking, 6 | interacting with objects, and attacking. The new control bindings are 7 | Control Bindings: 8 | D-Pad Move 9 | Y+Dpad Move slower 10 | B Wide slash 11 | B+Dpad Long slash 12 | Y+B Spin Attack 13 | A Interact 14 | A+Dpad Roll 15 | A Advance Dialogs 16 | 17 | 18 | 19 | 2. Instructions for Patching 20 | 21 | Apply Patch using the xdelta command line: 22 | 23 | xdelta.exe -d -s (old_file) (delta_file) (decoded_new_file) 24 | 25 | old_file should be a copy of Legend of Zelda Phantom Hourglass US/EU 26 | delta_file is the patch (zelda_phantom_hourglass_dpad.xdelta) 27 | decoded_new_file is the filename after patching 28 | 29 | 3. Contact 30 | 31 | I tried out the patch a bit in the beginning and end of the game 32 | and it doesn't seem to break anything. But I haven't checked the 33 | entire game/story so I don't know if it breaks anything throughout. 34 | You can let me know about any errors or bugs at 35 | griegamaster@gmail.com 36 | -------------------------------------------------------------------------------- /interact/interact_arm9_freespace.asm: -------------------------------------------------------------------------------- 1 | interact_check_keys: 2 | stmfd r13!, r2-r6, r14 3 | strh r1, [r4, 0x62] 4 | sub r13, r13, 0x10 5 | bl util_get_keys_held 6 | tst r6, GBAKEY_DIRECTION 7 | bne @@endroutine 8 | bl util_get_keys_down 9 | tst r6, GBAKEY_A 10 | beq @@endroutine 11 | mov r0, r5 12 | add r1, r13, 0x54 13 | add r2, r13, 0x04 14 | add r3, r13, 0x00 15 | rbl 0x1FFE468, 0x1FFE468 16 | ldrh r7, [r13, 0x00] 17 | ldrh r8, [r13, 0x04] 18 | ldr r0, =0x027E0F64 19 | ldr r0, [r0, 0x00] 20 | rbl 0x0208B120, 0x0208B180 21 | add r0, r0, 0x200 22 | ldrsh r0, [r0, 0x26] 23 | ldrh r1, [r4, 0x6A] 24 | add r1, r1, r0 25 | cmp r1, 0x8000 26 | mov r0, 0x00 27 | addlt r0, r0, 0x1E 28 | subgt r0, r0, 0x1E 29 | cmp r1, 0x00 30 | moveq r0, 0x00 31 | add r8, r8, r0 32 | mov r0, 0x00 33 | cmp r1, 0x4000 34 | addlt r0, r0, 0x1E 35 | ble @@add_done 36 | cmp r1, 0xC000 37 | addgt r0, r0, 0x1E 38 | bgt @@add_done 39 | sublt r0, r0, 0x1E 40 | @@add_done: 41 | add r7, r7, r0 42 | rldr r6, 0x027E0D84, 0x027E0D84 43 | mov r1, 0x01 44 | str r1, [r6, 0x00] 45 | str r8, [r6, 0x04] 46 | str r7, [r6, 0x08] 47 | strb r1, [r4, 0x0C] 48 | str r8, [r4, 0x10] 49 | str r7, [r4, 0x14] 50 | mov r0, r4 51 | mov r1, 0x02 52 | rbl 0x0207AC18, 0x0207AC78 53 | @@endroutine: 54 | add r13, r13, 0x10 55 | ldmfd r13!, r2-r6, r15 56 | ;eof -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Legend of Zelda Phantom Hourglass D-Pad Patch 2 | =============== 3 | This patch implements non-touchscreen controls for essential actions in *The Legend of Zelda: Phantom Hourglass*. 4 | 5 | Control Bindings: 6 | ------------- 7 | * D-Pad = Run 8 | * Y + D-Pad = Walk 9 | * B = Wide Slash 10 | * B + D-Pad = Long slash 11 | * Y + B = Spin Attack 12 | * A = Interact 13 | * A + D-Pad = Roll 14 | * A = Advance Dialog 15 | 16 | Compiling 17 | ------------- 18 | Compiling these sources requires: 19 | 20 | * [Python 3.7+](https://www.python.org/) 21 | * [armips](https://github.com/Kingcom/armips) by Kingcom 22 | * [blz](https://www.romhacking.net/utilities/826/) by CUE 23 | * [ndstool](https://github.com/devkitPro/installer/releases) by DarkFader (*this is included in a devkitPro installation in (**devkitPro install folder**)\tools\bin*) 24 | * *The Legend of Zelda: Phantom Hourglass* ROM 25 | 26 | After obtaining the required tools: 27 | 1. Copy `armips`, `blz`, and `ndstool` to parent directory, with `compile.bat`. 28 | 1. Copy a **US** or **EU** copy of *The Legend of Zelda: Phantom Hourglass* to the `unpack` directory and name it `input.nds`. 29 | 1. Run `compile.bat`. 30 | 31 | Compatibility 32 | ------------- 33 | This patch has only been tested with the **US** and **EU** release of *The Legend of Zelda: Phantom Hourglass*. Support for other regions hasn't been tested so it may or may not work. 34 | -------------------------------------------------------------------------------- /movement/movement_arm9_freespace.asm: -------------------------------------------------------------------------------- 1 | movement_check_keys: 2 | stmfd r13!, r0-r7, r14 3 | rbl 0x020B7888, 0x020B78E8;overwritten opcode 4 | bl util_get_keys_held 5 | tst r6, GBAKEY_SELECT 6 | ;If select is pressed no movement 7 | bne @@endroutine 8 | ands r1, r6, GBAKEY_DIRECTION 9 | mov r3, 0x07 * 0x04 10 | ldr r2, =@@dpad_movement_angle_pool 11 | @@key_lookup_start: 12 | ldrh r5, [r2, r3] 13 | cmp r5, r1 14 | beq @@keyfound 15 | subs r3, 0x04 16 | bge @@key_lookup_start 17 | b @@endroutine 18 | @@keyfound: 19 | mov r0, 0x5F0000 20 | tst r6, DSKEY_Y 21 | movne r0, 0x060000 22 | str r0, [r4, 0x58] 23 | 24 | ;get map angle 25 | rldr r0, 0x027E0F64, 0x027E0F64 26 | ldr r0, [r0, 0x00] 27 | rbl 0x0208B120, 0x0208B180 28 | add r0 ,r0, 0x200 29 | ldrsh r5, [r0, 0x26] 30 | 31 | add r3, 0x02 32 | ldr r2, =@@dpad_movement_angle_pool 33 | ldrh r1, [r2, r3];get angle for direction 34 | add r1, r5, r1 35 | strh r1, [r4, 0x6A] 36 | mov r2, 0x00 37 | strh r2, [r4, 0x60] 38 | strh r2, [r4, 0x64] 39 | ldmfd r13!, r0-r7, r15 40 | @@endroutine: 41 | @@end2: 42 | ldmfd r13!, r0-r7, r15 43 | @@dpad_movement_angle_pool: 44 | .halfword (GBAKEY_UP), 0x8000;up 45 | .halfword (GBAKEY_DOWN), 0x0000;down 46 | .halfword (GBAKEY_LEFT), 0xC000;left 47 | .halfword (GBAKEY_RIGHT), 0x4000;right 48 | .halfword (GBAKEY_UP | GBAKEY_RIGHT), 0x6000;UPRIGHT 49 | .halfword (GBAKEY_DOWN | GBAKEY_RIGHT), 0x2000;DOWNRIGHT 50 | .halfword (GBAKEY_UP | GBAKEY_LEFT), 0xA000;UPLEFT 51 | .halfword (GBAKEY_DOWN | GBAKEY_LEFT), 0xE000;DOWNLEFT 52 | 53 | quickmenu_check_buttons: 54 | stmfd r13!, r0-r7, r14 55 | tst r2, GBAKEY_SELECT 56 | blne util_get_keys_down 57 | tstne r6, GBAKEY_LEFT 58 | ldmfd r13!, r0-r7, r15 59 | 60 | quickitemmenu_check_buttons: 61 | stmfd r13!, r0-r7, r14 62 | tst r2, GBAKEY_SELECT 63 | blne util_get_keys_down 64 | tstne r6, GBAKEY_RIGHT 65 | ldmfd r13!, r0-r7, r15 66 | 67 | quickmap_check_buttons: 68 | stmfd r13!, r0-r7, r14 69 | tst r2, GBAKEY_SELECT 70 | blne util_get_keys_down 71 | tstne r6, GBAKEY_DOWN 72 | ldmfd r13!, r0-r7, r15 73 | 74 | quickstatus_check_buttons: 75 | ands r0, r0 ,GBAKEY_UP | GBAKEY_SELECT 76 | cmp r0, GBAKEY_UP | GBAKEY_SELECT 77 | mov r15, r14 78 | ;eof -------------------------------------------------------------------------------- /compile.asm: -------------------------------------------------------------------------------- 1 | .nds 2 | .relativeinclude on 3 | .include "macros.asm" 4 | 5 | ;edit the ARM9 6 | ;uncompressed sizes: 0x5CF78, 0x5CFD8 7 | ;RAM ranges: 0x02004000-0x02060F77, 0x02004000-0x02060FD7 8 | .ropen "arm9/arm9.bin", "arm9/arm9.bin", \ 9 | "temp/arm9.bin", "temp/arm9.bin", \ 10 | 0x02004000, 0x02004000 11 | ;advance dialog with A/B in addition to tap 12 | .rorg 0x0203A474, 0x0203A4B8 13 | bl dialog_advance_check_keys 14 | 15 | ;stops game from clearing the pressed buttons during boss battles 16 | .rorg 0x0202ADEC, 0x0202AE04 17 | rb 0x0202ADF8, 0x0202AE10 18 | 19 | ;ARM9 "freespace" 20 | .rorg 0x02058608, 0x02058664 21 | .area 0x4B4 22 | .include "util.asm" 23 | .include "movement/movement_arm9_freespace.asm" 24 | .include "interact/interact_arm9_freespace.asm" 25 | .include "attack/attack_arm9_freespace.asm" 26 | .include "dialog/dialog_arm9_freespace.asm" 27 | .pool 28 | .endarea 29 | .close 30 | 31 | ;edit the overlay0000 (contains code for movement with touchscreen) 32 | ;uncompressed size: 0x71F60, 0x71F60 33 | ;RAM range 0x02077360-0x020E92C0, 0x020773C0-0x020E9320 34 | .ropen "arm9/overlay_0000.bin", "arm9/overlay_0000.bin", \ 35 | "temp/overlay_0000.bin", "temp/overlay_0000.bin", \ 36 | 0x02077360, 0x020773C0 37 | .include "interact/interact_overlay0000.asm" 38 | .include "attack/attack_overlay0000.asm" 39 | .include "movement/movement_overlay0000.asm" 40 | .close 41 | 42 | ;edit the overlay0021/20 (contains checks for key inputs for quick menus) 43 | ;EU uses overlay20 for this but it was extracted as "overlay_0021_original.bin" to work around an armips bug (this is fixed in later releases) 44 | ;uncompressed size: 0xC900 45 | ;RAM range 0x02112BA0-0x0211F4A0 46 | .ropen "arm9/overlay_0021.bin", "arm9/overlay_0020.bin", \ 47 | "temp/overlay_0021.bin", "temp/overlay_0020.bin", \ 48 | 0x02112BA0, 0x02112C20 49 | .include "movement/movement_overlay0021.asm" 50 | .close 51 | 52 | ;edit the overlay0029 (contains checks for key inputs for quick menus) 53 | ;uncompressed size: 0x3B560, 0x3B580 54 | ;RAM range 0x0211F5C0-0x0215AB20 55 | .ropen "arm9/overlay_0029.bin", "arm9/overlay_0029.bin", \ 56 | "temp/overlay_0029.bin", "temp/overlay_0029.bin", \ 57 | 0x0211F5C0, 0x0211F640 58 | .include "movement/movement_overlay0029.asm" 59 | .close 60 | ;eof 61 | -------------------------------------------------------------------------------- /macros.asm: -------------------------------------------------------------------------------- 1 | ;Regions 2 | REGION_US equ 0 3 | REGION_EU equ 1 4 | 5 | ;KeyPad Macros 6 | GBAKEY_A equ 1<<0;0x01 7 | GBAKEY_B equ 1<<1;0x02 8 | GBAKEY_SELECT equ 1<<2;0x04 9 | GBAKEY_START equ 1<<3;0x08 10 | GBAKEY_RIGHT equ 1<<4;0x10 11 | GBAKEY_LEFT equ 1<<5;0x20 12 | GBAKEY_UP equ 1<<6;0x40 13 | GBAKEY_DOWN equ 1<<7;0x80 14 | GBAKEY_DIRECTION equ 0xF0; 15 | GBAKEY_R equ 1<<8;0x100 16 | GBAKEY_L equ 1<<9;0x200 17 | DSKEY_X equ 1<<10;0x400 18 | DSKEY_Y equ 1<<11;0x800 19 | 20 | ;Sets the output pointer depending on the game region 21 | .macro .rorg, us_offset, eu_offset 22 | .if current_region == REGION_US 23 | .org us_offset 24 | .elseif current_region == REGION_EU 25 | .org eu_offset 26 | .endif 27 | .endmacro 28 | 29 | ;Open depending on the game region 30 | .macro .ropen, us_original_file, eu_original_file, us_output_file, eu_output_file, us_offset, eu_offset 31 | .if current_region == REGION_US 32 | .open us_original_file, us_output_file, us_offset 33 | .elseif current_region == REGION_EU 34 | .open eu_original_file, eu_output_file, eu_offset 35 | .endif 36 | .endmacro 37 | 38 | ;Branches depending on game region 39 | .macro rbl, us_offset, eu_offset 40 | .if current_region == REGION_US 41 | bl us_offset 42 | .elseif current_region == REGION_EU 43 | bl eu_offset 44 | .endif 45 | .endmacro 46 | 47 | ;Branches depending on game region 48 | .macro rb, us_offset, eu_offset 49 | .if current_region == REGION_US 50 | b us_offset 51 | .elseif current_region == REGION_EU 52 | b eu_offset 53 | .endif 54 | .endmacro 55 | 56 | ;Branches depending on game region 57 | .macro rbne, us_offset, eu_offset 58 | .if current_region == REGION_US 59 | bne us_offset 60 | .elseif current_region == REGION_EU 61 | bne eu_offset 62 | .endif 63 | .endmacro 64 | 65 | ;Branches depending on game region 66 | .macro rbeq, us_offset, eu_offset 67 | .if current_region == REGION_US 68 | beq us_offset 69 | .elseif current_region == REGION_EU 70 | beq eu_offset 71 | .endif 72 | .endmacro 73 | 74 | ;loads value to register depending on game region 75 | .macro rldr, reg, us_offset, eu_offset 76 | .if current_region == REGION_US 77 | ldr reg, =us_offset 78 | .elseif current_region == REGION_EU 79 | ldr reg, =eu_offset 80 | .endif 81 | .endmacro 82 | 83 | ;loads value to register depending on game region 84 | .macro rldrne, reg, us_offset, eu_offset 85 | .if current_region == REGION_US 86 | ldrne reg, =us_offset 87 | .elseif current_region == REGION_EU 88 | ldrne reg, =eu_offset 89 | .endif 90 | .endmacro 91 | 92 | ;eof 93 | --------------------------------------------------------------------------------