jep-hack/engine/menus/save.asm
Llinos Evans 2a14a32533 Stereo and Fast text are now default options
Making Stereo the default was weirdly taxing. I don't know how or where this is usually initialised, I searched for like 2 hours and came up empty-handed...so I made a virus in the intro sequence instead. Tested with an empty save file.

Fast text was just a case of adding it to defaultoptions. I have no idea how or why this worked, as it only seems to be called when save files corrupt, and adding the stereo bit there didn't let it work in the same way. Oh well...
2025-01-03 16:45:44 +00:00

1393 lines
25 KiB
NASM

SaveMenu:
call LoadStandardMenuHeader
farcall DisplaySaveInfoOnSave
call SpeechTextbox
call UpdateSprites
farcall SaveMenu_CopyTilemapAtOnce
ld hl, WouldYouLikeToSaveTheGameText
call SaveTheGame_yesorno
jr nz, .refused
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call SavedTheGame
call ResumeGameLogic
call ExitMenu
and a
ret
.refused
call ExitMenu
farcall SaveMenu_CopyTilemapAtOnce
scf
ret
SaveAfterLinkTrade:
call PauseGameLogic
farcall StageRTCTimeForSave
farcall BackupMysteryGift
call SavePokemonData
call SaveIndexTables
call SaveChecksum
call SaveBackupPokemonData
call SaveBackupIndexTables
call SaveBackupChecksum
farcall BackupPartyMonMail
farcall SaveRTC
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 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 SavedTheGame
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
farcall StageRTCTimeForSave
farcall BackupMysteryGift
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveIndexTables
call SaveChecksum
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupIndexTables
call SaveBackupChecksum
farcall BackupPartyMonMail
farcall BackupMobileEventIndex
farcall SaveRTC
call LoadBox
call ResumeGameLogic
ld de, SFX_SAVE
jp PlaySFX
StartMoveMonWOMail_SaveGame:
ld hl, MoveMonWOMailSaveText
call MenuTextbox
call YesNoBox
call ExitMenu
jr c, .refused
call AskOverwriteSaveFile
jr c, .refused
call PauseGameLogic
call SavedTheGame
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, HOF_LENGTH
call CopyBytes
call CloseSRAM
ret
AskOverwriteSaveFile:
ld a, [wSaveFileExists]
and a
jr z, .erase
call CompareLoadedAndSavedPlayerID
ret z
ld hl, AnotherSaveFileText
call SaveTheGame_yesorno
jr nz, .refused
.erase
call ErasePreviousSave
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
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
SavedTheGame:
ld hl, wOptions
set NO_TEXT_SCROLL, [hl]
push hl
ld hl, .saving_text
call PrintText
pop hl
res NO_TEXT_SCROLL, [hl]
call SaveGameData
; <PLAYER> saved the game!
ld hl, SavedTheGameText
call PrintText
ld de, SFX_SAVE
call WaitPlaySFX
jp WaitSFX
.saving_text
text "Saving…"
done
SaveGameData:
ld a, TRUE
ld [wSaveFileExists], a
farcall StageRTCTimeForSave
farcall BackupMysteryGift
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveIndexTables
call SaveBox
call SaveChecksum
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupIndexTables
call SaveBackupChecksum
call UpdateStackTop
farcall BackupPartyMonMail
farcall BackupMobileEventIndex
farcall SaveRTC
ld a, BANK(sBattleTowerChallengeState)
call OpenSRAM
ld a, [sBattleTowerChallengeState]
cp BATTLETOWER_RECEIVED_REWARD
jr nz, .ok
xor a
ld [sBattleTowerChallengeState], a
.ok
call CloseSRAM
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
ErasePreviousSave:
call EraseBoxes
call EraseHallOfFame
call EraseLinkBattleStats
call EraseMysteryGift
call SaveData
call EraseBattleTowerStatus
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
EraseMysteryGift:
ld a, BANK(sBackupMysteryGiftItem)
call OpenSRAM
ld hl, sBackupMysteryGiftItem
ld bc, sBackupMysteryGiftItemEnd - sBackupMysteryGiftItem
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
InitDefaultEZChatMsgs: ; unreferenced
ld a, BANK(sEZChatMessages) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
ld hl, .Data
ld de, sEZChatMessages
ld bc, EASY_CHAT_MESSAGE_LENGTH * 4
call CopyBytes
jp CloseSRAM
.Data:
; introduction
db $0d, EZCHAT_GREETINGS, $00, EZCHAT_EXCLAMATIONS, $00, EZCHAT_POKEMON
db $22, EZCHAT_GREETINGS, $01, EZCHAT_EXCLAMATIONS, $00, EZCHAT_POKEMON
; begin battle
db $03, EZCHAT_BATTLE, $05, EZCHAT_CONDITIONS, $03, EZCHAT_EXCLAMATIONS
db $0e, EZCHAT_CONVERSATION, $03, EZCHAT_GREETINGS, $00, EZCHAT_POKEMON
; win battle
db $39, EZCHAT_FEELINGS, $07, EZCHAT_BATTLE, $00, EZCHAT_EXCLAMATIONS
db $04, EZCHAT_FEELINGS, $01, EZCHAT_EXCLAMATIONS, $00, EZCHAT_POKEMON
; lose battle
db $0f, EZCHAT_EXCLAMATIONS, $14, EZCHAT_FEELINGS, $05, EZCHAT_EXCLAMATIONS
db $11, EZCHAT_TIME, $0c, EZCHAT_CONVERSATION, $06, EZCHAT_BATTLE
EraseBattleTowerStatus:
ld a, BANK(sBattleTowerChallengeState)
call OpenSRAM
xor a
ld [sBattleTowerChallengeState], a
jp CloseSRAM
SaveData:
call _SaveData
ret
; Do not delete, it's likely to be used sometime.
Function14d6c: ; unreferenced
ld a, BANK(s4_a60b) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
ld a, [s4_a60b] ; address of MBC30 bank
ld b, $0
and a
jr z, .ok
ld b, $2
.ok
ld a, b
ld [s4_a60b], a ; address of MBC30 bank
call CloseSRAM
ret
; Same here.
Function14d83: ; unreferenced
ld a, BANK(s4_a60c) ; aka BANK(s4_a60d) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
xor a
ld [s4_a60c], a ; address of MBC30 bank
ld [s4_a60d], a ; address of MBC30 bank
call CloseSRAM
ret
; yep
Function14d93: ; unreferenced
ld a, BANK(s7_a000) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
xor a
ld [s7_a000], a ; address of MBC30 bank
call CloseSRAM
ret
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
SaveIndexTables:
; saving is already a long operation, so take the chance to GC the table
farcall ForceGarbageCollection
ldh a, [rSVBK]
push af
ld a, BANK("16-bit WRAM tables")
ldh [rSVBK], a
ld a, BANK(sPokemonIndexTable)
call OpenSRAM
ld hl, wPokemonIndexTable
ld de, sPokemonIndexTable
ld bc, wPokemonIndexTableEnd - wPokemonIndexTable
call CopyBytes
ld a, BANK(sMoveIndexTable)
call OpenSRAM
ld hl, wMoveIndexTable
ld de, sMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call CopyBytes
pop af
ldh [rSVBK], a
jp CloseSRAM
SaveBox:
call GetBoxAddress
push de
push af
call SaveBoxAddress
pop af
call OpenSRAM
pop hl
call ComputeSavedBoxIndexTable
call GetBoxPokemonIndexesAddress
call OpenSRAM
ld d, h
ld e, l
ld hl, wBoxPartialData
ld bc, 2 * MONS_PER_BOX
call CopyBytes
call GetBoxAddress
ld b, a
ld c, 0
farcall BillsPC_ConvertBoxData
jp CloseSRAM
SaveChecksum:
ld a, BANK(sMoveIndexTable)
call OpenSRAM
ld hl, sMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call Checksum
ld a, BANK(sSaveData)
call OpenSRAM
ld hl, sConversionTableChecksum
ld a, e
ld [hli], a
ld [hl], d
ld hl, sSaveData
ld bc, sSaveDataEnd - sSaveData
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
SaveBackupIndexTables:
ld a, BANK(sBackupPokemonIndexTable)
call OpenSRAM
ldh a, [rSVBK]
push af
ld a, BANK("16-bit WRAM tables")
ldh [rSVBK], a
ld hl, wPokemonIndexTable
ld de, sBackupPokemonIndexTable
ld bc, wPokemonIndexTableEnd - wPokemonIndexTable
call CopyBytes
ld a, BANK(sBackupMoveIndexTable)
call OpenSRAM
ld hl, wMoveIndexTable
ld de, sBackupMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call CopyBytes
pop af
ldh [rSVBK], a
jp CloseSRAM
SaveBackupChecksum:
ld a, BANK(sBackupMoveIndexTable)
call OpenSRAM
ld hl, sBackupMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call Checksum
ld a, BANK(sBackupSaveData)
call OpenSRAM
ld hl, sBackupConversionTableChecksum
ld a, e
ld [hli], a
ld [hl], d
ld hl, sBackupSaveData
ld bc, sBackupSaveDataEnd - sBackupSaveData
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 LoadIndexTables
call LoadBox
farcall RestorePartyMonMail
farcall RestoreMobileEventIndex
farcall RestoreMysteryGift
call ValidateBackupSave
call SaveBackupOptions
call SaveBackupPlayerData
call SaveBackupPokemonData
call SaveBackupIndexTables
call SaveBackupChecksum
and a
ret
.backup
call VerifyBackupChecksum
jr nz, .corrupt
call LoadBackupPlayerData
call LoadBackupPokemonData
call LoadBackupIndexTables
call LoadBox
farcall RestorePartyMonMail
farcall RestoreMobileEventIndex
farcall RestoreMysteryGift
call ValidateSave
call SaveOptions
call SavePlayerData
call SavePokemonData
call SaveIndexTables
call SaveChecksum
and a
ret
.corrupt
ld a, [wOptions]
push af
set NO_TEXT_SCROLL, a
ld [wOptions], a
ld hl, SaveFileCorruptedText
call PrintText
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, wOptions
set STEREO, [hl]
ld hl, DefaultOptions
ld de, wOptions
ld bc, wOptionsEnd - wOptions
call CopyBytes
call ClearClock
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
ld a, BANK(sBattleTowerChallengeState)
call OpenSRAM
ld a, [sBattleTowerChallengeState]
cp BATTLETOWER_RECEIVED_REWARD
jr nz, .not_4
ld a, BATTLETOWER_WON_CHALLENGE
ld [sBattleTowerChallengeState], a
.not_4
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
LoadIndexTables:
ldh a, [rSVBK]
push af
ld a, BANK("16-bit WRAM tables")
ldh [rSVBK], a
ld a, BANK(sPokemonIndexTable)
call OpenSRAM
ld hl, sPokemonIndexTable
ld de, wPokemonIndexTable
ld bc, wPokemonIndexTableEnd - wPokemonIndexTable
call CopyBytes
ld a, BANK(sMoveIndexTable)
call OpenSRAM
ld hl, sMoveIndexTable
ld de, wMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call CopyBytes
pop af
ldh [rSVBK], a
jp CloseSRAM
LoadBox:
call GetBoxAddress
call LoadBoxAddress
lb bc, BANK(sBox), 1
ld de, sBox
farcall BillsPC_ConvertBoxData
call GetBoxPokemonIndexesAddress
call OpenSRAM
ld de, wBoxPartialData
ld bc, 2 * MONS_PER_BOX
call CopyBytes
ld a, BANK(sBox)
call OpenSRAM
call ClearIndexesForLoadedBox
; GC the table now that lots of entries are free
farcall ForceGarbageCollection
call UpdateIndexesForLoadedBox
jp CloseSRAM
VerifyChecksum:
ld hl, sSaveData
ld bc, sSaveDataEnd - sSaveData
ld a, BANK(sSaveData)
call OpenSRAM
call Checksum
ld a, [sChecksum + 0]
cp e
jr nz, .fail
ld a, [sChecksum + 1]
cp d
jr nz, .fail
ld hl, sConversionTableChecksum
ld a, [hli]
ld h, [hl]
ld l, a
push hl
ld a, BANK(sMoveIndexTable)
call OpenSRAM
ld hl, sMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call Checksum
pop hl
ld a, d
cp h
jr nz, .fail
ld a, e
cp l
.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
LoadBackupIndexTables:
ldh a, [rSVBK]
push af
ld a, BANK("16-bit WRAM tables")
ldh [rSVBK], a
ld a, BANK(sBackupPokemonIndexTable)
call OpenSRAM
ld hl, sBackupPokemonIndexTable
ld de, wPokemonIndexTable
ld bc, wPokemonIndexTableEnd - wPokemonIndexTable
call CopyBytes
ld a, BANK(sBackupMoveIndexTable)
call OpenSRAM
ld hl, sBackupMoveIndexTable
ld de, wMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call CopyBytes
pop af
ldh [rSVBK], a
jp CloseSRAM
VerifyBackupChecksum:
ld hl, sBackupSaveData
ld bc, sBackupSaveDataEnd - sBackupSaveData
ld a, BANK(sBackupSaveData)
call OpenSRAM
call Checksum
ld a, [sBackupChecksum + 0]
cp e
jr nz, .fail
ld a, [sBackupChecksum + 1]
cp d
jr nz, .fail
ld hl, sBackupConversionTableChecksum
ld a, [hli]
ld h, [hl]
ld l, a
push hl
ld a, BANK(sBackupMoveIndexTable)
call OpenSRAM
ld hl, sBackupMoveIndexTable
ld bc, wMoveIndexTableEnd - wMoveIndexTable
call Checksum
pop hl
ld a, d
cp h
jr nz, .fail
ld a, e
cp l
.fail
push af
call CloseSRAM
pop af
ret
_SaveData:
; This is called within two scenarios:
; a) ErasePreviousSave (the process of erasing the save from a previous game file)
; b) unused mobile functionality
; It is not part of a regular save.
ld a, BANK(sCrystalData)
call OpenSRAM
ld hl, wCrystalData
ld de, sCrystalData
ld bc, wCrystalDataEnd - wCrystalData
call CopyBytes
; This block originally had some mobile functionality, but since we're still in
; BANK(sCrystalData), it instead overwrites the sixteen wEventFlags starting at 1:s4_a60e with
; garbage from wd479. This isn't an issue, since ErasePreviousSave is followed by a regular
; save that unwrites the garbage.
ld hl, wd479
ld a, [hli]
ld [s4_a60e + 0], a
ld a, [hli]
ld [s4_a60e + 1], a
jp CloseSRAM
_LoadData:
ld a, BANK(sCrystalData)
call OpenSRAM
ld hl, sCrystalData
ld de, wCrystalData
ld bc, wCrystalDataEnd - wCrystalData
call CopyBytes
; This block originally had some mobile functionality to mirror _SaveData above, but instead it
; (harmlessly) writes the aforementioned wEventFlags to the unused wd479.
ld hl, wd479
ld a, [s4_a60e + 0]
ld [hli], a
ld a, [s4_a60e + 1]
ld [hli], a
jp CloseSRAM
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
GetBoxPokemonIndexesAddress:
ld a, [wCurBox]
ld e, a
ld d, 0
ld hl, BoxAddresses + 5 * NUM_BOXES
add hl, de
add hl, de
add hl, de
ld a, [hli]
push af
ld a, [hli]
ld h, [hl]
ld l, a
pop af
ret
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
ComputeSavedBoxIndexTable:
push hl
ld a, [hl]
ld de, wBoxPartialData
and a
jr z, .empty_box
cp MONS_PER_BOX
jr c, .valid_count
ld a, MONS_PER_BOX
.valid_count
ld bc, sBoxMons - sBox
add hl, bc
ld [wTempLoopCounter], a
ld c, BOXMON_STRUCT_LENGTH
.loop
ld a, [hl]
add hl, bc
push hl
call GetPokemonIndexFromID
ld a, l
ld [de], a
inc de
ld a, h
ld [de], a
inc de
ld hl, wTempLoopCounter
dec [hl]
pop hl
jr nz, .loop
.empty_box
pop hl
ld a, MONS_PER_BOX
sub [hl]
ret c
add a, a
ld h, d
ld l, e
ld c, a
xor a
ld b, a
jp ByteFill
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
ClearIndexesForLoadedBox:
ld hl, sBoxMon1Species
ld bc, BOXMON_STRUCT_LENGTH
ld a, MONS_PER_BOX
.loop
ld [hl], 0
add hl, bc
dec a
jr nz, .loop
ret
UpdateIndexesForLoadedBox:
ld de, sBox
ld a, [de]
cp MONS_PER_BOX
jr c, .count_OK
ld a, MONS_PER_BOX
ld [de], a
.count_OK
inc de
and a
jr z, .done
ld [wTempLoopCounter], a
ld bc, sBoxMon1Species
ld hl, wBoxPartialData - 1
.loop
inc hl
ld a, [hli]
push hl
ld h, [hl]
ld l, a
call GetPokemonIDFromIndex
ld [bc], a
ld a, [de]
cp EGG
jr z, .is_egg
ld a, [bc]
ld [de], a
.is_egg
ld hl, BOXMON_STRUCT_LENGTH
add hl, bc
ld b, h
ld c, l
inc de
ld hl, wTempLoopCounter
dec [hl]
pop hl
jr nz, .loop
.done
ld a, -1
ld [de], a
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
ld e, NUM_BOXES
.index_loop
ld a, [hli]
call OpenSRAM
ld a, [hli]
ld b, a
ld a, [hli]
push hl
ld h, a
ld l, b
xor a
ld bc, 2 * MONS_PER_BOX
call ByteFill
pop hl
dec e
jr nz, .index_loop
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
; index addresses
BoxIndexAddresses:
table_width 3, BoxIndexAddresses
dba sBox1PokemonIndexes
dba sBox2PokemonIndexes
dba sBox3PokemonIndexes
dba sBox4PokemonIndexes
dba sBox5PokemonIndexes
dba sBox6PokemonIndexes
dba sBox7PokemonIndexes
dba sBox8PokemonIndexes
dba sBox9PokemonIndexes
dba sBox10PokemonIndexes
dba sBox11PokemonIndexes
dba sBox12PokemonIndexes
dba sBox13PokemonIndexes
dba sBox14PokemonIndexes
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
SavedTheGameText:
text_far _SavedTheGameText
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