From 890a167367759a54f01d140a078033ac501080f9 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Mon, 23 Mar 2026 03:21:00 +0900
Subject: [PATCH 01/28] toggable_crash_bugs.
implement changes to the vanilla bugfixes code to allow for customization on what to do with the hardlock bugs.
---
patches/rom_map/Bank 82.txt | 2 +-
patches/rom_map/Bank 84.txt | 1 +
patches/rom_map/Bank 85.txt | 2 +-
patches/rom_map/RAM.txt | 1 +
patches/src/vanilla_bugfixes.asm | 190 ++++++++++++++++++++++++-------
5 files changed, 152 insertions(+), 44 deletions(-)
diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt
index 2d0926b14..017327f7e 100644
--- a/patches/rom_map/Bank 82.txt
+++ b/patches/rom_map/Bank 82.txt
@@ -6,7 +6,7 @@ C37E - C385:
C3B6 - C3BD: map_area.asm
C3C4 - C3D9: map_area.asm
C411 - C42D: map_area.asm
-C42D - C465:
+C42D - C465: split_speed.asm
F70F - F810: map_area.asm
F810 - F830: vanilla_bugfixes.asm
F830 - F9E0: disable_etanks.asm
diff --git a/patches/rom_map/Bank 84.txt b/patches/rom_map/Bank 84.txt
index 934cc1b76..d141231be 100644
--- a/patches/rom_map/Bank 84.txt
+++ b/patches/rom_map/Bank 84.txt
@@ -1,3 +1,4 @@
+$858C - $85B2: vanilla_bugfixes.asm (unused vanilla code, repurposed for main street save station plm check.)
$EFD3 - $EFD7: load_plms_early.asm
$EFD7 - $F000: vanilla_bugfixes.asm
$F000 - $F0E2: walljump_plm.asm
diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt
index 318025ef4..51a24aaf6 100644
--- a/patches/rom_map/Bank 85.txt
+++ b/patches/rom_map/Bank 85.txt
@@ -16,7 +16,7 @@ $AA00 - $AAA0: pause_menu_objectives.asm
$AAA0 - $AB00: [FREE]
$AB00 - $ACA0: map_area.asm
$ACA0 - $AD00: load_flash_suit
-$AD00 - $AE20: [FREE]
+$AD00 - $AE20: vanilla_bugfixes.asm
$AE20 - $B000: reserve_backward_fill.asm
$B000 - $B600: vanilla_bugfixes.asm
$B600 - $BA00: map_area.asm
\ No newline at end of file
diff --git a/patches/rom_map/RAM.txt b/patches/rom_map/RAM.txt
index 9b4919b17..7c79659cb 100644
--- a/patches/rom_map/RAM.txt
+++ b/patches/rom_map/RAM.txt
@@ -1,5 +1,6 @@
$95: temporary variable (unused debug location) - used by extended messageboxes, safe to use outside message boxes.
$9B: decompression.asm
+$CF: vanilla_bugfixes.asm (used by crash dialogues to prevent unpause from resetting gamestate.)
$09EC: number of disabled ETanks (saved to SRAM)
$0A1A: previous reserve health (reserve_hud.asm)
$1F5B: map area (like $079F)
diff --git a/patches/src/vanilla_bugfixes.asm b/patches/src/vanilla_bugfixes.asm
index 324172bcb..99b2affba 100644
--- a/patches/src/vanilla_bugfixes.asm
+++ b/patches/src/vanilla_bugfixes.asm
@@ -8,30 +8,53 @@
arch snes.cpu
lorom
-!bank_80_free_space_start = $80D200
+incsrc "constants.asm"
+
+!bank_80_free_space_start = $80D200 ; camera alignment fix.
!bank_80_free_space_end = $80D240
-!bank_86_free_space_start = $86F4B0
+
+!bank_82_free_space2_start = $82f810 ; hook unpause (crash dialog boxes)
+!bank_82_free_space2_end = $82f830
+
+!bank_82_free_space_start = $82fbf0 ; pause / reserve bug
+!bank_82_free_space_end = $82fc30
+
+!bank_83_free_space_start = $83BA00 ; reserve icon ui fix.
+!bank_83_free_space_end = $83BA15
+
+!bank_84_free_space_start = $84EFD7 ; maridia tube fix
+!bank_84_free_space_end = $84F000
+
+!bank_85_free_space2_start = $85AD00 ; customizable crash dialogs
+!bank_85_free_space2_end = $85AE20
+
+!bank_85_free_space_start = $85b000 ; do not change, first jmp used externally (crash dialog boxes)
+!bank_85_free_space_end = $85b5FF
+
+!bank_86_free_space_start = $86F4B0 ; powamp projectile fix
!bank_86_free_space_end = $86F4D0
-!msg_crash_timer_override = $7EF596
-incsrc "constants.asm"
+
+!msg_crash_timer_override = $7EF596 ; temporary variable used for overriding messagebox close delay times during crash box.
+
+
; Fix the crash that occurs when you kill an eye door whilst a eye door projectile is alive
; See the comments in the bank logs for $86:B6B9 for details on the bug
; The fix here is setting the X register to the enemy projectile index,
; which can be done without free space due to an unnecessary RTS in the original code
org $86B704
-BEQ ret
-TYX
+ BEQ ret
+ TYX
org $86B713
-ret:
+ ret:
;;; skips suits acquisition animation
org $848717
;rep 4 : nop
- JSL $91DEBA ; call the load samus suit palette to update the suit.
+ JSL $91DEBA ; call the load samus suit palette to update the suit.
assert pc() <= $84871B ; just to make sure the next instruction isn't overwritten
@@ -43,21 +66,24 @@ org $91b629
;;; fix screw attack select in pause menu
org $82b4c4
cpx #$000c
+
;;; In inventory menu, when having only a beam and a suit you have
;;; to press right+up to go from beam to suit.
;;; It's not natural, so fix it to only require right.
+
org $82b000
+ bcc $64
;; test of return of $B4B7 compare A and #$0000,
;; when no item found A==#$ffff, which sets the carry,
;; so when carry is clear it means that an item was found in misc.
;; if no item was found in misc, we check in boots then in suits,
;; so if an item is found in both boots and suits, as suits is
;; tested last the selection will be set on suits.
- bcc $64
;;; fix morph ball in hidden chozo PLM
org $84e8ce
db $04
+
org $84ee02
db $04
@@ -83,6 +109,7 @@ assert pc() <= $a0f830
;;; allow all possible save slots (needed for area rando extra stations)
org $848d0c
and #$001f
+
;;; For an unknown reason, the save station we put in main street
;;; sometimes (circumstances unclear) spawn two detection PLMs
;;; instead of one. These PLMs are supposed to precisely detect
@@ -96,14 +123,18 @@ org $848d0c
;;; hijack in detection block PLM code when samus is
;;; positioned correctly
+
org $84b5d4
search_loop_start:
jmp save_station_check
+
org $84b5d9
search_loop_cont:
+
org $84b5df
search_loop_found:
-;;; some unused bank 84 space
+
+;;; some unused bank 84 space
org $84858c
save_station_check:
cmp $1c87,x ; original block coord check
@@ -119,7 +150,6 @@ save_station_check:
pla
jmp search_loop_found
-;;; end of unused space
assert pc() <= $8485b2
@@ -142,7 +172,6 @@ fix_big_boy:
org $A9EF80
.done
-
; Fix Bomb Torizo crumbling animation (which can be very messed up if the player earlier visited a room
; that maxed out enemy projectiles)
org $86A8FD
@@ -207,6 +236,7 @@ check_item_plm:
rts
assert pc() <= $848398
+
org $848398
special_xray_end:
@@ -216,6 +246,7 @@ special_xray_end:
org $b4bda3
bpl $f8 ; was bne $f8
+
; Fix auto-reserve / pause bug
;
; This patch will initiate the death sequence if pause hit with auto-reserve enabled
@@ -223,17 +254,16 @@ org $b4bda3
;
; (thanks to Benox50 for his initial patch, nn44/aquanight for the light pillar fix)
-!bank_82_free_space_start = $82fbf0
-!bank_82_free_space_end = $82fc30
+
org $828b3f
jsr pause_func : nop ; gamestate 1Ch (unused) handler
org !bank_82_free_space_start
pause_func:
- lda #$0041 ; msg ID
- jsl bug_dialog
- stz $9d6 ; clear reserve health
+ jsl fp_pause
+ phk
+ plb
rts
assert pc() <= !bank_82_free_space_end
@@ -289,8 +319,9 @@ org !bank_90_free_space_start
yapping_maw_crash:
cmp #$0005 ; valid table entries are 0-2 * 2
bcc .skip
- lda #$0043 ; bug ID
- jsl bug_dialog
+ jsl ym_crash
+ phk
+ plb
rts
.skip
@@ -310,9 +341,6 @@ org $91ea07
org $91f1fc
jsl spring_ball_crash
-!bank_85_free_space_start = $85b000 ; do not change, first jmp used externally
-!bank_85_free_space_end = $85b5FF
-
org !bank_85_free_space_start
jmp bug_dialog ; for external calls, do not move
@@ -320,20 +348,37 @@ spring_ball_crash:
lda $0B20 ; morph bounce state
cmp #$0600 ; bugged?
bcc .skip
+
+ lda !bank_85_free_space2_start
+ and #$000F
+ beq .default
+ cmp #$0001
+ beq .warn
+ cmp #$0002
+ beq .fix
+.default
sep #$20
lda #$42 ; bug ID
sta $00cf ; set flag to prevent unpause from resetting gamestate to 8
rep #$30
jsl bug_dialog
+ jsr kill_samus
lda #$0000
stz $0B20
rtl
-
.skip
lda $0B20 ; replaced code
asl ;
rtl
+.warn
+ lda #$0042
+ jsl bug_dialog
+.fix
+ lda #$0000
+ stz $0b20
+ rtl
+
;;; Implementation of custom dialog boxes
;;; Requires hooking multiple functions to support extended msg IDs (0x40+)
;;; and additional lookup tables
@@ -350,14 +395,11 @@ bug_dialog: ; A = msg ID
pla ; dlg box parameter
jsl $858080 ; dlg box
-
- lda #$8000 ; init death sequence (copied from $82db80)
- sta $a78
- lda #$0011
- jsl $90f084
-
- lda #$0013 ; set gamestate
- sta $998
+ cmp #$0044
+ bcs .xoob_crash
+ rtl
+.xoob_crash ;if this crash dialog is ever called it is safe to assume oob death is on so also kill samus.
+ jsr kill_samus
rtl
hook_message_box:
@@ -428,8 +470,8 @@ reserve_pause_msg:
springball_msg:
dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, "UNEQUIPPED SPRING BALL IN ", $000e,$000e,$000e
- dw $000e,$000e,$000e, "UNDERWATER NEUTRAL BOUNCE!", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " UNEQUIPPED SPRING BALL ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " IN NEUTRAL BOUNCE! ", $000e,$000e,$000e
yapping_maw_msg:
dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
@@ -486,9 +528,6 @@ org $85840c
org $8293bb
jmp check_unpause
-!bank_82_free_space2_start = $82f810
-!bank_82_free_space2_end = $82f830
-
org !bank_82_free_space2_start
check_unpause:
php
@@ -506,6 +545,79 @@ check_unpause:
assert pc() <= !bank_82_free_space2_end
+org !bank_85_free_space2_start
+ dw $0000 ; to be set by patch.rs 0 = default (msg+kill), 1 = warn (msg only), 2 = silent (fix only).
+ ; unused / yappingmaw crash / reserve pause / springball
+kill_samus:
+ lda #$8000 ; init death sequence (copied from $82db80)
+ sta $a78
+ lda #$0011
+ jsl $90f084
+
+ lda #$0013 ; set gamestate
+ sta $998
+ rts
+
+
+fp_pause:
+ lda !bank_85_free_space2_start
+ and #$00F0
+ beq .default
+ cmp #$0010
+ beq .warn
+ cmp #$0020
+ beq .fix
+.default
+ lda #$0041 ; msg ID
+ jsl bug_dialog
+ phk
+ plb
+ jsr kill_samus
+ stz $9d6 ; clear reserve health
+ rtl
+.warn
+ lda #$0041 ; "fix" leave the screen black, "warning" will relight the screen due to showing the dialog
+ jsl bug_dialog
+
+.fix
+ lda #$0008
+ sta $0998
+ rtl
+
+ym_crash:
+ lda !bank_85_free_space2_start
+ and #$0F00
+ beq .default
+ cmp #$0100
+ beq .warn
+ cmp #$0200
+ beq .fix
+
+.default
+ lda #$0043 ; bug ID
+ jsl bug_dialog
+ phk
+ plb
+ jsr kill_samus
+ rtl
+.warn
+ lda #$0043 ; bug ID
+ jsl bug_dialog
+
+.fix
+ lda #$d3f3
+ sta $0a58
+ lda #$001e
+ sta $0aa2
+ sta $0ac0
+ sta $0ac2
+ lda #$0000
+ ldx #$0000
+ ldy #$0004
+ rtl
+
+assert pc() <= !bank_85_free_space2_end
+
; Map scrolling bug
; Leftmost edge function @ $829f4a has an off-by-one bug when scanning
; the 2nd page of an area. This can lead to shifted map placement as
@@ -532,9 +644,6 @@ assert pc() <= $918181 ; Make sure we don't overwrite the next routine.
; (Maridia Tube Fix - written by AmoebaOfDoom)
;patches horizontal PLM updates to DMA tiles even when the PLM is above the screen if part of it is on the screen
-!bank_84_free_space_start = $84EFD7
-!bank_84_free_space_end = $84F000
-
org $848DA0
SkipEntry_Inject:
JMP SkipEntry
@@ -581,9 +690,6 @@ org $84B7EF ; PLM $B8AC (speed booster escape) LDA $09A4 [$7E:09A4]
; vanilla bugfix, ui shows reserve icon when only boots item is equipped.
;
-!bank_83_free_space_start = $83BA00
-!bank_83_free_space_end = $83BA15
-
org !bank_83_free_space_start
BNE .found
From e883f3aefca7e5c64dc7d2a89bcb13674c63fe71 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Mon, 23 Mar 2026 03:33:22 +0900
Subject: [PATCH 02/28] Update vanilla_bugfixes.asm
---
patches/src/vanilla_bugfixes.asm | 1 -
1 file changed, 1 deletion(-)
diff --git a/patches/src/vanilla_bugfixes.asm b/patches/src/vanilla_bugfixes.asm
index 99b2affba..3a5763eb5 100644
--- a/patches/src/vanilla_bugfixes.asm
+++ b/patches/src/vanilla_bugfixes.asm
@@ -348,7 +348,6 @@ spring_ball_crash:
lda $0B20 ; morph bounce state
cmp #$0600 ; bugged?
bcc .skip
-
lda !bank_85_free_space2_start
and #$000F
beq .default
From 2d92cf415b88a0e1abbd8cb2aa012091b8290834 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Mon, 23 Mar 2026 22:07:26 +0900
Subject: [PATCH 03/28] refine code a little
---
patches/src/vanilla_bugfixes.asm | 85 +++++++++++++++++---------------
1 file changed, 44 insertions(+), 41 deletions(-)
diff --git a/patches/src/vanilla_bugfixes.asm b/patches/src/vanilla_bugfixes.asm
index 3a5763eb5..730d5ed5d 100644
--- a/patches/src/vanilla_bugfixes.asm
+++ b/patches/src/vanilla_bugfixes.asm
@@ -348,13 +348,19 @@ spring_ball_crash:
lda $0B20 ; morph bounce state
cmp #$0600 ; bugged?
bcc .skip
+
lda !bank_85_free_space2_start
and #$000F
beq .default
- cmp #$0001
- beq .warn
cmp #$0002
beq .fix
+.warn
+ lda #$0042
+ jsl bug_dialog
+.fix
+ lda #$0000
+ stz $0b20
+ rtl
.default
sep #$20
lda #$42 ; bug ID
@@ -369,13 +375,6 @@ spring_ball_crash:
lda $0B20 ; replaced code
asl ;
rtl
-.warn
- lda #$0042
- jsl bug_dialog
-.fix
- lda #$0000
- stz $0b20
- rtl
;;; Implementation of custom dialog boxes
@@ -395,10 +394,9 @@ bug_dialog: ; A = msg ID
pla ; dlg box parameter
jsl $858080 ; dlg box
cmp #$0044
- bcs .xoob_crash
- rtl
-.xoob_crash ;if this crash dialog is ever called it is safe to assume oob death is on so also kill samus.
+ bne .skipkill ; oob death (dlg 44) is removable via major glitches patch, if its thrown here then the intent is to kill as it isn't toggleable.
jsr kill_samus
+.skipkill
rtl
hook_message_box:
@@ -545,64 +543,46 @@ check_unpause:
assert pc() <= !bank_82_free_space2_end
org !bank_85_free_space2_start
- dw $0000 ; to be set by patch.rs 0 = default (msg+kill), 1 = warn (msg only), 2 = silent (fix only).
- ; unused / yappingmaw crash / reserve pause / springball
+ dw $1111 ; to be set by patch.rs 0 = default (msg+kill), 1 = warn (msg only), 2 = silent (fix only).
+ ; xmode / yappingmaw crash / reserve pause / springball
kill_samus:
lda #$8000 ; init death sequence (copied from $82db80)
sta $a78
lda #$0011
jsl $90f084
-
lda #$0013 ; set gamestate
sta $998
rts
-
fp_pause:
lda !bank_85_free_space2_start
and #$00F0
beq .default
- cmp #$0010
- beq .warn
cmp #$0020
beq .fix
-.default
- lda #$0041 ; msg ID
- jsl bug_dialog
- phk
- plb
- jsr kill_samus
- stz $9d6 ; clear reserve health
- rtl
.warn
- lda #$0041 ; "fix" leave the screen black, "warning" will relight the screen due to showing the dialog
+ lda #$0041 ; "fix" leave the screen black warning will relight the screen due to showing the dialog
jsl bug_dialog
-
.fix
lda #$0008
sta $0998
rtl
+.default
+ lda #$0041 ; msg ID
+ jsl bug_dialog
+ jsr kill_samus
+ stz $9d6 ; clear reserve health
+ rtl
ym_crash:
lda !bank_85_free_space2_start
and #$0F00
beq .default
- cmp #$0100
- beq .warn
cmp #$0200
beq .fix
-
-.default
- lda #$0043 ; bug ID
- jsl bug_dialog
- phk
- plb
- jsr kill_samus
- rtl
.warn
lda #$0043 ; bug ID
jsl bug_dialog
-
.fix
lda #$d3f3
sta $0a58
@@ -614,8 +594,31 @@ ym_crash:
ldx #$0000
ldy #$0004
rtl
+.default
+ lda #$0043 ; bug ID
+ jsl bug_dialog
+ jsr kill_samus
+ rtl
+
+xmode:
+ lda !bank_85_free_space2_start
+ and #$F000
+ beq .default
+ ;cmp #$0200
+ ;beq .fix
+.warn
+ ;lda #$0045 ; crash dialog (warning) removed until better solution found, it will re-trigger many times until samus is out of collission so annoying.
+ ;jsl bug_dialog
+.fix
+ rtl
+.default
+ lda #$0045
+ jsl bug_dialog
+ jsr kill_samus
+ rtl
+
-assert pc() <= !bank_85_free_space2_end
+assert pc() <= !bank_85_free_space2_end ;ad9d
; Map scrolling bug
; Leftmost edge function @ $829f4a has an off-by-one bug when scanning
@@ -634,7 +637,7 @@ org $91816f ; rewrite original input handler for solid tile collision to free up
LDA $0A78 ; load frozen time variable
BEQ $07 ; if time is NOT frozen skip over the next instruction (kill samus)
LDA #$0045 ; load the msg bug id for X-Mode Collision
- JSL $85B000 ; collision with solid tile while time is frozen is only possible in x-mode so jump to code that will kill samus rather than hardlock (retain vanilla behaviour)
+ JSL xmode ; collision with solid tile while time is frozen is only possible in x-mode so jump to code that will kill samus rather than hardlock (retain vanilla behaviour)
PLP
RTS
From f0a0ccb430021e3b25dbdb3f5fae521f475c08da Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Tue, 24 Mar 2026 01:19:47 +0900
Subject: [PATCH 04/28] backend html
---
.../templates/generate/crash_fixes.html | 75 +++++++++++++++++++
.../templates/generate/game_variations.html | 43 +++++++++--
.../generate/help/variations/crash_fixes.html | 52 +++++++++++++
3 files changed, 162 insertions(+), 8 deletions(-)
create mode 100644 rust/maprando-web/templates/generate/crash_fixes.html
create mode 100644 rust/maprando-web/templates/generate/help/variations/crash_fixes.html
diff --git a/rust/maprando-web/templates/generate/crash_fixes.html b/rust/maprando-web/templates/generate/crash_fixes.html
new file mode 100644
index 000000000..5e75a6127
--- /dev/null
+++ b/rust/maprando-web/templates/generate/crash_fixes.html
@@ -0,0 +1,75 @@
+
+
+
+
+
Crash Fixes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rust/maprando-web/templates/generate/game_variations.html b/rust/maprando-web/templates/generate/game_variations.html
index 87b09345f..c64b570f2 100644
--- a/rust/maprando-web/templates/generate/game_variations.html
+++ b/rust/maprando-web/templates/generate/game_variations.html
@@ -28,18 +28,22 @@
-
+
-
+
-
+
-
+
-
@@ -66,6 +70,27 @@
+
+
+ {% include "help/variations/crash_fixes.html" %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% include "help/variations/energy_free_shinesparks.html" %}
@@ -109,7 +134,8 @@
-
+
@@ -149,3 +175,4 @@
{% include "area_assignment.html" %}
+{% include "crash_fixes.html" %}
\ No newline at end of file
diff --git a/rust/maprando-web/templates/generate/help/variations/crash_fixes.html b/rust/maprando-web/templates/generate/help/variations/crash_fixes.html
new file mode 100644
index 000000000..d51808696
--- /dev/null
+++ b/rust/maprando-web/templates/generate/help/variations/crash_fixes.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
Crash Fixes
+
+
+
+
Change the behaviour of the crash handler. This feature prevents a hardlock if any of the following bugs are
+ triggered:
+
+
+
Spring Ball bounce: Triggered by pausing and unequipping Spring ball at certain points
+ mid bounce.
+
+
Yapping Maw shinespark: If grabbed by a Yapping Maw during a shinespark followed by
+ having no inputs held during the shinespark end.
+
+
Frame perfect pause, autoreserve: Having autoreserves trigger on the exact frame the
+ screen fades out during a pause.
+
+
X-Mode tile collision: Colliding with a solid tile during X-Mode traversal.
+
+
+ Individual customization options are available by clicking the gear button on the right.
+
+
+
+
+
Default: When triggered, a dialog box will appear explaining the crash followed by
+ triggering a game-over, preventing the vanilla hardlock.
+
+
Warn: When triggered, a dialog box will appear explaining the crash but game play will
+ continiue after the dialog box is closed.
+
+
Silent: When triggered, no dialog box will appear and the game will continue as if the
+ crash hadn't been triggered.
+
+
+
+
+
+
+
+
\ No newline at end of file
From 33f1e98479ff4ecbc9327c2a3f49da354ac6422b Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Tue, 24 Mar 2026 03:01:50 +0900
Subject: [PATCH 05/28] html
some changes to the web frontend code
---
.../Community Race Season 4.json | 7 +++
rust/data/presets/full-settings/Default.json | 7 +++
.../templates/generate/crash_fixes.html | 2 +-
.../templates/generate/game_variations.html | 6 +-
.../templates/generate/scripts.html | 57 +++++++++++++++++++
5 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/rust/data/presets/full-settings/Community Race Season 4.json b/rust/data/presets/full-settings/Community Race Season 4.json
index 58859f508..473262c66 100644
--- a/rust/data/presets/full-settings/Community Race Season 4.json
+++ b/rust/data/presets/full-settings/Community Race Season 4.json
@@ -4665,6 +4665,13 @@
},
"door_locks_size": "Large",
"map_station_reveal": "Full",
+ "crash_fixes": {
+ "preset": "Default",
+ "spring_ball": "Default",
+ "yapping_maw": "Default",
+ "auto_reserve": "Default",
+ "x-mode": "Default"
+ },
"energy_free_shinesparks": false,
"ultra_low_qol": false,
"disable_spikesuit": false,
diff --git a/rust/data/presets/full-settings/Default.json b/rust/data/presets/full-settings/Default.json
index b7818e54d..d3bacaba3 100644
--- a/rust/data/presets/full-settings/Default.json
+++ b/rust/data/presets/full-settings/Default.json
@@ -4666,6 +4666,13 @@
"transition_letters": true,
"door_locks_size": "Large",
"map_station_reveal": "Full",
+ "crash_fixes": {
+ "preset": "Default",
+ "spring_ball": "Default",
+ "yapping_maw": "Default",
+ "auto_reserve": "Default",
+ "x-mode": "Default"
+ },
"energy_free_shinesparks": false,
"disable_spikesuit": false,
"disable_bluesuit": false,
diff --git a/rust/maprando-web/templates/generate/crash_fixes.html b/rust/maprando-web/templates/generate/crash_fixes.html
index 5e75a6127..c3696504e 100644
--- a/rust/maprando-web/templates/generate/crash_fixes.html
+++ b/rust/maprando-web/templates/generate/crash_fixes.html
@@ -10,7 +10,7 @@
Change the behaviour of the crash handler. This feature prevents a hardlock if any of the following bugs are
+
Change the behaviour of how game crashes are handled. This feature can prevent a hardlock if any of the following bugs are
triggered:
@@ -32,14 +32,16 @@
Crash Fixes
-
Default: When triggered, a dialog box will appear explaining the crash followed by
- triggering a game-over, preventing the vanilla hardlock.
+
Death: When triggered, a dialog box will appear explaining the crash followed by
+ triggering a death sequence / game-over, preventing the vanilla hardlock.
Warn: When triggered, a dialog box will appear explaining the crash but game play will
continiue after the dialog box is closed.
Silent: When triggered, no dialog box will appear and the game will continue as if the
crash hadn't been triggered.
+
+
Crash: When triggered, only the vanilla code will run. This WILL cause a hardlock of the game.
Crash: When triggered, only the vanilla code will run. This WILL cause a hardlock of the game.
Death: When triggered, a dialog box will appear explaining the crash followed by
triggering a death sequence / game-over, preventing the vanilla hardlock.
@@ -40,8 +42,6 @@
Crash Fixes
Silent: When triggered, no dialog box will appear and the game will continue as if the
crash hadn't been triggered.
-
-
Crash: When triggered, only the vanilla code will run. This WILL cause a hardlock of the game.
diff --git a/rust/maprando/tests/logic_scenarios.rs b/rust/maprando/tests/logic_scenarios.rs
index 302dac501..1c0cface7 100644
--- a/rust/maprando/tests/logic_scenarios.rs
+++ b/rust/maprando/tests/logic_scenarios.rs
@@ -302,7 +302,7 @@ fn get_settings(scenario: &Scenario) -> Result {
door_locks_size: maprando::settings::DoorLocksSize::Large,
map_station_reveal: maprando::settings::MapStationReveal::Full,
crash_fixes: maprando::settings::CrashFixes::from_preset(
- maprando::settings::CrashFixesPreset::Default,
+ maprando::settings::CrashFixesPreset::Death,
),
energy_free_shinesparks: settings.energy_free_shinesparks.unwrap_or(false),
speed_booster: maprando::settings::SpeedBooster::Vanilla,
From f63df6171b1a3579f149e3977ca18024605f1f56 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Tue, 24 Mar 2026 23:02:48 +0900
Subject: [PATCH 09/28] move crash handler/ togglable patches out of vanilla
bugfixes.
remove crash handler code and toggleable crash fixes code from vanilla_bugfixes and tidy up the remaining fille
---
patches/ips/vanilla_bugfixes.ips | Bin 2239 -> 399 bytes
patches/rom_map/Bank 83.txt | 3 +-
patches/src/vanilla_bugfixes.asm | 403 ++-----------------------------
3 files changed, 16 insertions(+), 390 deletions(-)
diff --git a/patches/ips/vanilla_bugfixes.ips b/patches/ips/vanilla_bugfixes.ips
index 94d3eecba4b5a800be229e5186862915058ced61..d905effe4997a3ba90e324fbad83dc177418e383 100644
GIT binary patch
delta 73
zcmV-P0Ji_X5sw3qK`|eY00L=z0W1Rm1x$Uy>goY70007zWC1k900ZC*00BaM01eOx
f=;+`E089w1Obo1%xTTW<2sZ_`DF6WhlY0pbEG&Qi@wmb5(%Ps+ZKpK*ox(_mqnvht7=g#sI3iZv@
zx!H5iJ@}@7tzRZ}D?rtlo%m;fvygWPqg@M7`W=8nF27LONuZvVO1Kz-K@!2
zDBKIIuLPn-=j{Pg6L`a;W3X1GIj{M9HvEP|c?QBUM|j*3jyn{97{IUv0_!RWU&k5-
z7f_CvI3^?-6fFCiHB~6mp)hjDbilJv2n(et?*Jew6a|(Ar74_(xR-^8VD!So2z3N%
z*Ps>mdHJ>TAOCq8RqFeKes?e^>n)I`%v0Q})>P{apiy6ADCRuiI~H0Oa8muR4Ip(K
zzPKqkSJ~-2a8cS^D;x}eDN48cPKKs6la8458Rl>zaAg%_4%;VFoEfrp&&`;OY)qnOyIIf6ITX6CTg)#Ah(NDfGoWv6^SG
z6KAjw7dOw~Chl#V!A|_+eeJgMU*exc|MU1yU&Q*&;Q6qdCyQq{X>xItv93>I^^(Z5
zF5iCsTN(_zy${AwH)C2znmZeI^Yv@Jj2EzBkDbJ)hTX+};e}p_dj1A_>e(tGb1G}=?SefvLFrYy9r#e-i_)XY9?AA?EpVJIXT{f-YO?LY!#v}1u-8#ob#)6+
ytgo%53!|?(qis&9eC2H@R|l-=A_We9c9vI`R)QAgmzCW97e5E$T0L^?<$nMt#%6T@
diff --git a/patches/rom_map/Bank 83.txt b/patches/rom_map/Bank 83.txt
index 46816f635..a10d6c739 100644
--- a/patches/rom_map/Bank 83.txt
+++ b/patches/rom_map/Bank 83.txt
@@ -3,8 +3,7 @@ AD66 - AD72: escape_autosave.asm
B000 - B700: [FREE]
B700 - B800: rng_fix.asm
B800 - BA00: Mosaic (Area FX.asm)
-BA00 - BA15: vanilla_bugfixes.asm
-BA15 - BB00: [FREE]
+BA00 - BB00: [FREE]
BB00 - BC40: disableable_etanks.asm
BC40 - E000: [FREE]
E000 - F000: item_dots_disappear.asm
diff --git a/patches/src/vanilla_bugfixes.asm b/patches/src/vanilla_bugfixes.asm
index 24402e719..3a6ec687e 100644
--- a/patches/src/vanilla_bugfixes.asm
+++ b/patches/src/vanilla_bugfixes.asm
@@ -13,33 +13,16 @@ incsrc "constants.asm"
!bank_80_free_space_start = $80D200 ; camera alignment fix.
!bank_80_free_space_end = $80D240
-!bank_82_free_space2_start = $82f810 ; hook unpause (crash dialog boxes)
-!bank_82_free_space2_end = $82f830
-
-!bank_82_free_space_start = $82fbf0 ; pause / reserve bug
-!bank_82_free_space_end = $82fc30
-
-!bank_83_free_space_start = $83BA00 ; reserve icon ui fix.
-!bank_83_free_space_end = $83BA15
+!bank_82_free_space_start = $82C27D ; reserve icon ui fix.
+!bank_82_free_space_end = $82C298
!bank_84_free_space_start = $84EFD7 ; maridia tube fix
!bank_84_free_space_end = $84F000
-!bank_85_free_space2_start = $85AD00 ; customizable crash dialogs (used in patch.rs)
-!bank_85_free_space2_end = $85AE20
-
-!bank_85_free_space_start = $85b000 ; do not change, first jmp used externally (crash dialog boxes)
-!bank_85_free_space_end = $85b5FF
-
!bank_86_free_space_start = $86F4B0 ; powamp projectile fix
!bank_86_free_space_end = $86F4D0
-
-!msg_crash_timer_override = $7EF596 ; temporary variable used for overriding messagebox close delay times during crash box.
-
-
-
; Fix the crash that occurs when you kill an eye door whilst a eye door projectile is alive
; See the comments in the bank logs for $86:B6B9 for details on the bug
; The fix here is setting the X register to the enemy projectile index,
@@ -247,27 +230,6 @@ org $b4bda3
bpl $f8 ; was bne $f8
-; Fix auto-reserve / pause bug
-;
-; This patch will initiate the death sequence if pause hit with auto-reserve enabled
-; on exact frame that leads to crash.
-;
-; (thanks to Benox50 for his initial patch, nn44/aquanight for the light pillar fix)
-
-
-
-org $828b3f
- jsr pause_func : nop ; gamestate 1Ch (unused) handler
-
-org !bank_82_free_space_start
-pause_func:
- jsl fp_pause
- phk
- plb
- rts
-
-assert pc() <= !bank_82_free_space_end
-
; Fix for powamp projectile bug
;
; Rare hardlock can occur if powamp killed using contact damage and errant projectiles are spawned
@@ -305,320 +267,6 @@ assert pc() <= !bank_86_free_space_end
org $80a27a
lda #$a29b
-; Yapping maw shinespark crash
-; Noted by PJBoy: https://patrickjohnston.org/bank/A8#fA68A
-; If bug triggered, show dialog box and then initiate death sequence.
-
-org $90d354
- jsr yapping_maw_crash
-
-!bank_90_free_space_start = $90fc10
-!bank_90_free_space_end = $90fc20
-
-org !bank_90_free_space_start
-yapping_maw_crash:
- cmp #$0005 ; valid table entries are 0-2 * 2
- bcc .skip
- jsl ym_crash
- phk
- plb
- rts
-
-.skip
- jmp ($d37d,x) ; valid entry
-
-assert pc() <= !bank_90_free_space_end
-
-;;; Spring ball menu crash fix by strotlog.
-;;; Fix obscure vanilla bug where: turning off spring ball while bouncing, can crash in $91:EA07,
-;;; or exactly the same way as well in $91:F1FC.
-;;; Adapted for map rando by Stag Shot:
-;;; If bug triggered, show dialog box and then initiate death sequence.
-
-org $91ea07
- jsl spring_ball_crash
-
-org $91f1fc
- jsl spring_ball_crash
-
-org !bank_85_free_space_start
- jmp bug_dialog ; for external calls, do not move
-
-spring_ball_crash:
- lda $0B20 ; morph bounce state
- cmp #$0600 ; bugged?
- bcc .skip
-
- lda !bank_85_free_space2_start
- and #$000F
- beq .default
- cmp #$0002
- beq .fix
-.warn
- lda #$0042
- jsl bug_dialog
-.fix
- lda #$0000
- stz $0b20
- rtl
-.default
- sep #$20
- lda #$42 ; bug ID
- sta $00cf ; set flag to prevent unpause from resetting gamestate to 8
- rep #$30
- jsl bug_dialog
- jsr kill_samus
- lda #$0000
- stz $0B20
- rtl
-.skip
- lda $0B20 ; replaced code
- asl ;
- rtl
-
-
-;;; Implementation of custom dialog boxes
-;;; Requires hooking multiple functions to support extended msg IDs (0x40+)
-;;; and additional lookup tables
-
-bug_dialog: ; A = msg ID
- and #$00ff
- pha
- sep #$20
- lda #$0f ; restore screen brightness to full
- sta $51
- sta !msg_crash_timer_override ; messagebox timer will check if this is 0 (if its non zero load a longer time)
- rep #$30
- jsl $808338 ; wait for NMI
-
- pla ; dlg box parameter
- jsl $858080 ; dlg box
- cmp #$0044
- bne .skipkill ; oob death (dlg 44) is removable via major glitches patch, if its thrown here then the intent is to kill as it isn't toggleable.
- jsr kill_samus
-.skipkill
- rtl
-
-hook_message_box:
- rep #$30
- lda $1c1f
- cmp #$0040 ; custom boxes >= 0x40
- bcs .custom
- jmp $8241 ; original func
-
-.custom
- ldx #(new_message_boxes-$869b) ; ptr for extended lookup table
- jmp $824f
-
-hook_index_lookup:
- lda $1c1f
- cmp #$0040
- bcs .custom
- rts
-
-.custom
- sec
- sbc #$0040
- rts
-
-hook_message_table:
- adc $34 ; replaced code
- tax ;
- lda $1c1f
- cmp #$0040
- bcs .custom
- rts
-
-.custom
- txa
- clc
- adc #(new_message_boxes-$869b) ; adjust ptr for extended table
- tax
- rts
-
-hook_button_lookup:
- lda $1c1f
- cmp #$0040
- bcs .custom
- rts
-
-.custom
- lda #$0001 ; blank button tilemap
- ldy #(reserve_pause_msg-$8426) ; blank button letter
- rts
-
-; custom messages start at 0x41
-new_message_boxes:
- dw $83c5, $825a, reserve_pause_msg ; 0x41
- dw $83c5, $825a, springball_msg ; 0x42
- dw $83c5, $825a, yapping_maw_msg ; 0x43
- dw $83c5, $825a, oob_msg ; 0x44
- dw $83c5, $825a, xmode_msg ; 0x45
- dw $0000, $0000, msg_end
-
-table "tables/dialog_chars.tbl",RTL
-
-reserve_pause_msg:
- dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " PAUSED ON EXACT FRAME ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " AUTO-REFILL STARTED! ", $000e,$000e,$000e
-
-springball_msg:
- dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " UNEQUIPPED SPRING BALL ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " IN NEUTRAL BOUNCE! ", $000e,$000e,$000e
-
-yapping_maw_msg:
- dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " YAPPING MAW SHINESPARK ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " END WITH NO INPUTS HELD! ", $000e,$000e,$000e
-
-oob_msg:
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " SAMUS OUT-OF-BOUNDS! ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
-
-xmode_msg:
- dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " ", $000e,$000e,$000e
- dw $000e,$000e,$000e, " X-MODE TILE COLLISION ", $000e,$000e,$000e
- dw $000e,$000e,$000e, "COLLIDED WITH A SOLID TILE", $000e,$000e,$000e
-
-msg_end:
-
-hook_msgbox_delay:
- pha
- lda !msg_crash_timer_override
- beq .nochange
- ldx #$005a ; (put 1.5 seconds on the clock)
- lda #$00
- sta !msg_crash_timer_override ; clear our special variable so then next msgbox will have whatever timer was set on generation
-.nochange
- pla
- jsr $8136 ; hi-jacked instruction.
- rts
-
-assert pc() <= !bank_85_free_space_end
-
- ;$85:8493 20 36 81 JSR $8136 [$85:8136] ;\
-org $858493 ; before we do things, lets double check we are not in a crash dialog (if so we want a longer timer)
- jsr hook_msgbox_delay
-
-org $858093
- jsr hook_message_box
-
-org $8582e5
- jsr hook_index_lookup
-
-org $8582ee
- jsr hook_message_table
-
-org $85840c
- jsr hook_button_lookup
-
-; hook unpause to prevent resetting gamestate to 8 if crash ID set
-
-org $8293bb
- jmp check_unpause
-
-org !bank_82_free_space2_start
-check_unpause:
- php
- sep #$20
- lda $00cf ; pending crash ID
- stz $00cf
- cmp #$42 ; springball?
- bne .skip
- plp
- jmp $93c1 ; skip changing gamestate
-.skip
- plp
- lda #$0008 ; replaced code
- jmp $93be
-
-assert pc() <= !bank_82_free_space2_end
-
-org !bank_85_free_space2_start
- dw $0000 ; to be set by patch.rs 0 = default (msg+kill), 1 = warn (msg only), 2 = silent (fix only).
- ; xmode / yappingmaw crash / reserve pause / springball
-kill_samus:
- lda #$8000 ; init death sequence (copied from $82db80)
- sta $a78
- lda #$0011
- jsl $90f084
- lda #$0013 ; set gamestate
- sta $998
- rts
-
-fp_pause:
- lda !bank_85_free_space2_start
- and #$00F0
- beq .default
- cmp #$0020
- beq .fix
-.warn
- lda #$0041 ; "fix" leave the screen black warning will relight the screen due to showing the dialog
- jsl bug_dialog
-.fix
- lda #$0008
- sta $0998
- rtl
-.default
- lda #$0041 ; msg ID
- jsl bug_dialog
- jsr kill_samus
- stz $9d6 ; clear reserve health
- rtl
-
-ym_crash:
- lda !bank_85_free_space2_start
- and #$0F00
- beq .default
- cmp #$0200
- beq .fix
-.warn
- lda #$0043 ; bug ID
- jsl bug_dialog
-.fix
- lda #$d3f3
- sta $0a58
- lda #$001e
- sta $0aa2
- sta $0ac0
- sta $0ac2
- lda #$0000
- ldx #$0000
- ldy #$0004
- rtl
-.default
- lda #$0043 ; bug ID
- jsl bug_dialog
- jsr kill_samus
- rtl
-
-xmode:
- lda !bank_85_free_space2_start
- and #$F000
- beq .default
- ;cmp #$0200
- ;beq .fix
-.warn
- ;lda #$0045 ; crash dialog (warning) removed until better solution found, it will re-trigger many times until samus is out of collission so annoying.
- ;jsl bug_dialog
-.fix
- rtl
-.default
- lda #$0045
- jsl bug_dialog
- jsr kill_samus
- rtl
-
-
-assert pc() <= !bank_85_free_space2_end ;ad9d
; Map scrolling bug
; Leftmost edge function @ $829f4a has an off-by-one bug when scanning
@@ -628,21 +276,6 @@ assert pc() <= !bank_85_free_space2_end ;ad9d
org $829f90
adc #$7c
-; X-Mode collision fix - nn_357
-
-org $91816f ; rewrite original input handler for solid tile collision to free up space. -- thanks to StagShot for noticing the rep$30 isn't needed here, it's already initialized by the single caller to this function.
- ; whole fix can now fit in the original function.
- PHP
- JSR $81A9 ; this fixes regular xmode collision by using the correct pose lookup routine for all collisions
- LDA $0A78 ; load frozen time variable
- BEQ $07 ; if time is NOT frozen skip over the next instruction (kill samus)
- LDA #$0045 ; load the msg bug id for X-Mode Collision
- JSL xmode ; collision with solid tile while time is frozen is only possible in x-mode so jump to code that will kill samus rather than hardlock (retain vanilla behaviour)
- PLP
- RTS
-
-assert pc() <= $918181 ; Make sure we don't overwrite the next routine.
-
; (Maridia Tube Fix - written by AmoebaOfDoom)
;patches horizontal PLM updates to DMA tiles even when the PLM is above the screen if part of it is on the screen
@@ -690,25 +323,19 @@ org $84B7EF ; PLM $B8AC (speed booster escape) LDA $09A4 [$7E:09A4]
; vanilla bugfix, ui shows reserve icon when only boots item is equipped.
-;
-
-org !bank_83_free_space_start
-
-BNE .found
-INX
-INX
-CPX #$0006
-BMI .loop
-JML $82AC15
+; this is due to a missing conditional branch instruction.
+org $82AC03
+ jmp $c27d ; !bank_82_free_space_start [bankless form of this]
+ nop #2
+
+org !bank_82_free_space_start
+ bne .found
+ inx
+ inx
+ cpx #$0006
+ jmp $AC08
.found
-JML $82AC0C
+ jmp $AC0C
-.loop
-JML $82AC00
-
-assert pc() <= !bank_83_free_space_end
-
-org $82AC03
- JML !bank_83_free_space_start
- nop #5
\ No newline at end of file
+assert pc() <= !bank_82_free_space_end
From a7c33e91197b2e2e0858d8eb32bc9770c33ac8cf Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 00:10:40 +0900
Subject: [PATCH 10/28] frontend ui fixes
---
.../templates/generate/scripts.html | 58 +++++++++----------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/rust/maprando-web/templates/generate/scripts.html b/rust/maprando-web/templates/generate/scripts.html
index 1f5825acb..fa164ff7f 100644
--- a/rust/maprando-web/templates/generate/scripts.html
+++ b/rust/maprando-web/templates/generate/scripts.html
@@ -796,34 +796,34 @@
document.getElementById("areaAssignmentPresetRandom").checked = false;
}
-function applyCrashFixesPreset(preset) {
- if (preset == "Death") {
- document.getElementById("crashFixesPresetDeath").checked = true;
- document.getElementById("crashFixesSpringballDeath").checked = true;
- document.getElementById("crashFixesYappingmawDeath").checked = true;
- document.getElementById("crashFixesAutoreserveDeath").checked = true;
- document.getElementById("crashFixesXmodeDeath").checked = true;
- } else if (preset == "Warn") {
- document.getElementById("crashFixesPresetWarn").checked = true;
- document.getElementById("crashFixesSpringballWarn").checked = true;
- document.getElementById("crashFixesYappingmawWarn").checked = true;
- document.getElementById("crashFixesAutoreserveWarn").checked = true;
- document.getElementById("crashFixesXmodeWarn").checked = true;
- } else if (preset == "Silent") {
- document.getElementById("crashFixesPresetSilent").checked = true;
- document.getElementById("crashFixesSpringballSilent").checked = true;
- document.getElementById("crashFixesYappingmawSilent").checked = true;
- document.getElementById("crashFixesAutoreserveSilent").checked = true;
- document.getElementById("crashFixesXmodeSilent").checked = true;
- } else if (preset == "Crash") {
- document.getElementById("crashFixesPresetCrash").checked = true;
- document.getElementById("crashFixesSpringballCrash").checked = true;
- document.getElementById("crashFixesYappingmawCrash").checked = true;
- document.getElementById("crashFixesAutoreserveCrash").checked = true;
- document.getElementById("crashFixesXmodeCrash").checked = true;
- }
-
-}
+ function applyCrashFixesPreset(preset) {
+ if (preset == "Crash") {
+ document.getElementById("crashFixesPresetCrash").checked = true;
+ document.getElementById("crashFixesSpringballCrash").checked = true;
+ document.getElementById("crashFixesYappingmawCrash").checked = true;
+ document.getElementById("crashFixesAutoreserveCrash").checked = true;
+ document.getElementById("crashFixesXmodeCrash").checked = true;
+ } else if (preset == "Death") {
+ document.getElementById("crashFixesPresetDeath").checked = true;
+ document.getElementById("crashFixesSpringballDeath").checked = true;
+ document.getElementById("crashFixesYappingmawDeath").checked = true;
+ document.getElementById("crashFixesAutoreserveDeath").checked = true;
+ document.getElementById("crashFixesXmodeDeath").checked = true;
+ } else if (preset == "Warn") {
+ document.getElementById("crashFixesPresetWarn").checked = true;
+ document.getElementById("crashFixesSpringballWarn").checked = true;
+ document.getElementById("crashFixesYappingmawWarn").checked = true;
+ document.getElementById("crashFixesAutoreserveWarn").checked = true;
+ document.getElementById("crashFixesXmodeWarn").checked = true;
+ } else if (preset == "Silent") {
+ document.getElementById("crashFixesPresetSilent").checked = true;
+ document.getElementById("crashFixesSpringballSilent").checked = true;
+ document.getElementById("crashFixesYappingmawSilent").checked = true;
+ document.getElementById("crashFixesAutoreserveSilent").checked = true;
+ document.getElementById("crashFixesXmodeSilent").checked = true;
+ }
+
+ }
function applyCrashFixes(preset) {
applyRadioValue("crashFixesSpringball", preset.spring_ball);
@@ -1262,7 +1262,7 @@
!document.getElementById("areaAssignmentPresetStandard").checked ||
document.getElementById("doorLocksSizeSmall").checked ||
document.getElementById("mapStationRevealPartial").checked ||
- !document.getElementById("crashFixPresetDefault").checked ||
+ !document.getElementById("crashFixPresetDeath").checked ||
document.getElementById("ultraLowQoLYes").checked ||
document.getElementById("raceModeYes").checked)
{
From f719b1b1e7ecfab59c1072e2cb2083754d09707e Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 00:43:00 +0900
Subject: [PATCH 11/28] fix typo in scripts.
---
rust/maprando-web/templates/generate/scripts.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/maprando-web/templates/generate/scripts.html b/rust/maprando-web/templates/generate/scripts.html
index fa164ff7f..fd1f52d1b 100644
--- a/rust/maprando-web/templates/generate/scripts.html
+++ b/rust/maprando-web/templates/generate/scripts.html
@@ -1262,7 +1262,7 @@
!document.getElementById("areaAssignmentPresetStandard").checked ||
document.getElementById("doorLocksSizeSmall").checked ||
document.getElementById("mapStationRevealPartial").checked ||
- !document.getElementById("crashFixPresetDeath").checked ||
+ !document.getElementById("crashFixesPresetDeath").checked ||
document.getElementById("ultraLowQoLYes").checked ||
document.getElementById("raceModeYes").checked)
{
From e30f5c600b609ac97552130f172d6f4af61a0530 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 00:46:08 +0900
Subject: [PATCH 12/28] more errors
---
rust/maprando-web/templates/generate/crash_fixes.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/maprando-web/templates/generate/crash_fixes.html b/rust/maprando-web/templates/generate/crash_fixes.html
index f704c362e..5065188b0 100644
--- a/rust/maprando-web/templates/generate/crash_fixes.html
+++ b/rust/maprando-web/templates/generate/crash_fixes.html
@@ -75,7 +75,7 @@
Crash Fixes
-
+
From b45aa609d33d23cd1118e8774ef6039b945b61d2 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 02:25:06 +0900
Subject: [PATCH 13/28] add base crash handler and subpatch for xmode
---
patches/ips/crash_handle_base.ips | Bin 0 -> 1520 bytes
patches/ips/crash_handle_xmode.ips | Bin 0 -> 57 bytes
patches/rom_map/Bank 82.txt | 4 +-
patches/rom_map/Bank 85.txt | 6 +-
patches/rom_map/Bank 91.txt | 4 +-
patches/src/crash_handle_base.asm | 179 +++++++++++++++++++++++++++++
patches/src/crash_handle_xmode.asm | 54 +++++++++
7 files changed, 241 insertions(+), 6 deletions(-)
create mode 100644 patches/ips/crash_handle_base.ips
create mode 100644 patches/ips/crash_handle_xmode.ips
create mode 100644 patches/src/crash_handle_base.asm
create mode 100644 patches/src/crash_handle_xmode.asm
diff --git a/patches/ips/crash_handle_base.ips b/patches/ips/crash_handle_base.ips
new file mode 100644
index 0000000000000000000000000000000000000000..ce0ff77788ae7583f6302be37540bfc30dc2d1d7
GIT binary patch
literal 1520
zcmd5+&r2Io5Pq)pkW#vcmmUfY=_xP=4@GiR)RKc>3xz^XdnvW#A1FPPZ8R4_6bhxs
zklO~NtJS-%n`G1c-WA(RkO)0^5ig$go40O4+}K;uyfAOxy!mG4n>Wd;nK!dZMgJ;B
zqt8tx^B4OR_A~5rrn2$~`&;aM|K3zKg;4M{#n?{#QY25JhMIl7{_WzkH5p4R>q{|R
z&sOF{>p?8Hn@wAjo6nwBriD4WFq6r5;|q(KYr}|^IGq;SyBCF?VfD#}ZoHuN&c=wy
zeIhOS1JTbT`IImOCt8Y5ebI``sZ7q4a!bl>DSt}2Bk;-XJ{G)G8eApXqZ-+;TGWQ_
zQkjl#`1r`-_`ztyPhor$nGTW5rUT?^P#w~y->_QbK`+W${JK;M;~DJ%*MXl6Z-=T_
zDk6G_(T3#60go`gU*}!$KLlT!THxSN6U*;{3#|=Hol0P`4<0_JVf@)kl~K44{BF#C
zA06S?79s}RndyDw{|EIl?|^F!=Ofg~v&S|Y8c#@@e!+UPej6SAk@L?t(W6<8RM3?B;*s?_&N{
z{L(w_egqv@#PgP#df^_
literal 0
HcmV?d00001
diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips
new file mode 100644
index 0000000000000000000000000000000000000000..a2dfd67bd170b1b2e22e81032e8d9cebaf65ea6c
GIT binary patch
literal 57
zcmWG=3~~10Xv}9204IeM7ytkO
literal 0
HcmV?d00001
diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt
index 017327f7e..5b02161eb 100644
--- a/patches/rom_map/Bank 82.txt
+++ b/patches/rom_map/Bank 82.txt
@@ -1,6 +1,6 @@
C262 - C27D: disableable_etanks.asm
-C27D - C298:
-C2A6 - C2B7:
+C27D - C298: vanilla_bugfixes.asm
+C2A6 - C2B7: split_speed.asm
C362 - C369:
C37E - C385:
C3B6 - C3BD: map_area.asm
diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt
index 51a24aaf6..c22042747 100644
--- a/patches/rom_map/Bank 85.txt
+++ b/patches/rom_map/Bank 85.txt
@@ -16,7 +16,9 @@ $AA00 - $AAA0: pause_menu_objectives.asm
$AAA0 - $AB00: [FREE]
$AB00 - $ACA0: map_area.asm
$ACA0 - $AD00: load_flash_suit
-$AD00 - $AE20: vanilla_bugfixes.asm
+$AD00 - $AD02: crash_handle_base.asm (crash toggles referenced by patch.rs )
+$AD02 - $AE20: [FREE]
$AE20 - $B000: reserve_backward_fill.asm
-$B000 - $B600: vanilla_bugfixes.asm
+$B000 - $B5B3: crash_handle_base.asm
+$B5B3 - $B600: [FREE]
$B600 - $BA00: map_area.asm
\ No newline at end of file
diff --git a/patches/rom_map/Bank 91.txt b/patches/rom_map/Bank 91.txt
index f393e4660..7641f5073 100644
--- a/patches/rom_map/Bank 91.txt
+++ b/patches/rom_map/Bank 91.txt
@@ -1,4 +1,4 @@
+95bc - 965a: {unused vanilla code - demo instructions} - crash_handle_xmode.asm
F7F4 - F88C: {unused vanilla code} - split_speed.asm
FC42 - FC66: {unsued vanilla code} - split_speed.asm
-
-FFEE - FFFE: mosaic\fake lava.asm
+FFEE - FFFE: mosaic\fake lava.asm
\ No newline at end of file
diff --git a/patches/src/crash_handle_base.asm b/patches/src/crash_handle_base.asm
new file mode 100644
index 000000000..34bcd96f7
--- /dev/null
+++ b/patches/src/crash_handle_base.asm
@@ -0,0 +1,179 @@
+;;; crash handler (displays a custom message box explaining the crash)
+;;; StagShot, toggle modifications - nn_357
+;;;
+;;; Implementation of custom dialog boxes
+;;; Requires hooking multiple functions to support extended msg IDs (0x40+)
+;;; and additional lookup tables
+;;;
+;;; This code is used by other patches that handle hardlocks caused by the unequip springball, frame perfect pause on autoreserve trigger, yappingmaw shinespark
+;;; X-mode solid tile collision.
+
+arch snes.cpu
+lorom
+
+!bank_85_free_space_start = $85b000 ; be careful if moving this, the subpatches that use this code assume this location (xmode/oob/yappingmaw/autoreserve/springball)
+!bank_85_free_space_end = $85b5a0
+
+!bank_85_free_space2_start = $85b5a0 ; kill samus routine, location called by sub patches, as above, be careful if ever moving this code and update subpatches.
+!bank_85_free_space2_end = $85b5b4
+
+!msg_crash_timer_override = $7EF596 ; temporary variable used for overriding messagebox close delay times during crash box.
+!crash_toggles = $85ad00 ; this location is looked at by the subpatches to decide if to call the handler etc.
+
+org !crash_toggles
+ dw $0000 ; overwritten by patch.rs if any values are changed, subpatches reference this. default is gameover / death (pre-toggleable setting)
+
+;;; hooks into vanilla code
+
+org $858493 ; override messagebox delay if crash dialog
+ jsr hook_msgbox_delay
+
+org $858093
+ jsr hook_message_box
+
+org $8582e5
+ jsr hook_index_lookup
+
+org $8582ee
+ jsr hook_message_table
+
+org $85840c ; hook unpause to prevent resetting gamestate to 8 if crash ID set
+ jsr hook_button_lookup
+
+;;; custom code
+
+org !bank_85_free_space_start
+bug_dialog: ; A = msg ID
+ and #$00ff
+ pha
+ sep #$20
+ lda #$0f ; restore screen brightness to full
+ sta $51
+ sta !msg_crash_timer_override ; messagebox timer will check if this is 0 (if its non zero load a longer time)
+ rep #$30
+ jsl $808338 ; wait for NMI
+
+ pla ; dlg box parameter
+ jsl $858080 ; dlg box
+ cmp #$0044
+ bne .skipkill ; oob death (dlg 44) is removable via major glitches patch, if its thrown then the intent is to kill as it isn't toggleable.
+ jsl kill_samus
+.skipkill
+ rtl
+
+hook_message_box:
+ rep #$30
+ lda $1c1f
+ cmp #$0040 ; custom boxes >= 0x40
+ bcs .custom
+ jmp $8241 ; original func
+
+.custom
+ ldx #(new_message_boxes-$869b) ; ptr for extended lookup table
+ jmp $824f
+
+hook_index_lookup:
+ lda $1c1f
+ cmp #$0040
+ bcs .custom
+ rts
+
+.custom
+ sec
+ sbc #$0040
+ rts
+
+hook_message_table:
+ adc $34 ; replaced code
+ tax ;
+ lda $1c1f
+ cmp #$0040
+ bcs .custom
+ rts
+
+.custom
+ txa
+ clc
+ adc #(new_message_boxes-$869b) ; adjust ptr for extended table
+ tax
+ rts
+
+hook_button_lookup:
+ lda $1c1f
+ cmp #$0040
+ bcs .custom
+ rts
+
+.custom
+ lda #$0001 ; blank button tilemap
+ ldy #(reserve_pause_msg-$8426) ; blank button letter
+ rts
+
+hook_msgbox_delay:
+ pha
+ lda !msg_crash_timer_override
+ beq .nochange
+ ldx #$005a ; (put 1.5 seconds on the clock)
+ lda #$00
+ sta !msg_crash_timer_override ; clear our special variable so then next msgbox will have whatever timer was set on generation
+.nochange
+ pla
+ jsr $8136 ; hi-jacked instruction.
+ rts
+
+; custom messages start at 0x41
+new_message_boxes:
+ dw $83c5, $825a, reserve_pause_msg ; 0x41
+ dw $83c5, $825a, springball_msg ; 0x42
+ dw $83c5, $825a, yapping_maw_msg ; 0x43
+ dw $83c5, $825a, oob_msg ; 0x44
+ dw $83c5, $825a, xmode_msg ; 0x45
+ dw $0000, $0000, msg_end
+
+table "tables/dialog_chars.tbl",RTL
+
+reserve_pause_msg:
+ dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " PAUSED ON EXACT FRAME ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " AUTO-REFILL STARTED! ", $000e,$000e,$000e
+
+springball_msg:
+ dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " UNEQUIPPED SPRING BALL ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " IN NEUTRAL BOUNCE! ", $000e,$000e,$000e
+
+yapping_maw_msg:
+ dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " YAPPING MAW SHINESPARK ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " END WITH NO INPUTS HELD! ", $000e,$000e,$000e
+
+oob_msg:
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " SAMUS OUT-OF-BOUNDS! ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+
+xmode_msg:
+ dw $000e,$000e,$000e, " GAME CRASH! ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, " X-MODE TILE COLLISION ", $000e,$000e,$000e
+ dw $000e,$000e,$000e, "COLLIDED WITH A SOLID TILE", $000e,$000e,$000e
+
+msg_end:
+
+assert pc() <= !bank_85_free_space_end
+
+org !bank_85_free_space2_start
+kill_samus:
+ lda #$8000 ; init death sequence (copied from $82db80)
+ sta $a78
+ lda #$0011
+ jsl $90f084
+ lda #$0013 ; set gamestate
+ sta $998
+ rtl
+
+assert pc() <= !bank_85_free_space2_end
\ No newline at end of file
diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm
new file mode 100644
index 000000000..b33928869
--- /dev/null
+++ b/patches/src/crash_handle_xmode.asm
@@ -0,0 +1,54 @@
+;;; X-Mode collision fix - nn_357
+;;; rewrite original input handler for solid tile collision to free up space.
+;;; thanks to StagShot for noticing the rep$30 isn't needed here, it's already initialized by the single caller to this function.
+;;; whole fix can now fit in the original function.
+
+
+;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
+
+!crash_toggles = $85AD00
+!kill_samus = $85b5a0
+!bug_dialog = $85b000
+
+!bank_91_free_space_start = $9195bc
+!bank_91_free_space_end = $91965a
+
+arch snes.cpu
+lorom
+
+org $91816f
+xmodefix:
+ php
+ jsr $81a9 ; this fixes regular xmode collision by using the correct pose lookup routine for all collisions
+ lda $0a78 ; load frozen time variable
+ beq .skip ; if time is NOT frozen skip over the next instruction (jump to xmode crash handler.)
+ lda #$0045 ; load the msg bug id for X-Mode Collision
+ jsr xmode ; now in same bank so uses a jsr.
+.skip
+ plp
+ rts
+
+assert pc() <= $918181 ; Make sure we don't overwrite the next routine.
+
+;;;
+;;; Currently only using upto {9195D2} {approx 130bytes free, reserved for more future xmode warn fix.}
+
+org !bank_91_free_space_start
+xmode:
+ lda !crash_toggles
+ and #$F000
+ beq .default
+ ;cmp #$0200
+ ;beq .fix
+.warn
+ ;lda #$0045 ; crash dialog (warning) removed until better solution found, it will re-trigger many times until samus is out of collission so annoying.
+ ;jsl !bug_dialog ; there is space available here for additional code.
+.fix
+ rtl
+.default
+ lda #$0045
+ jsl !bug_dialog
+ jsl !kill_samus
+ rts
+
+assert pc() <= !bank_91_free_space_end
\ No newline at end of file
From 6907dc4e71bd8e83f85b9314860d96989ddc1b94 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 02:36:04 +0900
Subject: [PATCH 14/28] fix xmode handler bug
wrong return instruction.
---
patches/ips/crash_handle_xmode.ips | Bin 57 -> 57 bytes
patches/src/crash_handle_xmode.asm | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips
index a2dfd67bd170b1b2e22e81032e8d9cebaf65ea6c..d95e9b4104fbf0a16ad82432796caea9706b1867 100644
GIT binary patch
delta 21
ccmcDtoS-R?u+o)5iD5&l(t@q639kNb07xVU7XSbN
delta 21
ccmcDtoS-R?z0#FIiD5&l(t@q639kNb07%paA^-pY
diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm
index b33928869..af5eee3e9 100644
--- a/patches/src/crash_handle_xmode.asm
+++ b/patches/src/crash_handle_xmode.asm
@@ -44,7 +44,7 @@ xmode:
;lda #$0045 ; crash dialog (warning) removed until better solution found, it will re-trigger many times until samus is out of collission so annoying.
;jsl !bug_dialog ; there is space available here for additional code.
.fix
- rtl
+ rts
.default
lda #$0045
jsl !bug_dialog
From 509d1151869562f5abfac0a1689ccdebc3f54a0a Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 03:10:21 +0900
Subject: [PATCH 15/28] add crash handler for yapping maw shinespark
---
patches/ips/crash_handle_yapping.ips | Bin 0 -> 100 bytes
patches/rom_map/Bank 85.txt | 5 +-
patches/src/crash_handle_xmode.asm | 9 ++--
patches/src/crash_handle_yapping.asm | 67 +++++++++++++++++++++++++++
4 files changed, 74 insertions(+), 7 deletions(-)
create mode 100644 patches/ips/crash_handle_yapping.ips
create mode 100644 patches/src/crash_handle_yapping.asm
diff --git a/patches/ips/crash_handle_yapping.ips b/patches/ips/crash_handle_yapping.ips
new file mode 100644
index 0000000000000000000000000000000000000000..6d5b98fd113eae192b2dbeca38e55b6d4e631e06
GIT binary patch
literal 100
zcmWG=3~}~g+Pa0oc0I$|R!s)}4;m*Km_D$tbY@Uu*wDK2^X1+Mu9b2Oy^FYd4{-G!
z;#$eTu!w
Date: Wed, 25 Mar 2026 03:39:39 +0900
Subject: [PATCH 16/28] add springball crash handler
---
patches/ips/crash_handle_springball.ips | Bin 0 -> 133 bytes
patches/rom_map/Bank 82.txt | 2 +-
patches/rom_map/Bank 85.txt | 3 +-
patches/rom_map/Bank 90.txt | 2 +-
patches/src/crash_handle_springball.asm | 86 ++++++++++++++++++++++++
5 files changed, 90 insertions(+), 3 deletions(-)
create mode 100644 patches/ips/crash_handle_springball.ips
create mode 100644 patches/src/crash_handle_springball.asm
diff --git a/patches/ips/crash_handle_springball.ips b/patches/ips/crash_handle_springball.ips
new file mode 100644
index 0000000000000000000000000000000000000000..f63002a17ba4d1dd7041fcab1f60b53ea869fcf1
GIT binary patch
literal 133
zcmWG=3~}~g6yD9i>?81lu|j}Bg5#0G+Vc!^&NG~Jy1=60b8xc8N)85}eUq8ivM@NV
zRp36!z&62TJ;T~oO@4+C!Y7#+KCrKJVo+k((7KX=VU7ZK_9KOrPQB+D4jF*Nloo7-
bi39a=Wplh@XJAocS=-9-@ehRI>hA^sXQwOH
literal 0
HcmV?d00001
diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt
index 5b02161eb..ee70d90c3 100644
--- a/patches/rom_map/Bank 82.txt
+++ b/patches/rom_map/Bank 82.txt
@@ -8,7 +8,7 @@ C3C4 - C3D9: map_area.asm
C411 - C42D: map_area.asm
C42D - C465: split_speed.asm
F70F - F810: map_area.asm
-F810 - F830: vanilla_bugfixes.asm
+F810 - F830: crash_handle_springball.asm
F830 - F9E0: disable_etanks.asm
F9E0 - FA00: reserve_backward_fill.asm
FA00 - FA80: alternate_door_colors.asm
diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt
index 646eaa427..e0f4828d1 100644
--- a/patches/rom_map/Bank 85.txt
+++ b/patches/rom_map/Bank 85.txt
@@ -17,7 +17,8 @@ $AAA0 - $AB00: [FREE]
$AB00 - $ACA0: map_area.asm
$ACA0 - $AD00: load_flash_suit
$AD00 - $AD02: crash_handle_base.asm (crash toggles referenced by patch.rs )
-$AD02 - $AE20: [FREE]
+$AD02 - $AD45: crash_handle_springball.asm
+$AD45 - $AE20: [FREE]
$AE20 - $B000: reserve_backward_fill.asm
$B000 - $B5B4: crash_handle_base.asm
$B5B4 - $B5F1: crash_handle_yapping.asm
diff --git a/patches/rom_map/Bank 90.txt b/patches/rom_map/Bank 90.txt
index f47e31b18..3901f8d28 100644
--- a/patches/rom_map/Bank 90.txt
+++ b/patches/rom_map/Bank 90.txt
@@ -7,6 +7,6 @@ $F900 - $F910: escape_timer.asm
$F980 - $FA00: respin.asm
$FA00 - $FC00: map_progress_maintain.asm (list of cross-area tiles to reveal)
$FC00 - $FC10: Fake Lava.asm
-$FC10 - $FC20: vanilla_bugfixes.asm
+$FC10 - $FC20: crash_handle_yapping.asm
$FC20 - $FC40: remove_spikesuit.asm
$FC40 - $FD00: split_speed.asm
\ No newline at end of file
diff --git a/patches/src/crash_handle_springball.asm b/patches/src/crash_handle_springball.asm
new file mode 100644
index 000000000..63fef93a4
--- /dev/null
+++ b/patches/src/crash_handle_springball.asm
@@ -0,0 +1,86 @@
+;;; Spring ball menu crash fix by strotlog.
+;;; Fix obscure vanilla bug where: turning off spring ball while bouncing, can crash in $91:EA07,
+;;; or exactly the same way as well in $91:F1FC.
+;;; Adapted for map rando by Stag Shot:
+;;; toggle-modifications nn_357.
+
+arch snes.cpu
+lorom
+
+;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
+
+!crash_toggles = $85AD00
+!kill_samus = $85b5a0
+!bug_dialog = $85b000
+
+!bank_82_free_space2_start = $82f810 ; hook unpause (springball crash)
+!bank_82_free_space2_end = $82f830
+
+!bank_85_free_space_start = $85ad04
+!bank_85_free_space_end = $85ad45
+
+;;; vanilla hooks
+
+org $8293bb
+ jmp check_unpause
+
+org $91ea07
+ jsl spring_ball_crash
+
+org $91f1fc
+ jsl spring_ball_crash
+
+;;; custom code
+
+
+org !bank_82_free_space2_start
+check_unpause:
+ php
+ sep #$20
+ lda $00cf ; pending crash ID
+ stz $00cf
+ cmp #$42 ; springball?
+ bne .skip
+ plp
+ jmp $93c1 ; skip changing gamestate
+.skip
+ plp
+ lda #$0008 ; replaced code
+ jmp $93be
+
+assert pc() <= !bank_82_free_space2_end
+
+
+org !bank_85_free_space_start
+spring_ball_crash:
+ lda $0B20 ; morph bounce state
+ cmp #$0600 ; bugged?
+ bcc .skip
+ lda !crash_toggles
+ and #$000F
+ beq .default
+ cmp #$0002
+ beq .fix
+.warn
+ lda #$0042
+ jsl !bug_dialog
+.fix
+ lda #$0000
+ stz $0b20
+ rtl
+.default
+ sep #$20
+ lda #$42 ; bug ID
+ sta $00cf ; set flag to prevent unpause from resetting gamestate to 8
+ rep #$30
+ jsl !bug_dialog
+ jsl !kill_samus
+ lda #$0000
+ stz $0B20
+ rtl
+.skip
+ lda $0B20 ; replaced code
+ asl ;
+ rtl
+
+assert pc() <= !bank_85_free_space_end
From 77343644313696f23cde803bd0c10c921e58c88c Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 03:52:58 +0900
Subject: [PATCH 17/28] update bug in autoreserves patch
---
patches/ips/crash_handle_autoreserves.ips | Bin 0 -> 65 bytes
patches/rom_map/Bank 82.txt | 2 +-
patches/src/crash_handle_autoreserves.asm | 49 ++++++++++++++++++++++
3 files changed, 50 insertions(+), 1 deletion(-)
create mode 100644 patches/ips/crash_handle_autoreserves.ips
create mode 100644 patches/src/crash_handle_autoreserves.asm
diff --git a/patches/ips/crash_handle_autoreserves.ips b/patches/ips/crash_handle_autoreserves.ips
new file mode 100644
index 0000000000000000000000000000000000000000..17f3ee08ef29319f321c525c8f56a917f3b89091
GIT binary patch
literal 65
zcmWG=3~}~gYg;uxFnka`slf1oeWfFV62pepl^hJcGdL5V
RJf#I&TjyNkOmOvg0|50f7n=Y8
literal 0
HcmV?d00001
diff --git a/patches/rom_map/Bank 82.txt b/patches/rom_map/Bank 82.txt
index ee70d90c3..861abc1cf 100644
--- a/patches/rom_map/Bank 82.txt
+++ b/patches/rom_map/Bank 82.txt
@@ -15,7 +15,7 @@ FA00 - FA80: alternate_door_colors.asm
FA80 - FA90: fix_water_fx_bug.asm
FA90 - FBB0: seed_hash_display.asm
FBB0 - FBF0: fix_kraid_hud.asm
-FBF0 - FC30: vanilla_bugfixes.asm
+FBF0 - FC30: crash_handle_autoreserve.asm
FC30 - FC50: fix_transition_bad_tiles.asm
FC50 - FCD0: map_area.asm
FCD0 - FD00: escape.asm
diff --git a/patches/src/crash_handle_autoreserves.asm b/patches/src/crash_handle_autoreserves.asm
new file mode 100644
index 000000000..dd34b9cf8
--- /dev/null
+++ b/patches/src/crash_handle_autoreserves.asm
@@ -0,0 +1,49 @@
+;;; Fix auto-reserve / pause bug
+;;;
+;;; This patch will initiate the death sequence if pause hit with auto-reserve enabled
+;;; on exact frame that leads to crash.
+;;;
+;;; (thanks to Benox50 for his initial patch, nn44/aquanight for the light pillar fix)
+;;;
+
+
+;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
+
+!crash_toggles = $85AD00
+!kill_samus = $85b5a0
+!bug_dialog = $85b000
+
+!bank_82_free_space_start = $82fbf0 ; pause / reserve bug
+!bank_82_free_space_end = $82fc30
+
+
+;;; vanilla hooks
+
+org $828b3f
+ jsr pause_func : nop ; gamestate 1Ch (unused) handler
+
+
+;;; custom code
+
+org !bank_82_free_space_start
+pause_func:
+ lda !crash_toggles
+ and #$00F0
+ beq .default
+ cmp #$0020
+ beq .fix
+.warn
+ lda #$0041 ; "fix" leaves the screen black like any pause close to fadeout, warning will relight the screen due to showing the dialog
+ jsl !bug_dialog
+.fix
+ lda #$0008
+ sta $0998
+ rts
+.default
+ lda #$0041 ; msg ID
+ jsl !bug_dialog
+ jsl !kill_samus
+ stz $9d6 ; clear reserve health
+ rts
+
+assert pc() <= !bank_82_free_space_end
From 7703120fa2875b06a5faefba02f39e8e3d18dd80 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 04:54:42 +0900
Subject: [PATCH 18/28] updater patcher to apply individual crash patches
---
rust/maprando/src/patch.rs | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs
index 4e1bb7582..2bba2b35d 100644
--- a/rust/maprando/src/patch.rs
+++ b/rust/maprando/src/patch.rs
@@ -18,9 +18,9 @@ use crate::{
patch::map_tiles::diagonal_flip_tile,
randomize::{LockedDoor, Randomization, get_starting_items},
settings::{
- AreaAssignmentPreset, CrashFixes, DisableETankSetting, ETankRefill, Fanfares, ItemCount,
- MotherBrainFight, Objective, ObjectiveScreen, RandomizerSettings, SaveAnimals,
- SpeedBooster, StartLocationMode, WallJump,
+ AreaAssignmentPreset, CrashFixes, CrashFixesPreset, DisableETankSetting, ETankRefill,
+ Fanfares, FixMode, ItemCount, MotherBrainFight, Objective, ObjectiveScreen,
+ RandomizerSettings, SaveAnimals, SpeedBooster, StartLocationMode, WallJump,
},
};
use anyhow::{Context, Result, bail, ensure};
@@ -547,6 +547,28 @@ impl Patcher<'_> {
self.rom.write_u16(snes2pc(0xdfff07), 40)?;
}
+ if self.settings.other_settings.crash_fixes.preset != Some(CrashFixesPreset::Crash) {
+ patches.push("crash_handle_base");
+ }
+
+ let fixes = &self.settings.other_settings.crash_fixes;
+
+ if fixes.spring_ball != FixMode::Crash {
+ patches.push("crash_handle_springball");
+ }
+
+ if fixes.yapping_maw != FixMode::Crash {
+ patches.push("crash_handle_yapping");
+ }
+
+ if fixes.auto_reserve != FixMode::Crash {
+ patches.push("crash_handle_autoreserves");
+ }
+
+ if fixes.x_mode != FixMode::Crash {
+ patches.push("crash_handle_xmode");
+ }
+
match self.settings.other_settings.wall_jump {
WallJump::Vanilla => {}
WallJump::Collectible => {
@@ -979,8 +1001,10 @@ impl Patcher<'_> {
let crash_handler = 0x85AD00;
let word = crash_fixes.to_word();
+ if word == 0x3333 {
+ return Ok(()); // no need to patch the rom if the crash handler isn't applied at all
+ }
let bytes = word.to_le_bytes();
-
self.rom.write_n(snes2pc(crash_handler), &bytes)?;
Ok(())
From d70b635d544a26be1e1d3fbd5c8e118457069596 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 05:01:47 +0900
Subject: [PATCH 19/28] formatting error in settings.rs
---
rust/maprando/src/settings.rs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs
index 7bf7f9f92..9b45286aa 100644
--- a/rust/maprando/src/settings.rs
+++ b/rust/maprando/src/settings.rs
@@ -568,7 +568,7 @@ pub enum FixMode {
Silent = 2,
Crash = 3,
}
-#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CrashFixes {
pub preset: Option,
pub spring_ball: FixMode,
@@ -596,10 +596,10 @@ impl CrashFixes {
}
pub fn to_word(&self) -> u16 {
- ((self.x_mode as u16) << 12) |
- ((self.yapping_maw as u16) << 8) |
- ((self.auto_reserve as u16) << 4) |
- (self.spring_ball as u16)
+ ((self.x_mode as u16) << 12)
+ | ((self.yapping_maw as u16) << 8)
+ | ((self.auto_reserve as u16) << 4)
+ | (self.spring_ball as u16)
}
}
From 182104a2c23adaa5673db876df86972c67030a67 Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 05:39:25 +0900
Subject: [PATCH 20/28] update upgrade settings
add upgrade code for new toggles
---
rust/maprando/src/settings.rs | 47 ++++++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs
index 9b45286aa..116e91de3 100644
--- a/rust/maprando/src/settings.rs
+++ b/rust/maprando/src/settings.rs
@@ -1059,22 +1059,39 @@ fn upgrade_other_settings(settings: &mut serde_json::Value) -> Result<()> {
.context("missing other_settings")?
.as_object_mut()
.context("expected other_settings to be object")?;
-
- let area_assignment = other_settings
- .get_mut("area_assignment")
- .context("missing area_assignment")?;
-
- if area_assignment.is_string() {
- let preset_str = area_assignment.as_str().unwrap();
- let preset = match preset_str {
- "Standard" => AreaAssignmentPreset::Standard,
- "Ordered" => AreaAssignmentPreset::Size,
- "Random" => AreaAssignmentPreset::Random,
- _ => bail!("Unrecognized area assignment preset: {}", preset_str),
- };
- *area_assignment = serde_json::to_value(AreaAssignment::from_preset(preset))?;
+ {
+ let area_assignment = other_settings
+ .get_mut("area_assignment")
+ .context("missing area_assignment")?;
+
+ if area_assignment.is_string() {
+ let preset_str = area_assignment.as_str().unwrap();
+ let preset = match preset_str {
+ "Standard" => AreaAssignmentPreset::Standard,
+ "Ordered" => AreaAssignmentPreset::Size,
+ "Random" => AreaAssignmentPreset::Random,
+ _ => bail!("Unrecognized area assignment preset: {}", preset_str),
+ };
+ *area_assignment = serde_json::to_value(AreaAssignment::from_preset(preset))?;
+ }
+ }
+ {
+ let crash_fixes = other_settings
+ .get_mut("crash_fixes")
+ .context("missing crash fixes")?;
+
+ if crash_fixes.is_string() {
+ let preset_str = crash_fixes.as_str().unwrap();
+ let preset = match preset_str {
+ "Crash" => CrashFixesPreset::Crash,
+ "Death" => CrashFixesPreset::Death,
+ "Warn" => CrashFixesPreset::Warn,
+ "Silent" => CrashFixesPreset::Silent,
+ _ => bail!("Unrecognized area assignment preset: {}", preset_str),
+ };
+ *crash_fixes = serde_json::to_value(CrashFixes::from_preset(preset))?;
+ }
}
-
if other_settings.get("disable_spikesuit").is_none()
|| other_settings["disable_spikesuit"].as_bool().is_none()
{
From 8d2dad0bcfb3a0d27f73cf8e7ff41ce9e7dfdeea Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 18:39:57 +0900
Subject: [PATCH 21/28] update patches to use a common springboard.
this will make it easier if the crash-handler ever needs to be moved.
---
patches/ips/crash_handle_autoreserves.ips | Bin 65 -> 65 bytes
patches/ips/crash_handle_base.ips | Bin 1520 -> 1530 bytes
patches/ips/crash_handle_springball.ips | Bin 133 -> 133 bytes
patches/ips/crash_handle_xmode.ips | Bin 57 -> 57 bytes
patches/ips/crash_handle_yapping.ips | Bin 100 -> 100 bytes
patches/rom_map/Bank 80.txt | 2 +-
patches/rom_map/Bank 85.txt | 5 ++--
patches/src/constants.asm | 7 ++++++
patches/src/crash_handle_autoreserves.asm | 8 ++----
patches/src/crash_handle_base.asm | 29 ++++++++++++++--------
patches/src/crash_handle_springball.asm | 15 +++++------
patches/src/crash_handle_xmode.asm | 5 ++--
patches/src/crash_handle_yapping.asm | 9 +++----
rust/maprando/src/patch.rs | 2 +-
rust/maprando/src/settings.rs | 2 +-
15 files changed, 44 insertions(+), 40 deletions(-)
diff --git a/patches/ips/crash_handle_autoreserves.ips b/patches/ips/crash_handle_autoreserves.ips
index 17f3ee08ef29319f321c525c8f56a917f3b89091..1813e50685ca60e812d0655717a18f3384c0db31 100644
GIT binary patch
delta 50
zcmZ>CoFJlWaJfPA1H%X5lL`zU*jG9-C>dRDSjoZAJA*R;%2P7G+%V@FXM(H08vxbH
B5|sb|
delta 50
zcmZ>CoFJmhu(nn61H%X5lL`zU*jG9-C^2kkUCF`FJA*R;%2QgfwRO%l&IDI~Hvqlw
B5%d56
diff --git a/patches/ips/crash_handle_base.ips b/patches/ips/crash_handle_base.ips
index ce0ff77788ae7583f6302be37540bfc30dc2d1d7..e6be8eb7c7dce2473f4097acaff69279fec3735b 100644
GIT binary patch
delta 34
qcmeys{fk>Hz%j(xgCW>}fro)XiD5%)w$g&Ft=SVW(5GUj|!v!
delta 24
fcmeyx{ehb`z%j(xW1^TQ+gb(&CI*I$c6(R>UGoO3
diff --git a/patches/ips/crash_handle_springball.ips b/patches/ips/crash_handle_springball.ips
index f63002a17ba4d1dd7041fcab1f60b53ea869fcf1..0cb943754873d80bf997b153089c193a88dfd2f4 100644
GIT binary patch
delta 96
zcmZo=Y-OCF6Uo5fxK@GtBm>(7ll2Cd8#MVDJ_w&=V)($m(uqOI=yJnK28KBb+}V#5
nRyy^bXEqmViv#s?Wplh@XJAocSli0+@ehRI>hA^s_9Y*f
delta 96
zcmZo=Y-OCF6UoBhxK@GtBm>(7ll2U1TQ&I^J_w&=V)($m(uqNdVMFUm28KBb+}V#5
oRyy^bXEr~R6($bU%azUXik*Q)iDhjo$HzYqhO56D0L$VZ0{{R3
diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips
index d95e9b4104fbf0a16ad82432796caea9706b1867..67b0e3bcac8efa15007b63a868a24c7518df0fa1 100644
GIT binary patch
delta 29
lcmcDtoS-OSaJfN~;ll^Ugq5xgN=BC(l*}(TB)Iy!0RWq#3Zwu4
delta 29
lcmcDtoS-Pdu(nl`;ll^Ugq5xgN(>uXl@@GmO>p&h0|1i~3R3_8
diff --git a/patches/ips/crash_handle_yapping.ips b/patches/ips/crash_handle_yapping.ips
index 6d5b98fd113eae192b2dbeca38e55b6d4e631e06..17457d6bcfb4ac73a711fb160a47377b69db936e 100644
GIT binary patch
delta 48
zcmYdEkq>YTarR)^x`n}Zy}{)MO$PoC8YdZ;KCrKJW>7M^+%QpIfgMOGnFE<_asZM*
B5Yzww
delta 48
zcmYdEkq>YTarR)^x`n}ZJ;T~oO$PoC8YdZ;KCrKJW>8|-&^l3GfgMOGE!f&R(M=8j
Dflv@L
diff --git a/patches/rom_map/Bank 80.txt b/patches/rom_map/Bank 80.txt
index 26575e8d3..26292fe90 100644
--- a/patches/rom_map/Bank 80.txt
+++ b/patches/rom_map/Bank 80.txt
@@ -6,7 +6,7 @@ D140 - D200: oob_death.asm
D200 - D240: vanilla_bugfixes.asm
D240 - D310: stats.asm
D310 - D330: fast_reload.asm
-D330 - D340: [FREE]
+D330 - D340: crash_handle_base.asm [also referenced in patch.rs]
D340 - DA00: reserve_hud.asm
DA00 - DD00: msu1.asm
DD00 - E100: Palette pointer table for Mosaic
diff --git a/patches/rom_map/Bank 85.txt b/patches/rom_map/Bank 85.txt
index e0f4828d1..48bec0a6f 100644
--- a/patches/rom_map/Bank 85.txt
+++ b/patches/rom_map/Bank 85.txt
@@ -16,9 +16,8 @@ $AA00 - $AAA0: pause_menu_objectives.asm
$AAA0 - $AB00: [FREE]
$AB00 - $ACA0: map_area.asm
$ACA0 - $AD00: load_flash_suit
-$AD00 - $AD02: crash_handle_base.asm (crash toggles referenced by patch.rs )
-$AD02 - $AD45: crash_handle_springball.asm
-$AD45 - $AE20: [FREE]
+$AD00 - $AD43: crash_handle_springball.asm
+$AD43 - $AE20: [FREE]
$AE20 - $B000: reserve_backward_fill.asm
$B000 - $B5B4: crash_handle_base.asm
$B5B4 - $B5F1: crash_handle_yapping.asm
diff --git a/patches/src/constants.asm b/patches/src/constants.asm
index 483e01ffc..cef3b0386 100644
--- a/patches/src/constants.asm
+++ b/patches/src/constants.asm
@@ -56,3 +56,10 @@
; target number of frames for the pause menu black screen to lag
; (to compensate for VRAM decompression being faster):
!unpause_black_screen_lag_frames = $dfff07
+
+; crash handler / sub patches variables
+
+!crash_toggles = $80D330 ; overwritten by patch.rs , defined in crash_handle_base.asm
+
+!kill_samus = $80D337 ; defined in crash_handle_base.asm
+!bug_dialog = $80D332 ; defined in crash_handle_base.asm
diff --git a/patches/src/crash_handle_autoreserves.asm b/patches/src/crash_handle_autoreserves.asm
index dd34b9cf8..75bb1aa1a 100644
--- a/patches/src/crash_handle_autoreserves.asm
+++ b/patches/src/crash_handle_autoreserves.asm
@@ -7,11 +7,7 @@
;;;
-;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
-
-!crash_toggles = $85AD00
-!kill_samus = $85b5a0
-!bug_dialog = $85b000
+incsrc "constants.asm"
!bank_82_free_space_start = $82fbf0 ; pause / reserve bug
!bank_82_free_space_end = $82fc30
@@ -33,7 +29,7 @@ pause_func:
cmp #$0020
beq .fix
.warn
- lda #$0041 ; "fix" leaves the screen black like any pause close to fadeout, warning will relight the screen due to showing the dialog
+ lda #$0041 ; "fix" leaves the screen black like any pause close to fadeout, warning will relight the screen due to showing the dialog, could be changed to darken the screen again but it might be confusing.
jsl !bug_dialog
.fix
lda #$0008
diff --git a/patches/src/crash_handle_base.asm b/patches/src/crash_handle_base.asm
index 34bcd96f7..ef2d64da3 100644
--- a/patches/src/crash_handle_base.asm
+++ b/patches/src/crash_handle_base.asm
@@ -11,17 +11,27 @@
arch snes.cpu
lorom
-!bank_85_free_space_start = $85b000 ; be careful if moving this, the subpatches that use this code assume this location (xmode/oob/yappingmaw/autoreserve/springball)
-!bank_85_free_space_end = $85b5a0
+incsrc "constants.asm"
-!bank_85_free_space2_start = $85b5a0 ; kill samus routine, location called by sub patches, as above, be careful if ever moving this code and update subpatches.
-!bank_85_free_space2_end = $85b5b4
+!bank_80_free_space_start = $80D332 ; springboard for the sub patches.. They all point here for the crash loader / kill samus
+!bank_80_free_space_end = $80D340 ; if moving the crash handler base patch then this needs to be changed too.
+
+!bank_85_free_space_start = $85b000 ;
+!bank_85_free_space_end = $85b5b4
!msg_crash_timer_override = $7EF596 ; temporary variable used for overriding messagebox close delay times during crash box.
-!crash_toggles = $85ad00 ; this location is looked at by the subpatches to decide if to call the handler etc.
-org !crash_toggles
- dw $0000 ; overwritten by patch.rs if any values are changed, subpatches reference this. default is gameover / death (pre-toggleable setting)
+;;; global code.
+org !crash_toggles ; default at 80d330, srpringboard follows.
+ dw $0000 ; overwritten by patch.rs
+
+org !bank_80_free_space_start
+ jsl bug_dialog
+ rtl
+ jsl kill_samus
+ rtl
+
+assert pc() <= !bank_80_free_space_end
;;; hooks into vanilla code
@@ -164,9 +174,6 @@ xmode_msg:
msg_end:
-assert pc() <= !bank_85_free_space_end
-
-org !bank_85_free_space2_start
kill_samus:
lda #$8000 ; init death sequence (copied from $82db80)
sta $a78
@@ -176,4 +183,4 @@ kill_samus:
sta $998
rtl
-assert pc() <= !bank_85_free_space2_end
\ No newline at end of file
+assert pc() <= !bank_85_free_space_end
\ No newline at end of file
diff --git a/patches/src/crash_handle_springball.asm b/patches/src/crash_handle_springball.asm
index 63fef93a4..e7d322ccd 100644
--- a/patches/src/crash_handle_springball.asm
+++ b/patches/src/crash_handle_springball.asm
@@ -7,16 +7,13 @@
arch snes.cpu
lorom
-;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
+incsrc "constants.asm"
-!crash_toggles = $85AD00
-!kill_samus = $85b5a0
-!bug_dialog = $85b000
-!bank_82_free_space2_start = $82f810 ; hook unpause (springball crash)
-!bank_82_free_space2_end = $82f830
+!bank_82_free_space_start = $82f810 ; hook unpause (springball crash)
+!bank_82_free_space_end = $82f830
-!bank_85_free_space_start = $85ad04
+!bank_85_free_space_start = $85ad00
!bank_85_free_space_end = $85ad45
;;; vanilla hooks
@@ -33,7 +30,7 @@ org $91f1fc
;;; custom code
-org !bank_82_free_space2_start
+org !bank_82_free_space_start
check_unpause:
php
sep #$20
@@ -48,7 +45,7 @@ check_unpause:
lda #$0008 ; replaced code
jmp $93be
-assert pc() <= !bank_82_free_space2_end
+assert pc() <= !bank_82_free_space_end
org !bank_85_free_space_start
diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm
index f8f2ff85e..57daf8ab5 100644
--- a/patches/src/crash_handle_xmode.asm
+++ b/patches/src/crash_handle_xmode.asm
@@ -6,11 +6,10 @@
arch snes.cpu
lorom
+incsrc "constants.asm"
+
;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
-!crash_toggles = $85AD00
-!kill_samus = $85b5a0
-!bug_dialog = $85b000
!bank_91_free_space_start = $9195bc
!bank_91_free_space_end = $91965a
diff --git a/patches/src/crash_handle_yapping.asm b/patches/src/crash_handle_yapping.asm
index 7b41c99e1..417ff7d1d 100644
--- a/patches/src/crash_handle_yapping.asm
+++ b/patches/src/crash_handle_yapping.asm
@@ -5,11 +5,10 @@
arch snes.cpu
lorom
+incsrc "constants.asm"
+
;;; these variable are defined by the crash_handle_base.asm patch and patch.rs
-!crash_toggles = $85AD00
-!kill_samus = $85b5a0
-!bug_dialog = $85b000
!bank_85_free_space_start = $85b5b4
!bank_85_free_space_end = $85b5f1
@@ -39,7 +38,7 @@ assert pc() <= !bank_90_free_space_end
org !bank_85_free_space_start
ym_crash:
lda !crash_toggles
- and #$0F00
+ and #$0f00
beq .default
cmp #$0200
beq .fix
@@ -63,5 +62,5 @@ ym_crash:
jsl !kill_samus
rtl
-print pc
+
assert pc() <= !bank_85_free_space_end
\ No newline at end of file
diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs
index 2bba2b35d..b57efbd5d 100644
--- a/rust/maprando/src/patch.rs
+++ b/rust/maprando/src/patch.rs
@@ -998,7 +998,7 @@ impl Patcher<'_> {
}
pub fn write_crash_handler(&mut self, crash_fixes: &CrashFixes) -> Result<()> {
- let crash_handler = 0x85AD00;
+ let crash_handler = 0x80D330;
let word = crash_fixes.to_word();
if word == 0x3333 {
diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs
index 116e91de3..c99dcb6f6 100644
--- a/rust/maprando/src/settings.rs
+++ b/rust/maprando/src/settings.rs
@@ -1087,7 +1087,7 @@ fn upgrade_other_settings(settings: &mut serde_json::Value) -> Result<()> {
"Death" => CrashFixesPreset::Death,
"Warn" => CrashFixesPreset::Warn,
"Silent" => CrashFixesPreset::Silent,
- _ => bail!("Unrecognized area assignment preset: {}", preset_str),
+ _ => bail!("Unrecognized preset: {}", preset_str),
};
*crash_fixes = serde_json::to_value(CrashFixes::from_preset(preset))?;
}
From 2d442266c05b3d1cca4fd12682440ebd682ec42e Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 19:17:45 +0900
Subject: [PATCH 22/28] update randomize helpers
push the variations other than default (death) to the download page..
---
rust/maprando-web/src/randomize_helpers.rs | 24 +++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/rust/maprando-web/src/randomize_helpers.rs b/rust/maprando-web/src/randomize_helpers.rs
index 0ec7b32db..b29e9741a 100644
--- a/rust/maprando-web/src/randomize_helpers.rs
+++ b/rust/maprando-web/src/randomize_helpers.rs
@@ -10,9 +10,9 @@ use maprando::{
randomize::{DifficultyConfig, ItemPriorityGroup, Randomization, get_starting_items},
seed_repository::{Seed, SeedFile},
settings::{
- AreaAssignmentBaseOrder, AreaAssignmentPreset, DisableETankSetting, DoorLocksSize,
- ETankRefill, FillerItemPriority, ItemCount, RandomizerSettings, SpeedBooster, WallJump,
- get_objective_groups,
+ AreaAssignmentBaseOrder, AreaAssignmentPreset, CrashFixesPreset,
+ DisableETankSetting, DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount,
+ RandomizerSettings, SpeedBooster, WallJump, get_objective_groups,
},
spoiler_log::SpoilerLog,
spoiler_map,
@@ -189,6 +189,24 @@ impl SeedHeaderTemplate<'_> {
if other_settings.enable_major_glitches {
game_variations.push("Major glitches enabled");
}
+ if other_settings.crash_fixes.preset != Some(CrashFixesPreset::Death) {
+ match other_settings.crash_fixes.preset {
+ Some(CrashFixesPreset::Crash) => {
+ game_variations.push("Hardlock crashes unpatched");
+ }
+ Some(CrashFixesPreset::Warn) => {
+ game_variations.push("Hardlock crashes will display a warning (no death)");
+ }
+ Some(CrashFixesPreset::Silent) => {
+ game_variations
+ .push("Hardlock crashes will be fixed silently (no warning / no death)");
+ }
+ Some(CrashFixesPreset::Death) => {}
+ None => {
+ game_variations.push("Custom crash handler settings");
+ }
+ }
+ }
game_variations
}
}
From a8f44c05a83f84da6c1997b5725de2423cae068c Mon Sep 17 00:00:00 2001
From: nn <53490794+nn357@users.noreply.github.com>
Date: Wed, 25 Mar 2026 19:21:21 +0900
Subject: [PATCH 23/28] formatting
---
rust/maprando-web/src/randomize_helpers.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/rust/maprando-web/src/randomize_helpers.rs b/rust/maprando-web/src/randomize_helpers.rs
index b29e9741a..198aa1c84 100644
--- a/rust/maprando-web/src/randomize_helpers.rs
+++ b/rust/maprando-web/src/randomize_helpers.rs
@@ -10,9 +10,9 @@ use maprando::{
randomize::{DifficultyConfig, ItemPriorityGroup, Randomization, get_starting_items},
seed_repository::{Seed, SeedFile},
settings::{
- AreaAssignmentBaseOrder, AreaAssignmentPreset, CrashFixesPreset,
- DisableETankSetting, DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount,
- RandomizerSettings, SpeedBooster, WallJump, get_objective_groups,
+ AreaAssignmentBaseOrder, AreaAssignmentPreset, CrashFixesPreset, DisableETankSetting,
+ DoorLocksSize, ETankRefill, FillerItemPriority, ItemCount, RandomizerSettings,
+ SpeedBooster, WallJump, get_objective_groups,
},
spoiler_log::SpoilerLog,
spoiler_map,
From 1a389f377ce08283e9bd2325fe14815fab877eff Mon Sep 17 00:00:00 2001
From: Brent Kerby
Date: Wed, 25 Mar 2026 07:09:10 -0600
Subject: [PATCH 24/28] fix settings upgrade
---
rust/maprando/src/settings.rs | 66 +++++++++++------------------------
1 file changed, 20 insertions(+), 46 deletions(-)
diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs
index c99dcb6f6..17d4f15fa 100644
--- a/rust/maprando/src/settings.rs
+++ b/rust/maprando/src/settings.rs
@@ -528,38 +528,6 @@ pub enum CrashFixesPreset {
Crash,
}
-#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
-pub enum CrashFixesSpringball {
- Death,
- Warn,
- Silent,
- Crash,
-}
-
-#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
-pub enum CrashFixesYappingmaw {
- Death,
- Warn,
- Silent,
- Crash,
-}
-
-#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
-pub enum CrashFixesAutoreserve {
- Death,
- Warn,
- Silent,
- Crash,
-}
-
-#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
-pub enum CrashFixesXmode {
- Death,
- Warn,
- Silent,
- Crash,
-}
-
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[repr(u16)]
pub enum FixMode {
@@ -568,6 +536,7 @@ pub enum FixMode {
Silent = 2,
Crash = 3,
}
+
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct CrashFixes {
pub preset: Option,
@@ -1075,23 +1044,28 @@ fn upgrade_other_settings(settings: &mut serde_json::Value) -> Result<()> {
*area_assignment = serde_json::to_value(AreaAssignment::from_preset(preset))?;
}
}
- {
- let crash_fixes = other_settings
- .get_mut("crash_fixes")
- .context("missing crash fixes")?;
- if crash_fixes.is_string() {
- let preset_str = crash_fixes.as_str().unwrap();
- let preset = match preset_str {
- "Crash" => CrashFixesPreset::Crash,
- "Death" => CrashFixesPreset::Death,
- "Warn" => CrashFixesPreset::Warn,
- "Silent" => CrashFixesPreset::Silent,
- _ => bail!("Unrecognized preset: {}", preset_str),
- };
- *crash_fixes = serde_json::to_value(CrashFixes::from_preset(preset))?;
+ match other_settings.get_mut("crash_fixes") {
+ None => {
+ other_settings.insert(
+ "crash_fixes".to_string(),
+ serde_json::to_value(CrashFixes::from_preset(CrashFixesPreset::Death))?,
+ );
+ }
+ Some(crash_fixes) => {
+ if let Some(preset_str) = crash_fixes["preset"].as_str() {
+ let preset = match preset_str {
+ "Crash" => CrashFixesPreset::Crash,
+ "Death" => CrashFixesPreset::Death,
+ "Warn" => CrashFixesPreset::Warn,
+ "Silent" => CrashFixesPreset::Silent,
+ _ => bail!("Unrecognized preset: {}", preset_str),
+ };
+ *crash_fixes = serde_json::to_value(CrashFixes::from_preset(preset))?;
+ }
}
}
+
if other_settings.get("disable_spikesuit").is_none()
|| other_settings["disable_spikesuit"].as_bool().is_none()
{
From 3feec042d39ff56f33407f496489ae641baca6b3 Mon Sep 17 00:00:00 2001
From: Brent Kerby
Date: Wed, 25 Mar 2026 07:34:03 -0600
Subject: [PATCH 25/28] implement warn option for x-mode
---
patches/ips/crash_handle_xmode.ips | Bin 57 -> 95 bytes
patches/rom_map/RAM.txt | 3 ++-
patches/rom_map/vanilla_hooks.txt | 1 +
patches/src/crash_handle_xmode.asm | 23 ++++++++++++++++++++---
4 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips
index 67b0e3bcac8efa15007b63a868a24c7518df0fa1..dc24ef3a86050aa66aca700a21f8eb47b1395bfc 100644
GIT binary patch
delta 69
zcmcDFpCIpGvfkiwgC@g=55nu`e672{Bh?S2R=P4M8C`BjfYM6lKz45h*GdMEqz~h1
Qj#K{_n0=m1b@g`x037EX(f|Me
delta 31
ncma#IoFFeHw%*`!gC@g=4~z*bT^W>&E;lHdUv5Zn^>+gRwgn51
diff --git a/patches/rom_map/RAM.txt b/patches/rom_map/RAM.txt
index 7c79659cb..da84caccd 100644
--- a/patches/rom_map/RAM.txt
+++ b/patches/rom_map/RAM.txt
@@ -45,7 +45,8 @@ $7EF594-$7EF596: fix_transition_bad_tiles.asm
$7EF596: vanilla_bugfixes.asm
$7EF597-$7EF599: split_speed.asm
$7EF59A-$7EF59C: reserve_backward_fill.asm
-$7EF59C-$7EFE00: [FREE]
+$7EF59C-$7EF59E: crash_handle_xmode.asm
+$7EF59E-$7EFE00: [FREE]
$7EFE00: map area (copy of $1F5B to be saved to SRAM)
$7EFE02: map area explored mask
$7EFE04: copy of $1F5D (to be saved to SRAM for escape autosave)
diff --git a/patches/rom_map/vanilla_hooks.txt b/patches/rom_map/vanilla_hooks.txt
index f406e1ba1..33ab55fbe 100644
--- a/patches/rom_map/vanilla_hooks.txt
+++ b/patches/rom_map/vanilla_hooks.txt
@@ -990,6 +990,7 @@ $909e88 - walljump_plm.asm
[BANK 91]
$9181f4 - aim_anything.asm
+$91cafe - crash_handle_xmode.asm
$91e164 - fast_reload.asm
$91e604 - fix_hyper_slowlock.asm
$91deba - gravity_palette.asm
diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm
index 57daf8ab5..ce12ff969 100644
--- a/patches/src/crash_handle_xmode.asm
+++ b/patches/src/crash_handle_xmode.asm
@@ -14,6 +14,8 @@ incsrc "constants.asm"
!bank_91_free_space_start = $9195bc
!bank_91_free_space_end = $91965a
+!shown_warning = $7EF59C ; whether a warning has already been shown since X-Ray initialized
+
org $91816f
xmodefix:
php
@@ -28,8 +30,12 @@ xmodefix:
assert pc() <= $918181 ; Make sure we don't overwrite the next routine.
+org $91cafe
+ jmp hook_setup_xray
+return_setup_xray:
+
;;;
-;;; custom code - currently only using upto {9195D2} {approx 130bytes free, reserved for more future xmode warn fix.}
+;;; custom code - currently only using up to {9195D2} {approx 130bytes free, reserved for more future xmode warn fix.}
;;;
org !bank_91_free_space_start
xmode:
@@ -39,8 +45,12 @@ xmode:
;cmp #$0200
;beq .fix
.warn
- ;lda #$0045 ; crash dialog (warning) removed until better solution found, it will re-trigger many times until samus is out of collission so annoying.
- ;jsl !bug_dialog ; there is space available here for additional code.
+ lda !shown_warning
+ bne .fix
+ inc
+ sta !shown_warning ; set !shown_warning to 1
+ lda #$0045
+ jsl !bug_dialog
.fix
rts
.default
@@ -49,4 +59,11 @@ xmode:
jsl !kill_samus
rts
+; initialize shown_warning to 0 when starting to use x-ray
+hook_setup_xray:
+ sta $0a78 ; run hi-jacked instruction: time-is-frozen flag = 1
+ lda #$00
+ sta !shown_warning
+ jmp return_setup_xray
+
assert pc() <= !bank_91_free_space_end
\ No newline at end of file
From 3e1f36e60b5c33c7c8e25a8ca4d70e5cc552a44d Mon Sep 17 00:00:00 2001
From: Brent Kerby
Date: Wed, 25 Mar 2026 07:43:19 -0600
Subject: [PATCH 26/28] uncomment silent option for x-mode
---
patches/ips/crash_handle_xmode.ips | Bin 95 -> 100 bytes
patches/src/crash_handle_xmode.asm | 4 ++--
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/patches/ips/crash_handle_xmode.ips b/patches/ips/crash_handle_xmode.ips
index dc24ef3a86050aa66aca700a21f8eb47b1395bfc..f15618ca78e4f8d50be985ffc8cf3642c83656cd 100644
GIT binary patch
delta 29
lcmaz~nIO+^x!&M%gC@g=4-zLC6g~(|)HGy$In~wQ4FIWZ3qb$?
delta 24
gcmYd^pCHd=vfkiwgC@g=55g1m3|XH{b@g`x0CK|#M*si-
diff --git a/patches/src/crash_handle_xmode.asm b/patches/src/crash_handle_xmode.asm
index ce12ff969..1b436d44f 100644
--- a/patches/src/crash_handle_xmode.asm
+++ b/patches/src/crash_handle_xmode.asm
@@ -42,8 +42,8 @@ xmode:
lda !crash_toggles
and #$F000
beq .default
- ;cmp #$0200
- ;beq .fix
+ cmp #$2000
+ beq .fix
.warn
lda !shown_warning
bne .fix
From 9813200e861d1acff8aed92233e92a548f55df08 Mon Sep 17 00:00:00 2001
From: Brent Kerby
Date: Wed, 25 Mar 2026 08:13:33 -0600
Subject: [PATCH 27/28] adjust crash descriptions, add a bit more detail
---
.../generate/help/variations/crash_fixes.html | 20 ++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/rust/maprando-web/templates/generate/help/variations/crash_fixes.html b/rust/maprando-web/templates/generate/help/variations/crash_fixes.html
index bae12dea5..c3400e500 100644
--- a/rust/maprando-web/templates/generate/help/variations/crash_fixes.html
+++ b/rust/maprando-web/templates/generate/help/variations/crash_fixes.html
@@ -15,16 +15,16 @@
Crash Fixes
triggered:
-
Spring Ball bounce: Triggered by pausing and unequipping Spring ball at certain points
- mid bounce.
+
Spring Ball bounce: Triggered by pausing and unequipping Spring Ball at certain points
+ in the middle of a neutral bounce.
-
Yapping Maw shinespark: If grabbed by a Yapping Maw during a shinespark followed by
+
Yapping Maw shinespark: If a Yapping Maw tries to grab Samus during a shinespark, followed by
having no inputs held during the shinespark end.
Frame perfect pause, autoreserve: Having autoreserves trigger on the exact frame the
- screen fades out during a pause.
+ screen finishes fading out while pausing.
-
X-Mode tile collision: Colliding with a solid tile during X-Mode traversal.
+
X-Mode tile collision: Colliding with a solid tile during X-Mode.
Individual customization options are available by clicking the gear button on the right.
@@ -32,19 +32,25 @@
Crash Fixes
-
Crash: When triggered, only the vanilla code will run. This WILL cause a hardlock of the game.
+
Crash: When triggered, only the vanilla code will run. This will cause a hardlock of the game.
Death: When triggered, a dialog box will appear explaining the crash followed by
triggering a death sequence / game-over, preventing the vanilla hardlock.
Warn: When triggered, a dialog box will appear explaining the crash but game play will
- continiue after the dialog box is closed.
+ continue after the dialog box is closed.
Silent: When triggered, no dialog box will appear and the game will continue as if the
crash hadn't been triggered.
+
Details
+
+ In underwater physics, the Spring Ball bounce crash is triggered when Spring Ball is unequipped at any point
+ while Samus is moving upward from the neutral bounce. In air physics, it happens when Spring Ball is unequipped
+ on an exact frame when landing from the neutral bounce.
+