.set rC, 3 # SHOULD NOT BE USE UNTIL checkAreaLock ENDS
.set r817F, 4
.set rBtn, 5
.set rC4, 5
.set rIdx, 6
.set rAns, 7
.set rAnsEp, 8
.set rD, 9 # BL trick
.set rFM, 11 # FlagManager
.set rTmp, 10
.set rApp, 31 # gpApplication
.set rNextGameMode, 29
.set crAreaLock, 7
.set crArea9, 6
.set crArea234568, 5
.set $btnLevelSelect, 0x8 # D_UP
.set $btnShineSelect, 0x4 # D_DOWN

.macro li32 reg val
  lis \reg, \val@h
  ori \reg, \reg, \val@l
.endm

.L.prepare:
## prepare registers
  lis r817F, 0x817F
  lwz rFM, TFlagManager_smInstance$r13(r13)

## read button input: [M]*1 [C]*1 [btn]*2
  lis r12, JUTGamePad_mPadButton@ha
  lwz rBtn, JUTGamePad_mPadButton@l(r12)

/** NOTE: DO NOT CHANGE r12 UNTIL checkAreaLock ENDS */
.L.checkAreaLock:
## r0 = 0x22 - btn
  subfic r0, rBtn, 0x22
### (lower 16 bit of r0 & ~1) == 0 => use r0&1 as AreaLock enable state
  rlwinm. rTmp, r0, 0, 0x10, 0x1E
  beq .L.checkAreaLock.1
### else, load r0 = current state from RAM
  lbz r0, $LevelSelect.AreaLock@l(r817F)
.L.checkAreaLock.1:
## !cr0.eq: AreaLock enabled
  rlwinm. r0, r0, 0, 0x1
## write current enable state to RAM
  stb r0, $LevelSelect.AreaLock@l(r817F)

## activate AreaLock only if director finished (rNextGameMode > 1)
  cmplwi cr1, r3, 1
  crandc eq, 4*cr1+gt, eq
  bne+ .L.checkAreaLock.done

## set button input to (handleRestartN) if AreaLock enabled
  li rBtn, $btnLevelSelect
  stw rBtn, JUTGamePad_mPadButton@l(r12)

.L.checkAreaLock.done:
## orig
  mr. rNextGameMode, r3
## return now if still setting up (nextGameMode != 0)
  beq .L.done


## disable the code if paused
.L.checkPaused:
### r12 = *gpMarDirector
  lwz r12, gpMarDirector$r13(r13)
  cmplwi r12, 0
  beq .L.checkPaused.done
### r12 = gpMarDirector->gameMode
  lbz r12, 0x64(r12)
### if gameMode == 5 or 10
  li r0, 0x21 # (LSB 0, 5 == 1)
  rlwnm. r0, r0, r12, 31-10, 31-10 # LSB 10
  bne .L.done
.L.checkPaused.done:


## check button (Shine Select)
.L.checkButton.ShineSelect:
  andi. r0, rBtn, $btnShineSelect
  beq+ .L.checkButton.ShineSelect.done
### disable Shine Select when already in Shine Select
  lbz r0, 0x8(rApp) # directory type
  cmpwi r0, 8 # shine select
  beq .L.checkButton.ShineSelect.done
.ShineSelect:
## rAns = shineStageTable[curArea]
  lbz r0, 0xE(rApp) # current area
  lis rTmp, shineStageTable@ha
  la rTmp, shineStageTable@l(rTmp)
  lbzx rAns, rTmp, r0
## ignore Plaza (rAns <= 1)
  cmpwi rAns, 1
  ble .L.done
## map 7~8 to 8~9
  addi r0, rAns, 1
  rlwinm r0, r0, 32-3, 0x1
  add rAns, rAns, r0
## rAns = rAns << 8 | 0xff
  rlwinm rAns, rAns, 8, 0, 31
  ori rAns, rAns, 0xFF
## TODO handle invalid shine stage (0, 1, 0xFF)
  b handleRestartZ
.L.checkButton.ShineSelect.done:


### check button (Level Select)
.L.checkButton.LevelSelect:
  andi. r0, rBtn, $btnLevelSelect
  beq+ .L.done

## Level Select
  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

.L.main:
## calc index
## btn: S YXBA -LRZ ----
## XBRL=8,4,2,1
  rlwinm rIdx, rBtn, 32-7, 0xC # X,B=8,4
  rlwimi rIdx, rBtn, 32-4, 0x2 # R=2
  rlwimi rIdx, rBtn, 32-6, 0x1 # L=1
## Z=4
  rlwinm r0, rBtn, 32- 2, 0x4 # Z=4
  or rIdx, rIdx, r0
