pokecrystal-board/engine/menus/save.asm

1002 lines
18 KiB
NASM

SaveMenu:
call LoadStandardMenuHeader
farcall DisplaySaveInfoOnSave
call SpeechTextbox2bpp
call UpdateSprites
farcall CopyTilemapAtOnce
ld hl, WouldYouLikeToSaveTheGameText
call SaveTheGame_yesorno
jr nz, .refused
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call _SavingDontTurnOffThePower
call ResumeGameLogic
call ExitMenu
and a
ret
.refused
call ExitMenu
farcall CopyTilemapAtOnce
scf
ret
AutoSaveGameInOverworld:
call PauseGameLogic
; Prevent joypad interrupts
xor a
ldh [hJoypadReleased], a
ldh [hJoypadPressed], a
ldh [hJoypadSum], a
ldh [hJoypadDown], a
; Signal that we're saving inside a level
ld a, TRUE
ld [wSaveFileInOverworld], a
call SaveGameData
call ResumeGameLogic
ret
AutoSaveGameOutsideOverworld:
call PauseGameLogic
; Prevent joypad interrupts
xor a
ldh [hJoypadReleased], a
ldh [hJoypadPressed], a
ldh [hJoypadSum], a
ldh [hJoypadDown], a
; Signal that we're not saving inside a level
xor a ; FALSE
ld [wSaveFileInOverworld], a
call SaveGameData
call ResumeGameLogic
ret
SaveAfterLinkTrade:
call PauseGameLogic
call SavePokemonData
call SaveChecksum
call SaveBackupPokemonData
call SaveBackupChecksum
farcall BackupPartyMonMail
call ResumeGameLogic
ret
ChangeBoxSaveGame:
push de
ld hl, ChangeBoxSaveText
call MenuTextbox
call YesNoBox
call ExitMenu
jr c, .refused
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call SavingDontTurnOffThePower
call SaveBox
pop de
ld a, e
ld [wCurBox], a
call LoadBox
call SavedTheGame
call ResumeGameLogic
and a
ret
.refused
pop de
ret
Link_SaveGame:
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call _SavingDontTurnOffThePower
call ResumeGameLogic
and a
.refused
ret
MoveMonWOMail_SaveGame:
call PauseGameLogic
push de
call SaveBox
pop de
ld a, e
ld [wCurBox], a
call LoadBox
call ResumeGameLogic
ret
MoveMonWOMail_InsertMon_SaveGame:
call PauseGameLogic
push de
call SaveBox
pop de
ld a, e
ld [wCurBox], a
ld a, TRUE
ld [wSaveFileExists], a
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveChecksum
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupChecksum
farcall BackupPartyMonMail
call LoadBox
call ResumeGameLogic
ld de, SFX_SAVE
call PlaySFX
ld c, 24
call DelayFrames
ret
StartMoveMonWOMail_SaveGame:
ld hl, MoveMonWOMailSaveText
call MenuTextbox
call YesNoBox
call ExitMenu
jr c, .refused
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call _SavingDontTurnOffThePower
call ResumeGameLogic
and a
ret
.refused
scf
ret
PauseGameLogic:
ld a, TRUE
ld [wGameLogicPaused], a
ret
ResumeGameLogic:
xor a ; FALSE
ld [wGameLogicPaused], a
ret
AddHallOfFameEntry:
ld a, BANK(sHallOfFame)
call OpenSRAM
ld hl, sHallOfFame + HOF_LENGTH * (NUM_HOF_TEAMS - 1) - 1
ld de, sHallOfFame + HOF_LENGTH * NUM_HOF_TEAMS - 1
ld bc, HOF_LENGTH * (NUM_HOF_TEAMS - 1)
.loop
ld a, [hld]
ld [de], a
dec de
dec bc
ld a, c
or b
jr nz, .loop
ld hl, wHallOfFamePokemonList
ld de, sHallOfFame
ld bc, wHallOfFamePokemonListEnd - wHallOfFamePokemonList + 1
call CopyBytes
call CloseSRAM
; This vc_hook causes the Virtual Console to set [sMobileEventIndex] and [sMobileEventIndexBackup]
; to MOBILE_EVENT_OBJECT_GS_BALL, which enables you to get the GS Ball, take it to Kurt, and
; encounter Celebi. It assumes that sMobileEventIndex and sMobileEventIndexBackup are at their
; original addresses.
vc_hook Enable_GS_Ball_mobile_event
vc_assert BANK(sMobileEventIndex) == $1 && sMobileEventIndex == $be3c, \
"sMobileEventIndex is no longer located at 01:be3c."
vc_assert BANK(sMobileEventIndexBackup) == $1 && sMobileEventIndexBackup == $be44, \
"sMobileEventIndexBackup is no longer located at 01:be44."
vc_assert MOBILE_EVENT_OBJECT_GS_BALL == $0b, \
"MOBILE_EVENT_OBJECT_GS_BALL is no longer equal to $0b."
ret
AskOverwriteSaveFile:
ld a, [wSaveFileExists]
and a
jr z, .erase
call CompareLoadedAndSavedPlayerID
jr z, .yoursavefile
ld hl, AnotherSaveFileText
call SaveTheGame_yesorno
jr nz, .refused
jr .erase
.yoursavefile
ld hl, AlreadyASaveFileText
call SaveTheGame_yesorno
jr nz, .refused
jr .ok
.erase
call ErasePreviousSave
.ok
and a
ret
.refused
scf
ret
SaveTheGame_yesorno:
ld b, BANK(WouldYouLikeToSaveTheGameText)
call MapTextbox
call LoadMenuTextbox
lb bc, 0, 7
call PlaceYesNoBox
ld a, [wMenuCursorY]
dec a
call CloseWindow
push af
pop af
and a
ret
CompareLoadedAndSavedPlayerID:
ld a, BANK(sPlayerData)
call OpenSRAM
ld hl, sPlayerData + (wPlayerID - wPlayerData)
ld a, [hli]
ld c, [hl]
ld b, a
call CloseSRAM
ld a, [wPlayerID]
cp b
ret nz
ld a, [wPlayerID + 1]
cp c
ret
_SavingDontTurnOffThePower:
call SavingDontTurnOffThePower
SavedTheGame:
ld a, TRUE
ld [wSaveFileInOverworld], a
call SaveGameData
; wait 32 frames
ld c, 32
call DelayFrames
; copy the original text speed setting to the stack
ld a, [wOptions]
push af
; set text speed to medium
ld a, TEXT_DELAY_MED
ld [wOptions], a
; <PLAYER> saved the game!
ld hl, SavedTheGameText
call PrintText1bpp
; restore the original text speed setting
pop af
ld [wOptions], a
ld de, SFX_SAVE
call WaitPlaySFX
call WaitSFX
; wait 30 frames
ld c, 30
call DelayFrames
ret
SaveGameData:
ld a, TRUE
ld [wSaveFileExists], a
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveBox
call SaveChecksum
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupChecksum
call UpdateStackTop
farcall BackupPartyMonMail
ret
UpdateStackTop:
; sStackTop appears to be unused.
; It could have been used to debug stack overflow during saving.
call FindStackTop
ld a, BANK(sStackTop)
call OpenSRAM
ld a, [sStackTop + 0]
ld e, a
ld a, [sStackTop + 1]
ld d, a
or e
jr z, .update
ld a, e
sub l
ld a, d
sbc h
jr c, .done
.update
ld a, l
ld [sStackTop + 0], a
ld a, h
ld [sStackTop + 1], a
.done
call CloseSRAM
ret
FindStackTop:
; Find the furthest point that sp has traversed to.
; This is distinct from the current value of sp.
ld hl, wStackBottom
.loop
ld a, [hl]
or a
ret nz
inc hl
jr .loop
SavingDontTurnOffThePower:
; Prevent joypad interrupts
xor a
ldh [hJoypadReleased], a
ldh [hJoypadPressed], a
ldh [hJoypadSum], a
ldh [hJoypadDown], a
; Save the text speed setting to the stack
ld a, [wOptions]
push af
; Set the text speed to medium
ld a, TEXT_DELAY_MED
ld [wOptions], a
; SAVING... DON'T TURN OFF THE POWER.
ld hl, SavingDontTurnOffThePowerText
call PrintText1bpp
; Restore the text speed setting
pop af
ld [wOptions], a
; Wait for 16 frames
ld c, 16
call DelayFrames
ret
ErasePreviousSave:
call EraseBoxes
call EraseHallOfFame
call EraseLinkBattleStats
ld a, BANK(sStackTop)
call OpenSRAM
xor a
ld [sStackTop + 0], a
ld [sStackTop + 1], a
call CloseSRAM
ld a, $1
ld [wSavedAtLeastOnce], a
ret
EraseLinkBattleStats:
ld a, BANK(sLinkBattleStats)
call OpenSRAM
ld hl, sLinkBattleStats
ld bc, sLinkBattleStatsEnd - sLinkBattleStats
xor a
call ByteFill
jp CloseSRAM
EraseHallOfFame:
ld a, BANK(sHallOfFame)
call OpenSRAM
ld hl, sHallOfFame
ld bc, sHallOfFameEnd - sHallOfFame
xor a
call ByteFill
jp CloseSRAM
HallOfFame_InitSaveIfNeeded:
ld a, [wSavedAtLeastOnce]
and a
ret nz
call ErasePreviousSave
ret
ValidateSave:
ld a, BANK(sCheckValue1) ; aka BANK(sCheckValue2)
call OpenSRAM
ld a, SAVE_CHECK_VALUE_1
ld [sCheckValue1], a
ld a, SAVE_CHECK_VALUE_2
ld [sCheckValue2], a
jp CloseSRAM
SaveOptions:
ld a, BANK(sOptions)
call OpenSRAM
ld hl, wOptions
ld de, sOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
ld a, [wOptions]
and ~(1 << NO_TEXT_SCROLL)
ld [sOptions], a
jp CloseSRAM
SavePlayerData:
ld a, BANK(sPlayerData)
call OpenSRAM
ld hl, wPlayerData
ld de, sPlayerData
ld bc, wPlayerDataEnd - wPlayerData
call CopyBytes
ld hl, wCurMapData
ld de, sCurMapData
ld bc, wCurMapDataEnd - wCurMapData
call CopyBytes
jp CloseSRAM
SavePokemonData:
ld a, BANK(sPokemonData)
call OpenSRAM
ld hl, wPokemonData
ld de, sPokemonData
ld bc, wPokemonDataEnd - wPokemonData
call CopyBytes
call CloseSRAM
ret
SaveBox:
call GetBoxAddress
call SaveBoxAddress
ret
SaveChecksum:
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
ld a, BANK(sGameData)
call OpenSRAM
call Checksum
ld a, e
ld [sChecksum + 0], a
ld a, d
ld [sChecksum + 1], a
call CloseSRAM
ret
ValidateBackupSave:
ld a, BANK(sBackupCheckValue1) ; aka BANK(sBackupCheckValue2)
call OpenSRAM
ld a, SAVE_CHECK_VALUE_1
ld [sBackupCheckValue1], a
ld a, SAVE_CHECK_VALUE_2
ld [sBackupCheckValue2], a
call CloseSRAM
ret
SaveBackupOptions:
ld a, BANK(sBackupOptions)
call OpenSRAM
ld hl, wOptions
ld de, sBackupOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
call CloseSRAM
ret
SaveBackupPlayerData:
ld a, BANK(sBackupPlayerData)
call OpenSRAM
ld hl, wPlayerData
ld de, sBackupPlayerData
ld bc, wPlayerDataEnd - wPlayerData
call CopyBytes
ld hl, wCurMapData
ld de, sBackupCurMapData
ld bc, wCurMapDataEnd - wCurMapData
call CopyBytes
call CloseSRAM
ret
SaveBackupPokemonData:
ld a, BANK(sBackupPokemonData)
call OpenSRAM
ld hl, wPokemonData
ld de, sBackupPokemonData
ld bc, wPokemonDataEnd - wPokemonData
call CopyBytes
call CloseSRAM
ret
SaveBackupChecksum:
ld hl, sBackupGameData
ld bc, sBackupGameDataEnd - sBackupGameData
ld a, BANK(sBackupGameData)
call OpenSRAM
call Checksum
ld a, e
ld [sBackupChecksum + 0], a
ld a, d
ld [sBackupChecksum + 1], a
call CloseSRAM
ret
TryLoadSaveFile:
call VerifyChecksum
jr nz, .backup
call LoadPlayerData
call LoadPokemonData
call LoadBox
farcall RestorePartyMonMail
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupChecksum
and a
ret
.backup
call VerifyBackupChecksum
jr nz, .corrupt
call LoadBackupPlayerData
call LoadBackupPokemonData
call LoadBox
farcall RestorePartyMonMail
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveChecksum
and a
ret
.corrupt
ld a, [wOptions]
push af
set NO_TEXT_SCROLL, a
ld [wOptions], a
ld hl, SaveFileCorruptedText
call PrintText1bpp
pop af
ld [wOptions], a
scf
ret
TryLoadSaveData:
xor a ; FALSE
ld [wSaveFileExists], a
call CheckPrimarySaveFile
ld a, [wSaveFileExists]
and a
jr z, .backup
ld a, BANK(sPlayerData)
call OpenSRAM
ld hl, sPlayerData + wStartDay - wPlayerData
ld de, wStartDay
ld bc, 8
call CopyBytes
ld hl, sPlayerData + wStatusFlags - wPlayerData
ld de, wStatusFlags
ld a, [hl]
ld [de], a
call CloseSRAM
ret
.backup
call CheckBackupSaveFile
ld a, [wSaveFileExists]
and a
jr z, .corrupt
ld a, BANK(sBackupPlayerData)
call OpenSRAM
ld hl, sBackupPlayerData + wStartDay - wPlayerData
ld de, wStartDay
ld bc, 8
call CopyBytes
ld hl, sBackupPlayerData + wStatusFlags - wPlayerData
ld de, wStatusFlags
ld a, [hl]
ld [de], a
call CloseSRAM
ret
.corrupt
ld hl, DefaultOptions
ld de, wOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
ret
INCLUDE "data/default_options.asm"
CheckPrimarySaveFile:
ld a, BANK(sCheckValue1) ; aka BANK(sCheckValue2)
call OpenSRAM
ld a, [sCheckValue1]
cp SAVE_CHECK_VALUE_1
jr nz, .nope
ld a, [sCheckValue2]
cp SAVE_CHECK_VALUE_2
jr nz, .nope
ld hl, sOptions
ld de, wOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
call CloseSRAM
ld a, TRUE
ld [wSaveFileExists], a
.nope
call CloseSRAM
ret
CheckBackupSaveFile:
ld a, BANK(sBackupCheckValue1) ; aka BANK(sBackupCheckValue2)
call OpenSRAM
ld a, [sBackupCheckValue1]
cp SAVE_CHECK_VALUE_1
jr nz, .nope
ld a, [sBackupCheckValue2]
cp SAVE_CHECK_VALUE_2
jr nz, .nope
ld hl, sBackupOptions
ld de, wOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
ld a, $2
ld [wSaveFileExists], a
.nope
call CloseSRAM
ret
LoadPlayerData:
ld a, BANK(sPlayerData)
call OpenSRAM
ld hl, sPlayerData
ld de, wPlayerData
ld bc, wPlayerDataEnd - wPlayerData
call CopyBytes
ld hl, sCurMapData
ld de, wCurMapData
ld bc, wCurMapDataEnd - wCurMapData
call CopyBytes
call CloseSRAM
ret
LoadPokemonData:
ld a, BANK(sPokemonData)
call OpenSRAM
ld hl, sPokemonData
ld de, wPokemonData
ld bc, wPokemonDataEnd - wPokemonData
call CopyBytes
call CloseSRAM
ret
LoadBox:
call GetBoxAddress
call LoadBoxAddress
ret
VerifyChecksum:
ld hl, sGameData
ld bc, sGameDataEnd - sGameData
ld a, BANK(sGameData)
call OpenSRAM
call Checksum
ld a, [sChecksum + 0]
cp e
jr nz, .fail
ld a, [sChecksum + 1]
cp d
.fail
push af
call CloseSRAM
pop af
ret
LoadBackupPlayerData:
ld a, BANK(sBackupPlayerData)
call OpenSRAM
ld hl, sBackupPlayerData
ld de, wPlayerData
ld bc, wPlayerDataEnd - wPlayerData
call CopyBytes
ld hl, sBackupCurMapData
ld de, wCurMapData
ld bc, wCurMapDataEnd - wCurMapData
call CopyBytes
call CloseSRAM
ret
LoadBackupPokemonData:
ld a, BANK(sBackupPokemonData)
call OpenSRAM
ld hl, sBackupPokemonData
ld de, wPokemonData
ld bc, wPokemonDataEnd - wPokemonData
call CopyBytes
call CloseSRAM
ret
VerifyBackupChecksum:
ld hl, sBackupGameData
ld bc, sBackupGameDataEnd - sBackupGameData
ld a, BANK(sBackupGameData)
call OpenSRAM
call Checksum
ld a, [sBackupChecksum + 0]
cp e
jr nz, .fail
ld a, [sBackupChecksum + 1]
cp d
.fail
push af
call CloseSRAM
pop af
ret
GetBoxAddress:
ld a, [wCurBox]
cp NUM_BOXES
jr c, .ok
xor a
ld [wCurBox], a
.ok
ld e, a
ld d, 0
ld hl, BoxAddresses
rept 5
add hl, de
endr
ld a, [hli]
push af
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, [hli]
ld h, [hl]
ld l, a
pop af
ret
SaveBoxAddress:
; Save box via wBoxPartialData.
; We do this in three steps because the size of wBoxPartialData is less than
; the size of sBox.
push hl
; Load the first part of the active box.
push af
push de
ld a, BANK(sBox)
call OpenSRAM
ld hl, sBox
ld de, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
pop de
pop af
; Save it to the target box.
push af
push de
call OpenSRAM
ld hl, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
; Load the second part of the active box.
ld a, BANK(sBox)
call OpenSRAM
ld hl, sBox + (wBoxPartialDataEnd - wBoxPartialData)
ld de, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
pop de
pop af
ld hl, (wBoxPartialDataEnd - wBoxPartialData)
add hl, de
ld e, l
ld d, h
; Save it to the next part of the target box.
push af
push de
call OpenSRAM
ld hl, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
; Load the third and final part of the active box.
ld a, BANK(sBox)
call OpenSRAM
ld hl, sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2
ld de, wBoxPartialData
ld bc, sBoxEnd - (sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2) ; $8e
call CopyBytes
call CloseSRAM
pop de
pop af
ld hl, (wBoxPartialDataEnd - wBoxPartialData)
add hl, de
ld e, l
ld d, h
; Save it to the final part of the target box.
call OpenSRAM
ld hl, wBoxPartialData
ld bc, sBoxEnd - (sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2) ; $8e
call CopyBytes
call CloseSRAM
pop hl
ret
LoadBoxAddress:
; Load box via wBoxPartialData.
; We do this in three steps because the size of wBoxPartialData is less than
; the size of sBox.
push hl
ld l, e
ld h, d
; Load part 1
push af
push hl
call OpenSRAM
ld de, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
ld a, BANK(sBox)
call OpenSRAM
ld hl, wBoxPartialData
ld de, sBox
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
pop hl
pop af
ld de, (wBoxPartialDataEnd - wBoxPartialData)
add hl, de
; Load part 2
push af
push hl
call OpenSRAM
ld de, wBoxPartialData
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
ld a, BANK(sBox)
call OpenSRAM
ld hl, wBoxPartialData
ld de, sBox + (wBoxPartialDataEnd - wBoxPartialData)
ld bc, (wBoxPartialDataEnd - wBoxPartialData)
call CopyBytes
call CloseSRAM
pop hl
pop af
; Load part 3
ld de, (wBoxPartialDataEnd - wBoxPartialData)
add hl, de
call OpenSRAM
ld de, wBoxPartialData
ld bc, sBoxEnd - (sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2) ; $8e
call CopyBytes
call CloseSRAM
ld a, BANK(sBox)
call OpenSRAM
ld hl, wBoxPartialData
ld de, sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2
ld bc, sBoxEnd - (sBox + (wBoxPartialDataEnd - wBoxPartialData) * 2) ; $8e
call CopyBytes
call CloseSRAM
pop hl
ret
EraseBoxes:
ld hl, BoxAddresses
ld c, NUM_BOXES
.next
push bc
ld a, [hli]
call OpenSRAM
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
xor a
ld [de], a
inc de
ld a, -1
ld [de], a
inc de
ld bc, sBoxEnd - (sBox + 2)
.clear
xor a
ld [de], a
inc de
dec bc
ld a, b
or c
jr nz, .clear
ld a, [hli]
ld e, a
ld a, [hli]
ld d, a
ld a, -1
ld [de], a
inc de
xor a
ld [de], a
call CloseSRAM
pop bc
dec c
jr nz, .next
ret
BoxAddresses:
table_width 5, BoxAddresses
for n, 1, NUM_BOXES + 1
db BANK(sBox{d:n}) ; aka BANK(sBox{d:n}End)
dw sBox{d:n}, sBox{d:n}End
endr
assert_table_length NUM_BOXES
Checksum:
ld de, 0
.loop
ld a, [hli]
add e
ld e, a
ld a, 0
adc d
ld d, a
dec bc
ld a, b
or c
jr nz, .loop
ret
WouldYouLikeToSaveTheGameText:
text_far _WouldYouLikeToSaveTheGameText
text_end
SavingDontTurnOffThePowerText:
text_far _SavingDontTurnOffThePowerText
text_end
SavedTheGameText:
text_far _SavedTheGameText
text_end
AlreadyASaveFileText:
text_far _AlreadyASaveFileText
text_end
AnotherSaveFileText:
text_far _AnotherSaveFileText
text_end
SaveFileCorruptedText:
text_far _SaveFileCorruptedText
text_end
ChangeBoxSaveText:
text_far _ChangeBoxSaveText
text_end
MoveMonWOMailSaveText:
text_far _MoveMonWOMailSaveText
text_end