diff --git a/README.md b/README.md index 87b7282..e2b8adb 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,13 @@ ||NTSC-J 1.0|NTSC-J 1.1|NTSC-U|PAL| |-|:-:|:-:|:-:|:-:| |[Pattern Selector](#pattern-selector)|◎|◎|[〇](https://twitter.com/torcnein/status/1518614922850148353)|?| +|[Instant Level Select](#instant-level-select)|◎|||| |[Instant Restart](#instant-restart)|◎|◎|[〇](https://twitter.com/torcnein/status/1518614922850148353)|?| |[Fast Forward](#fast-forward)|◎|◎|?|?| |[Skip Stage Intro](#skip-stage-intro)|◎|◎|?|?| |[Mirror Mode](#mirror-mode)|◎|◎|[Source](https://discord.com/channels/83214196182880256/273126568795176960/960281550275104768)|?| +|[Spawn Yoshi](#spawn-yoshi)|◎|||| +|[Load flags for Any%](#load-flags-for-any)|◎|||| ◎: tested by sup39 〇: tested by others @@ -19,10 +22,21 @@ Note: **you need to enable `drawText` as well** +### Instant Level Select +- Press `B + D-Pad Up` to **restart the current area** + - The **respawn position** will be the same. It can be used to practice Honey Skip or stage movement in Delfino Plaza +- Press `{button of Level Select} + B + D-Pad Up` to activate Level Select immediately + - For `Z + B + D-Pad Up`, the current area will be restarted, but the **respawn position will be reset**. It is like B+DPad_Up, but the respawn position will be the default position instead of the previous one + - For `Y + B + D-Pad Up`, it will restart from the **previous selected area**. For example, if you select SB4 with this code, and enter hotel/casino then press Y+B+DPad_Up, it will restart from SB4 beach (instead of hotel/casino if you use B+DPad_Up or Z+B+DPad_Up) + - NOTE: **Z menu will be disabled** +- Press `R + D-Pad Left/Right` to enable/disable **Area Lock** + - With Area Lock, warps will restart the current area instead of sending Mario to other areas, which can be used to practice specific area (e.g. outside of BH2 wildmill, secret stage entering) + - Restarting acts like B+DPad_Up, and therefore can be used to practice Honey Skip, etc. + ### Instant Restart - press `Y + D-Pad Up` to restart without pausing + exit area -You can change the `0808` in the 3rd line to the button input value you want. +You can change the `0808` in the 3rd line to the [button input value](https://app.sms.sup39.dev/code/button-calculator/) you want. ### Fast Forward - press `B + D-Pad Down` to fast forward 4x @@ -56,7 +70,7 @@ Press `Y +` the following button to spawn Yoshi with specific color and make Mar - `D-Pad Down`: Pink - `D-Pad Up`: Green -### Load flags for Any% +### Load Flags for Any% Press `L + R +` C stick with the following direction `+ D-Pad Left` to load a preset file (flags) for Any% and spawn Mario in plaza C Stick|State diff --git a/src/InstantLevelSelect/InstantLevelSelect.ld b/src/InstantLevelSelect/InstantLevelSelect.ld new file mode 100644 index 0000000..162ce0b --- /dev/null +++ b/src/InstantLevelSelect/InstantLevelSelect.ld @@ -0,0 +1,24 @@ +__baseAddr__ = 0x817f9800; + +$LevelSelect.area = 0x817f0000; +$LevelSelect.epFlag = 0x817f0002; +$LevelSelect.en = 0x817f0004; +$AreaLock.en = 0x817f0005; + +$b$.TMarioGamePad.onNeutralMarioKey = 0x800fbad8; + +$b$.Mar.changeState = 0x800ec420; +.Mar.changeState.applied = 0x800ecb28; +$b$.Mar.moveStage = 0x800eab80; +/* TMarDirector::fireStreamingMovie */ +$bl$.Mar.disable.PinnaFMV = 0x800ed64c; + +$b$.gameLoop.preDirect = 0x800f9b60; +$b$.gameLoop.preReturn = 0x800f9d68; + +$b$.Movie.direct = 0x8010b100; +$b$.Movie.decideNextMode = 0x8010b3cc; +.Movie.decideNextMode.applied = 0x8010b534; + +$b$.ShineSelect.direct = 0x80236600; +.ShineSelect.direct.applied = 0x80236620; diff --git a/src/InstantLevelSelect/InstantLevelSelect.s b/src/InstantLevelSelect/InstantLevelSelect.s new file mode 100644 index 0000000..50eee91 --- /dev/null +++ b/src/InstantLevelSelect/InstantLevelSelect.s @@ -0,0 +1,92 @@ +.set FULL, 1 # FULL InstantLevelSelect +.include "LevelSelect.s" + +.Mar.changeState: +## Level Select + #lbz r0, 0x64(r31) + #cmpwi r0, 0 + #beq- .Mar.changeState.done + bl LevelSelect + bne+ cr1, .Mar.changeState.done # not activated + mr r3, r31 + bl TMarDirector_moveStage + li r28, 9 # nextState = LevelTransition + b .Mar.changeState.applied +.Mar.changeState.done: + lbz r0, 0x64(r31) + b 4+$b$.Mar.changeState + +.Mar.moveStage: + lis r817F, 0x817F + lhz r0, $LevelSelect.en@l(r817F) # [LS, AL] + cmpwi r0, 1 # AL only + bne .Mar.moveStage.done +## clear FMV flag + lhz r0, 0x4c(r31) + rlwinm r0, r0, 0, 0x18, 0x16 + sth r0, 0x4c(r31) +## store nextArea and set flags + lwz rFM, TFlagManager_smInstance$r13(r13) + mr rApp, r28 + lhz rAns, 0xE(rApp) + bl handleRestartN +## set curArea = prevArea # TODO + lhz rAns, 0xa(r28) + sth rAns, 0xe(r28) +.Mar.moveStage.done: +## orig + li r0, 0xff + addi r4, r1, 0x54 + b 4+$b$.Mar.moveStage + +.Mar.disable.PinnaFMV: + lis r12, 0x817F + lbz r4, $AreaLock.en@l(r12) + xori r4, r4, 1 # locked ? false : true + b TFlagManager_setBool + + +.if FULL == 1 +.Movie.direct: + bl LevelSelect + bne+ cr1, .Movie.direct.done # not activated + li r3, 2 + stw r3, 0x12c(r1) +.Movie.direct.done: +## orig + lwz r3, 0x20(r31) + b 4+$b$.Movie.direct + +.Movie.decideNextMode: + lis r817F, 0x817F + lbz r0, $LevelSelect.en@l(r817F) + cmpwi r0, 1 # test if LS activated + bne .Movie.moveStage.done +## return + b .Movie.decideNextMode.applied +.Movie.moveStage.done: +## orig + cmplwi r5, 0xe + b 4+$b$.Movie.decideNextMode + + +.ShineSelect.direct: + bl LevelSelect + bne+ cr1, .ShineSelect.direct.done # not activated + b .ShineSelect.direct.applied +.ShineSelect.direct.done: +## orig + lwz r3, 0x20(r30) + subi r5, r31, 0x34 + lbz r0, 0x13b(r3) + b 4+$b$.ShineSelect.direct +.endif + +.gameLoop.preReturn: +## reset LevelSelect.en + lis r3, 0x817F + li r0, 0 + stb r0, $LevelSelect.en@l(r3) +## orig + mr r3, r29 + b 4+$b$.gameLoop.preReturn diff --git a/src/InstantLevelSelect/LevelSelect.s b/src/InstantLevelSelect/LevelSelect.s new file mode 100644 index 0000000..583ed09 --- /dev/null +++ b/src/InstantLevelSelect/LevelSelect.s @@ -0,0 +1,258 @@ +.set r817F, 3 # 817F0000 +.set rC, 4 +.set rBtn, 5 +.set rC4, 5 +.set rIdx, 6 +.set rAns, 7 +.set rAnsEp, 8 +.set rD, 9 # BL trick +.set rApp, 10 # gpApplication +.set rFM, 11 # FlagManager +.set rArr, 12 # tmp + +LevelSelect: # cr1.EQ: activated +## read button input: [M]*1 [C]*1 [btn]*2 + lis rBtn, JUTGamePad_mPadButton@ha + lwz rBtn, JUTGamePad_mPadButton@l(rBtn) +## check input + andi. r0, rBtn, 0x208 + cmplwi cr1, r0, 0x208 +## check if already activated + lis r817F, 0x817F + lbz r0, $LevelSelect.en@l(r817F) + mr. r0, r0 +## return if needed + # cr1.eq && cr0.eq + crand 4*cr1+eq, 4*cr1+eq, eq + bnelr+ cr1 +## Level Select + mflr r12 + bl .L_LevelSelect +.D: +.D_Special: + # 8 bit/entry: [1bit] epFlag==7 | [7bit] area + .long 0x00141516 + .long 0x0017181D + .long 0x34000090 # Red coin fish@NB8: 0x80|0x10 +.D_Secrets: + # 8 bit/entry: [1bit] ep==1 | [7bit] area + .long 0x2F2E3020 + .long 0x32293328 + .long 0x2A1FBA3C +.D_Sublevels: + # 8 bit/entry + .long 0x371E213A + .long 0x0E2C3900 +.D_Plaza: + .long 0x00010507 + .long 0x08090200 +.D_Extra: +.D_PinnaPark: +## ep: 0,1,2,3,4,5,7 | area=D +## epFlag: 0,2,4,5,6,7,0 + .long 0x123457D0 + .long 0x24567000 +.D_SirenaHotel: +## ep: 0,1,2,2,3,4 | area=7 +## episo: 1,2,3,4,6,7 + .long 0x12234070 + .long 0x23467001 + +.L_LevelSelect: + mflr rD + mtlr r12 +## prepare registers + lis rApp, gpApplication@ha + la rApp, gpApplication@l(rApp) + lwz rFM, TFlagManager_smInstance$r13(r13) + +## calc index +## btn: S YXZA -LRZ ---- +## XZRL=8,4,2,1 + rlwinm rIdx, rBtn, 32-7, 0x8 # X=8 + rlwimi rIdx, rBtn, 32-2, 0x4 # Z=4 + rlwimi rIdx, rBtn, 32-4, 0x2 # R=2 + rlwimi rIdx, rBtn, 32-6, 0x1 # L=1 +## YSY=8,4,2 + rlwinm r0, rBtn, 32- 8, 0x8 # Y=+8 + rlwimi r0, rBtn, 32-10, 0x6 # S,Y=4,+2 +## merge XZRL and YSY- + or rIdx, rIdx, r0 +# TODO? handle rIdx>=12 + +## check C==0(Special) or 9(Secrets) + rlwinm. rC, rBtn, 32-16, 0xF + beq handleSpecial + cmpwi rC, 9 + beq handleSecrets + +## calc C index +### [(*), 6, 2, (*); 4, 5, 3, (*); 0, (*), 1] +### 110 0/10 00/0 100/ 101 0|11 00/0 000/ 000 0/01 00 +.set CIdxMagic, 06204530001<<(32-3*10) +### CIdx = magic <<(3*C) &7 + lis r0, CIdxMagic@h + ori r0, r0, CIdxMagic@l + mulli rC, rC, 3 + rlwnm rC, r0, rC, 0x7 +## prepare CIdx<<2 (destroy rBtn) + slwi rC4, rC, 2 + +## Y+Z(14) -> SirenaHotel +## X+Z(12) -> PinnaPark + cmpwi rIdx, 12 + bge handleExtra + +## Y(10) -> Plaza + cmpwi rIdx, 10 + bge handlePlaza +## X(8) -> Sublevels + cmpwi rIdx, 8 + bge handleSublevels + +## stage +### area: [2, 3, 4, 5, 6, 8, 9, (*)] +### magic = 0x34568902 rotl sizeof(0xFF) +### ans = idx | (magic<<(Cidx<<2)) &0x0F00 +### ep = idx + lis r0, 0x5689 + ori r0, r0, 0x0234 + slwi rArr, rC, 2 + rlwnm rArr, r0, rArr, 0x0F00 + or rAns, rIdx, rArr + rlwinm rAnsEp, rIdx, 0, 0x7 + b .L_loadStage + +handleSpecial: + lhz rAns, 0xE(rApp) # backup for handleRestartN +## neutral + cmpwi rIdx, 0 + beq handleRestartN +## Z restart + cmpwi rIdx, 4 + beq handleRestartZ +## Y restart + cmpwi rIdx, 10 + beq handleRestartY +## Special + # rD = .D_Special + lbzx r0, rD, rIdx # offset = idx + rlwinm rAns, r0, 8, 0x3F00 + rlwinm rAnsEp, r0, 32-7, 0x1 + mulli rAnsEp, rAnsEp, 7 + b .L_loadStage + +handleRestartN: +## set prevMap = curMap + lhz r0, 0xA(rApp) + sth r0, 0xE(rApp) +handleRestartZ: +## load curMap, ep + lbz rAnsEp, 0xDF(rFM) + b .L_loadStageWithoutBackup +handleRestartY: # load 817F0000 + lhz rAns, $LevelSelect.area@l(r817F) + lbz rAnsEp, $LevelSelect.epFlag@l(r817F) + b .L_loadStage + +handleSecrets: + la rArr, .D_Secrets-.D(rD) + lbzx r0, rArr, rIdx + rlwinm rAns, r0, 8, 0x3F00 + rlwimi rAns, r0, 32-7, 0x0001 + # ep: 2,5,3,0,1,5,1,3,4,5,0,* + .set SecretEpMagic, 05301513450<<2 | 02 + lis r0, SecretEpMagic@h + ori r0, r0, SecretEpMagic@l + mulli rArr, rIdx, 3 + rlwnm rAnsEp, r0, rArr, 0x7 + b .L_loadStage + +handleSublevels: + la rArr, .D_Sublevels-.D(rD) + lbzx r0, rArr, rC # Cidx as index + rlwinm rAns, r0, 8, 0x3F00 + # ep: 1,1,3,7,3,2,3 + .set SublevelEpMagic, 0x13732301 + lis r0, SublevelEpMagic@h + ori r0, r0, SublevelEpMagic@l + rlwnm rAnsEp, r0, rC4, 0x7 + b .L_loadStage + +handleExtra: + la rArr, .D_Extra-.D-12*4(rD) + rlwinm r0, rIdx, 2, 0x38 # offset: {12,14}<<2 + lwzux r0, rArr, r0 + rlwnm rAns, r0, rC4, 0x7 # ep + rlwimi rAns, r0, 4, 0x0F00 # a0 => aEE + lwz r0, 4(rArr) + rlwnm rAnsEp, r0, rC4, 0x7 + b .L_loadStage + +handlePlaza: +## [0, 1, 5, 7; 8, 9, 2, (*)] +## 15789200 +## ans == 0x0100 | (magic << (arr:=CIdx<<2) &0xF) + .set PlazaEpMagic, 0x15789200 + lis r0, PlazaEpMagic@h + ori r0, r0, PlazaEpMagic@l + rlwnm rAns, r0, rC4, 0xF + ori rAns, rAns, 0x0100 + li rAnsEp, 0 + +.L_loadStage: +### backup to 817F0000 (for Y) + sth rAns, $LevelSelect.area@l(r817F) + stb rAnsEp, $LevelSelect.epFlag@l(r817F) + +/* rAns, rAnsEp, rFM, rApp, r817F */ +.L_loadStageWithoutBackup: +## reset QFT + li r0, 1 + stb r0, 0xB3(r817F) +### Ingame Timer: Reset Stopwatch Flag + stb r0, 0x100(r817F) +### set SGT Disable Custom IG Timer Flag = 1 TODO + #stb r0, 0x101(r817F) +### en flag + stb r0, $LevelSelect.en@l(r817F) +## FlagManager +### epFlag(40003) + stb rAnsEp, 0xDF(rFM) +### reset coin counter(40002) + li r0, 0 + stw r0, 0xD8(rFM) +#### set SGT Stop Stopwatch Flag = 0 TODO + #stw r0, 0x10C(r817F) +### Got a Shine in previous stage (30006) +#### + watched AP, court, peach kidnap, FLUDD theft flag + lhz r0, 0xCC(rFM) + ori r0, r0, 0x40FF +#### clear watched Pinna kidnap FMV flag + rlwinm r0, r0, 0, 0x14, 0x12 + sth r0, 0xCC(rFM) +## rApp = gpApplication +### write nextArea + sth rAns, 0x12(rApp) + +# SirenaHotel(0x07) or Casino(0x0E) ? 59 : 0 + rlwinm r0, rAns, 32-8, 0xFF # area + lwz rArr, 0x20(rApp) # TMarioGamePad* + li rC, 59 + cmpwi r0, 0x07 # SirenaHotel + beq- .L_handleStickCD + cmpwi r0, 0x0E # Casino + beq- .L_handleStickCD + li rC, 0 +.L_handleStickCD: + sth rC, 0xe4(rArr) + blr + +# disable TMarioGamePad::onNeutralMarioKey when LevelSelect is activated +.TMarioGamePad.onNeutralMarioKey: + lis r12, $LevelSelect.en@ha + lbz r0, $LevelSelect.en@l(r12) + mr. r0, r0 + bnelr # return if LevelSelect is activated + li r0, 60 + b 4+$b$.TMarioGamePad.onNeutralMarioKey