## 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 r12, rC, 2
  rlwnm r12, r0, r12, 0x0F00
  or rAns, rIdx, r12
  rlwinm rAnsEp, rIdx, 0, 0x7
  b .L.loadStage

handleSpecial:
  lhz rAns, 0xE(rApp) # set for handleRestart{N,Z}
## 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 curArea = prevArea
  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 r12, .D.Secrets-.D(rD)
  lbzx r0, r12, 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 r12, rIdx, 3
  rlwnm rAnsEp, r0, r12, 0x7
  b .L.loadStage

handleSublevels:
  la r12, .D.Sublevels-.D(rD)
  lbzx r0, r12, 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 r12, .D.Extra-.D-12*4(rD)
  rlwinm r0, rIdx, 2, 0x38 # offset: {12,14}<<2
  lwzux r0, r12, r0
  rlwnm rAns, r0, rC4, 0x7 # ep
  rlwimi rAns, r0, 4, 0x0F00 # a0 => aEE
  lwz r0, 4(r12)
  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
  stb rNextGameMode, 0xB3(r817F) # >0
## FlagManager
### epFlag(40003)
  stb rAnsEp, 0xDF(rFM)
### reset coin counter(40002)
  li  r0, 0 ## FIXME: make use of other register
  stw r0, 0xD8(rFM)
### set flag
  lhz r0, 0xCC(rFM)
#### Got a Shine in previous stage (30006)
  ori r0, r0, 0x4000
#### clear watched Pinna kidnap FMV flag (prevent spawn in PP unlocked position)
  rlwinm r0, r0, 0, 0x14, 0x12
  sth r0, 0xCC(rFM)
## rApp = gpApplication
### write nextArea
  sth rAns, 0x12(rApp)

.L.handleStickCD:
  lwz r12, 0x20(rApp) # TMarioGamePad*
## area
  rlwinm r0, rAns, 32-8, 0xFF # area
## crArea9(eq): area == 9
  cmpwi crArea9, r0, 9
## crArea234568(eq)
### cr1(gt): area >= 0x10
  cmplwi cr1, r0, 0xF
### cr0(eq): area magic == 0
  li rTmp, 0x7D # 234568 => 111_1101
  rlwnm. rTmp, rTmp, r0, 31-8, 31-8 # LSB 8
### crArea234568(eq) = !(area >= 0x10) && !(area magic == 0)
  crnor 4*crArea234568+eq, 4*cr1+gt, eq
## SirenaHotel(0x07) or Casino(0x0E) ? 59 : 0
  cmpwi r0, 0x07 # SirenaHotel
  cmpwi cr1, r0, 0x0E # Casino
  cror eq, eq, 4*cr1+eq # (NOTE: cannot use rlwnm trick since area may >= 0x20)
  li r0, 59
  beq- .L.handleStickCD.apply
  li r0, 0
.L.handleStickCD.apply:
  sth r0, 0xe4(r12)

.L.handleStickFlag:
## clear 0x2 bit of 0xe2 flag
  lhz r0, 0xe2(r12)
  rlwinm r0, r0, 0, 31, 29
  sth r0, 0xe2(r12)

## set nextGameMode if gpMarDirector == NULL
### nextGameMode = nextArea == (shine select) ? 8 : 5
  ## ep==0xFF ? 3 : 0 (Note: all valid ep id < 0x40)
  ## cr0(eq) == ep!=0xFF
  rlwinm. rNextGameMode, rAns, 32-6, 0x3
  addi rNextGameMode, rNextGameMode, 5

.L.setFader:
## TSMSFader::setFadeStatus(gpApplication.fader, FadedOut(=0))
  lwz r12, 0x34(rApp)
.L.setFader.status:
### this->status = FadedOut(0);
  li r0, 0
  stw r0, 0x20(r12)
.L.setFader.color:
## TSMSFader::setColor(gpApplication.fader, color)
/**
 * (9, *) => white (0xd2d2d2ff)
 * (*, 0xFF) => black (0xff)
 * (2|3|4|5|6|8, *) => white
 * _ => black
 */
### area == 9 => white
  beq crArea9, .L.setFader.color.white
### !(ep != 0xFF) => black (Note: DO NOT DESTROY cr0 UNTIL HERE!)
  bne cr0, .L.setFader.color.black
### area != 2|3|4|5|6|8 => black
  bne crArea234568, .L.setFader.color.black
.L.setFader.color.white:
  li32 r0, 0xd2d2d200
.L.setFader.color.black:
  ori r0, r0, 0xff # r0 = 0 in .L.setFader.status
.L.setFader.color.apply:
  stw r0, 0x18(r12)

### this->color->a = 0xff; (overwritten afterward)
### (this->wipeRequest).type = 0x12;
  li r0, 0x12
  stw r0, 0x24(r12)

.L.done: