2017-09-03 14:13:01 +02:00
# include "global.h"
2019-03-02 09:18:08 +01:00
# include "agb_flash.h"
2017-09-03 14:13:01 +02:00
# include "gba/flash_internal.h"
2019-03-02 09:18:08 +01:00
# include "fieldmap.h"
2017-09-03 14:13:01 +02:00
# include "save.h"
2017-09-29 10:06:36 +02:00
# include "task.h"
2018-02-15 23:54:34 +01:00
# include "decompress.h"
2018-04-29 14:21:59 +02:00
# include "load_save.h"
# include "overworld.h"
2018-12-20 22:53:08 +01:00
# include "pokemon_storage_system.h"
2018-11-18 19:37:18 +01:00
# include "main.h"
2019-01-13 13:15:23 +01:00
# include "trainer_hill.h"
2019-03-02 09:18:08 +01:00
# include "link.h"
2018-11-18 19:37:18 +01:00
# include "constants/game_stat.h"
2021-10-29 04:54:41 +02:00
static u16 CalculateChecksum ( void * , u16 ) ;
static bool8 ReadFlashSector ( u8 , struct SaveSector * ) ;
static u8 GetSaveValidStatus ( const struct SaveSectorLocation * ) ;
static u8 CopySaveSlotData ( u16 , struct SaveSectorLocation * ) ;
static u8 TryWriteSector ( u8 , u8 * ) ;
static u8 HandleWriteSector ( u16 , const struct SaveSectorLocation * ) ;
static u8 HandleReplaceSector ( u16 , const struct SaveSectorLocation * ) ;
2017-09-03 14:13:01 +02:00
2018-01-31 21:26:57 +01:00
// Divide save blocks into individual chunks to be written to flash sectors
/*
* Sector Layout :
2018-04-29 14:21:59 +02:00
*
2018-01-31 21:26:57 +01:00
* Sectors 0 - 13 : Save Slot 1
* Sectors 14 - 27 : Save Slot 2
* Sectors 28 - 29 : Hall of Fame
2018-11-18 19:37:18 +01:00
* Sector 30 : Trainer Hill
2018-01-31 23:12:46 +01:00
* Sector 31 : Recorded Battle
2018-04-29 14:21:59 +02:00
*
2018-01-31 21:26:57 +01:00
* There are two save slots for saving the player ' s game data . We alternate between
* them each time the game is saved , so that if the current save slot is corrupt ,
* we can load the previous one . We also rotate the sectors in each save slot
* so that the same data is not always being written to the same sector . This
* might be done to reduce wear on the flash memory , but I ' m not sure , since all
* 14 sectors get written anyway .
2021-10-29 04:54:41 +02:00
*
* See SECTOR_ID_ * constants in save . h
2018-01-31 21:26:57 +01:00
*/
2021-10-13 19:19:15 +02:00
# define SAVEBLOCK_CHUNK(structure, chunkNum) \
{ \
chunkNum * SECTOR_DATA_SIZE , \
sizeof ( structure ) > = chunkNum * SECTOR_DATA_SIZE ? \
min ( sizeof ( structure ) - chunkNum * SECTOR_DATA_SIZE , SECTOR_DATA_SIZE ) : 0 \
}
2018-01-31 21:26:57 +01:00
2021-10-29 04:54:41 +02:00
struct
{
u16 offset ;
u16 size ;
} static const sSaveSlotLayout [ NUM_SECTORS_PER_SLOT ] =
2018-01-31 21:26:57 +01:00
{
2021-11-14 05:19:57 +01:00
SAVEBLOCK_CHUNK ( struct SaveBlock2 , 0 ) , // SECTOR_ID_SAVEBLOCK2
2018-01-31 21:26:57 +01:00
2021-11-14 05:19:57 +01:00
SAVEBLOCK_CHUNK ( struct SaveBlock1 , 0 ) , // SECTOR_ID_SAVEBLOCK1_START
2021-11-14 03:41:16 +01:00
SAVEBLOCK_CHUNK ( struct SaveBlock1 , 1 ) ,
SAVEBLOCK_CHUNK ( struct SaveBlock1 , 2 ) ,
2021-11-14 05:19:57 +01:00
SAVEBLOCK_CHUNK ( struct SaveBlock1 , 3 ) , // SECTOR_ID_SAVEBLOCK1_END
2018-01-31 21:26:57 +01:00
2021-11-14 05:19:57 +01:00
SAVEBLOCK_CHUNK ( struct PokemonStorage , 0 ) , // SECTOR_ID_PKMN_STORAGE_START
2021-11-14 03:41:16 +01:00
SAVEBLOCK_CHUNK ( struct PokemonStorage , 1 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 2 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 3 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 4 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 5 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 6 ) ,
SAVEBLOCK_CHUNK ( struct PokemonStorage , 7 ) ,
2021-11-14 05:19:57 +01:00
SAVEBLOCK_CHUNK ( struct PokemonStorage , 8 ) , // SECTOR_ID_PKMN_STORAGE_END
2018-01-31 21:26:57 +01:00
} ;
2017-09-03 15:39:33 +02:00
u16 gLastWrittenSector ;
u32 gLastSaveCounter ;
u16 gLastKnownGoodSector ;
u32 gDamagedSaveSectors ;
u32 gSaveCounter ;
2021-10-29 04:54:41 +02:00
struct SaveSector * gReadWriteSector ; // Pointer to a buffer for reading/writing a sector
u16 gIncrementalSectorId ;
2017-09-03 15:39:33 +02:00
u16 gSaveUnusedVar ;
2017-09-03 22:50:17 +02:00
u16 gSaveFileStatus ;
2017-09-03 15:39:33 +02:00
void ( * gGameContinueCallback ) ( void ) ;
2021-10-29 04:54:41 +02:00
struct SaveSectorLocation gRamSaveSectorLocations [ NUM_SECTORS_PER_SLOT ] ;
2018-09-10 01:01:39 +02:00
u16 gSaveUnusedVar2 ;
2020-01-12 21:27:37 +01:00
u16 gSaveAttemptStatus ;
2017-09-03 15:39:33 +02:00
2021-10-29 04:54:41 +02:00
EWRAM_DATA struct SaveSector gSaveDataBuffer = { 0 } ; // Buffer used for reading/writing sectors
2018-11-19 01:03:14 +01:00
EWRAM_DATA static u8 sUnusedVar = 0 ;
2017-09-03 14:13:01 +02:00
void ClearSaveData ( void )
{
u16 i ;
2021-10-19 22:30:00 +02:00
// Clear the full save two sectors at a time
for ( i = 0 ; i < SECTORS_COUNT / 2 ; i + + )
2017-09-03 14:13:01 +02:00
{
EraseFlashSector ( i ) ;
2021-10-19 22:30:00 +02:00
EraseFlashSector ( i + SECTORS_COUNT / 2 ) ;
2017-09-03 14:13:01 +02:00
}
}
2018-02-15 23:54:34 +01:00
void Save_ResetSaveCounters ( void )
2017-09-03 14:13:01 +02:00
{
gSaveCounter = 0 ;
gLastWrittenSector = 0 ;
gDamagedSaveSectors = 0 ;
}
2021-10-29 04:54:41 +02:00
static bool32 SetDamagedSectorBits ( u8 op , u8 sectorId )
2017-09-03 14:13:01 +02:00
{
bool32 retVal = FALSE ;
switch ( op )
{
case ENABLE :
2021-10-29 04:54:41 +02:00
gDamagedSaveSectors | = ( 1 < < sectorId ) ;
2017-09-03 14:13:01 +02:00
break ;
case DISABLE :
2021-10-29 04:54:41 +02:00
gDamagedSaveSectors & = ~ ( 1 < < sectorId ) ;
2017-09-03 14:13:01 +02:00
break ;
case CHECK : // unused
2021-10-29 04:54:41 +02:00
if ( gDamagedSaveSectors & ( 1 < < sectorId ) )
2017-09-03 14:13:01 +02:00
retVal = TRUE ;
break ;
}
return retVal ;
}
2021-10-29 04:54:41 +02:00
static u8 WriteSaveSectorOrSlot ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
u32 status ;
2017-09-03 14:13:01 +02:00
u16 i ;
2021-10-29 04:54:41 +02:00
gReadWriteSector = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
if ( sectorId ! = FULL_SAVE_SLOT )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// A sector was specified, just write that sector.
// This is never reached, FULL_SAVE_SLOT is always used instead.
status = HandleWriteSector ( sectorId , locations ) ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// No sector was specified, write full save slot.
2017-09-03 14:13:01 +02:00
gLastKnownGoodSector = gLastWrittenSector ; // backup the current written sector before attempting to write.
gLastSaveCounter = gSaveCounter ;
gLastWrittenSector + + ;
2021-10-29 04:54:41 +02:00
gLastWrittenSector = gLastWrittenSector % NUM_SECTORS_PER_SLOT ;
2017-09-03 14:13:01 +02:00
gSaveCounter + + ;
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
2021-10-19 22:30:00 +02:00
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
2021-10-29 04:54:41 +02:00
HandleWriteSector ( i , locations ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
if ( gDamagedSaveSectors )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// At least one sector save failed
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
}
2020-01-12 21:27:37 +01:00
return status ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
static u8 HandleWriteSector ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 sector ;
u8 * data ;
u16 size ;
2021-10-29 04:54:41 +02:00
// Adjust sector id for current save slot
2020-01-12 21:27:37 +01:00
sector = sectorId + gLastWrittenSector ;
2021-10-19 22:30:00 +02:00
sector % = NUM_SECTORS_PER_SLOT ;
sector + = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Get current save data
data = locations [ sectorId ] . data ;
size = locations [ sectorId ] . size ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Clear temp save sector
for ( i = 0 ; i < SECTOR_SIZE ; i + + )
( ( u8 * ) gReadWriteSector ) [ i ] = 0 ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Set footer data
gReadWriteSector - > id = sectorId ;
gReadWriteSector - > security = SECTOR_SECURITY_NUM ;
gReadWriteSector - > counter = gSaveCounter ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Copy current data to temp buffer for writing
2017-09-03 14:13:01 +02:00
for ( i = 0 ; i < size ; i + + )
2021-10-29 04:54:41 +02:00
gReadWriteSector - > data [ i ] = data [ i ] ;
gReadWriteSector - > checksum = CalculateChecksum ( data , size ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
return TryWriteSector ( sector , gReadWriteSector - > data ) ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
static u8 HandleWriteSectorNBytes ( u8 sectorId , u8 * data , u16 size )
2017-09-03 14:13:01 +02:00
{
u16 i ;
2021-10-29 04:54:41 +02:00
struct SaveSector * sector = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Clear temp save sector
for ( i = 0 ; i < SECTOR_SIZE ; i + + )
( ( u8 * ) sector ) [ i ] = 0 ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
sector - > security = SECTOR_SECURITY_NUM ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Copy data to temp buffer for writing
2017-09-03 14:13:01 +02:00
for ( i = 0 ; i < size ; i + + )
2021-10-29 04:54:41 +02:00
sector - > data [ i ] = data [ i ] ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
sector - > id = CalculateChecksum ( data , size ) ; // though this appears to be incorrect, it might be some sector checksum instead of a whole save checksum and only appears to be relevent to HOF data, if used.
return TryWriteSector ( sectorId , sector - > data ) ;
2017-09-03 14:13:01 +02:00
}
2018-11-18 19:37:18 +01:00
static u8 TryWriteSector ( u8 sector , u8 * data )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
if ( ProgramFlashSectorAndVerify ( sector , data ) ) // is damaged?
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Failed
SetDamagedSectorBits ( ENABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Succeeded
SetDamagedSectorBits ( DISABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
}
2021-10-29 04:54:41 +02:00
static u32 RestoreSaveBackupVarsAndIncrement ( const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
gReadWriteSector = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
gLastKnownGoodSector = gLastWrittenSector ;
gLastSaveCounter = gSaveCounter ;
gLastWrittenSector + + ;
2021-10-19 22:30:00 +02:00
gLastWrittenSector % = NUM_SECTORS_PER_SLOT ;
2017-09-03 14:13:01 +02:00
gSaveCounter + + ;
2021-10-29 04:54:41 +02:00
gIncrementalSectorId = 0 ;
2017-09-03 14:13:01 +02:00
gDamagedSaveSectors = 0 ;
return 0 ;
}
2021-10-29 04:54:41 +02:00
static u32 RestoreSaveBackupVars ( const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
gReadWriteSector = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
gLastKnownGoodSector = gLastWrittenSector ;
gLastSaveCounter = gSaveCounter ;
2021-10-29 04:54:41 +02:00
gIncrementalSectorId = 0 ;
2017-09-03 14:13:01 +02:00
gDamagedSaveSectors = 0 ;
return 0 ;
}
2021-10-29 04:54:41 +02:00
static u8 HandleWriteIncrementalSector ( u16 numSectors , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
u8 status ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
if ( gIncrementalSectorId < numSectors - 1 )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_OK ;
2021-10-29 04:54:41 +02:00
HandleWriteSector ( gIncrementalSectorId , locations ) ;
gIncrementalSectorId + + ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
{
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
}
else
{
2021-11-13 23:22:01 +01:00
// Exceeded max sector, finished
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
2020-01-12 21:27:37 +01:00
return status ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
static u8 HandleReplaceSectorAndVerify ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
u8 status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
HandleReplaceSector ( sectorId - 1 , locations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
{
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
2020-01-12 21:27:37 +01:00
return status ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// Similar to HandleWriteSector, but fully erases the sector first, and skips writing the first security byte
static u8 HandleReplaceSector ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 sector ;
u8 * data ;
u16 size ;
u8 status ;
2021-10-29 04:54:41 +02:00
// Adjust sector id for current save slot
2020-01-12 21:27:37 +01:00
sector = sectorId + gLastWrittenSector ;
2021-10-19 22:30:00 +02:00
sector % = NUM_SECTORS_PER_SLOT ;
sector + = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Get current save data
data = locations [ sectorId ] . data ;
size = locations [ sectorId ] . size ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Clear temp save sector.
for ( i = 0 ; i < SECTOR_SIZE ; i + + )
( ( u8 * ) gReadWriteSector ) [ i ] = 0 ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Set footer data
gReadWriteSector - > id = sectorId ;
gReadWriteSector - > security = SECTOR_SECURITY_NUM ;
gReadWriteSector - > counter = gSaveCounter ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Copy current data to temp buffer for writing
2017-09-03 14:13:01 +02:00
for ( i = 0 ; i < size ; i + + )
2021-10-29 04:54:41 +02:00
gReadWriteSector - > data [ i ] = data [ i ] ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
gReadWriteSector - > checksum = CalculateChecksum ( data , size ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Erase old save data
2017-09-03 14:13:01 +02:00
EraseFlashSector ( sector ) ;
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Write new save data up to security field
for ( i = 0 ; i < SECTOR_SECURITY_OFFSET ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
if ( ProgramFlashByte ( sector , i , ( ( u8 * ) gReadWriteSector ) [ i ] ) )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
break ;
}
}
2020-01-12 21:27:37 +01:00
if ( status = = SAVE_STATUS_ERROR )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Writing save data failed
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( ENABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Writing save data succeeded, write security and counter
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Write security (skipping the first byte) and counter fields.
// The byte of security that is skipped is instead written by WriteSectorSecurityByte or WriteSectorSecurityByte_NoOffset
for ( i = 0 ; i < SECTOR_SIZE - ( SECTOR_SECURITY_OFFSET + 1 ) ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
if ( ProgramFlashByte ( sector , SECTOR_SECURITY_OFFSET + 1 + i , ( ( u8 * ) gReadWriteSector ) [ SECTOR_SECURITY_OFFSET + 1 + i ] ) )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
break ;
}
}
2020-01-12 21:27:37 +01:00
if ( status = = SAVE_STATUS_ERROR )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Writing security/counter failed
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( ENABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Succeeded
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( DISABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
}
}
2021-10-29 04:54:41 +02:00
static u8 WriteSectorSecurityByte_NoOffset ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Adjust sector id for current save slot
// This first line lacking -1 is the only difference from WriteSectorSecurityByte
u16 sector = sectorId + gLastWrittenSector ;
2021-10-19 22:30:00 +02:00
sector % = NUM_SECTORS_PER_SLOT ;
sector + = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Write just the first byte of the security field, which was skipped by HandleReplaceSector
if ( ProgramFlashByte ( sector , SECTOR_SECURITY_OFFSET , SECTOR_SECURITY_NUM & 0xFF ) )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Succeeded
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( DISABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
}
2021-10-29 04:54:41 +02:00
static u8 CopySectorSecurityByte ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Adjust sector id for current save slot
u16 sector = sectorId + gLastWrittenSector - 1 ;
2021-10-19 22:30:00 +02:00
sector % = NUM_SECTORS_PER_SLOT ;
sector + = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Copy just the first byte of the security field from the read/write buffer
if ( ProgramFlashByte ( sector , SECTOR_SECURITY_OFFSET , ( ( u8 * ) gReadWriteSector ) [ SECTOR_SECURITY_OFFSET ] ) )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Succeded
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( DISABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
}
2021-10-29 04:54:41 +02:00
static u8 WriteSectorSecurityByte ( u16 sectorId , const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Adjust sector id for current save slot
u16 sector = sectorId + gLastWrittenSector - 1 ;
2021-10-19 22:30:00 +02:00
sector % = NUM_SECTORS_PER_SLOT ;
sector + = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
2021-10-29 04:54:41 +02:00
// Write just the first byte of the security field, which was skipped by HandleReplaceSector
if ( ProgramFlashByte ( sector , SECTOR_SECURITY_OFFSET , SECTOR_SECURITY_NUM & 0xFF ) )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Succeeded
2017-09-03 14:13:01 +02:00
SetDamagedSectorBits ( DISABLE , sector ) ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
}
2021-10-29 04:54:41 +02:00
static u8 TryLoadSaveSlot ( u16 sectorId , struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
u8 status ;
2021-10-29 04:54:41 +02:00
gReadWriteSector = & gSaveDataBuffer ;
if ( sectorId ! = FULL_SAVE_SLOT )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// This function may not be used with a specific sector id
2020-01-12 21:27:37 +01:00
status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
status = GetSaveValidStatus ( locations ) ;
CopySaveSlotData ( FULL_SAVE_SLOT , locations ) ;
2017-09-03 14:13:01 +02:00
}
2020-01-12 21:27:37 +01:00
return status ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// sectorId arg is ignored, this always reads the full save slot
static u8 CopySaveSlotData ( u16 sectorId , struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 checksum ;
2021-10-19 22:30:00 +02:00
u16 slotOffset = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
2017-09-03 14:13:01 +02:00
u16 id ;
2021-10-19 22:30:00 +02:00
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
ReadFlashSector ( i + slotOffset , gReadWriteSector ) ;
id = gReadWriteSector - > id ;
2017-09-03 14:13:01 +02:00
if ( id = = 0 )
gLastWrittenSector = i ;
2021-10-29 04:54:41 +02:00
checksum = CalculateChecksum ( gReadWriteSector - > data , locations [ id ] . size ) ;
// Only copy data for sectors whose security and checksum fields are correct
if ( gReadWriteSector - > security = = SECTOR_SECURITY_NUM & & gReadWriteSector - > checksum = = checksum )
2017-09-03 14:13:01 +02:00
{
u16 j ;
2021-10-29 04:54:41 +02:00
for ( j = 0 ; j < locations [ id ] . size ; j + + )
( ( u8 * ) locations [ id ] . data ) [ j ] = gReadWriteSector - > data [ j ] ;
2017-09-03 14:13:01 +02:00
}
}
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
static u8 GetSaveValidStatus ( const struct SaveSectorLocation * locations )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 checksum ;
u32 saveSlot1Counter = 0 ;
u32 saveSlot2Counter = 0 ;
2021-10-29 04:54:41 +02:00
u32 validSectorFlags = 0 ;
2017-09-03 14:13:01 +02:00
bool8 securityPassed = FALSE ;
u8 saveSlot1Status ;
u8 saveSlot2Status ;
2021-10-29 04:54:41 +02:00
// Check save slot 1
2021-10-19 22:30:00 +02:00
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
ReadFlashSector ( i , gReadWriteSector ) ;
if ( gReadWriteSector - > security = = SECTOR_SECURITY_NUM )
2017-09-03 14:13:01 +02:00
{
securityPassed = TRUE ;
2021-10-29 04:54:41 +02:00
checksum = CalculateChecksum ( gReadWriteSector - > data , locations [ gReadWriteSector - > id ] . size ) ;
if ( gReadWriteSector - > checksum = = checksum )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
saveSlot1Counter = gReadWriteSector - > counter ;
validSectorFlags | = 1 < < gReadWriteSector - > id ;
2017-09-03 14:13:01 +02:00
}
}
}
if ( securityPassed )
{
2021-10-29 04:54:41 +02:00
if ( validSectorFlags = = ( 1 < < NUM_SECTORS_PER_SLOT ) - 1 )
2020-01-12 21:27:37 +01:00
saveSlot1Status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
else
2020-01-12 21:27:37 +01:00
saveSlot1Status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// No sectors in slot 1 have the security number, treat it as empty
2020-01-12 21:27:37 +01:00
saveSlot1Status = SAVE_STATUS_EMPTY ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
validSectorFlags = 0 ;
2017-09-03 14:13:01 +02:00
securityPassed = FALSE ;
2021-10-29 04:54:41 +02:00
// Check save slot 2
2021-10-19 22:30:00 +02:00
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
ReadFlashSector ( i + NUM_SECTORS_PER_SLOT , gReadWriteSector ) ;
if ( gReadWriteSector - > security = = SECTOR_SECURITY_NUM )
2017-09-03 14:13:01 +02:00
{
securityPassed = TRUE ;
2021-10-29 04:54:41 +02:00
checksum = CalculateChecksum ( gReadWriteSector - > data , locations [ gReadWriteSector - > id ] . size ) ;
if ( gReadWriteSector - > checksum = = checksum )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
saveSlot2Counter = gReadWriteSector - > counter ;
validSectorFlags | = 1 < < gReadWriteSector - > id ;
2017-09-03 14:13:01 +02:00
}
}
}
if ( securityPassed )
{
2021-10-29 04:54:41 +02:00
if ( validSectorFlags = = ( 1 < < NUM_SECTORS_PER_SLOT ) - 1 )
2020-01-12 21:27:37 +01:00
saveSlot2Status = SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
else
2020-01-12 21:27:37 +01:00
saveSlot2Status = SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// No sectors in slot 2 have the security number, treat it as empty.
2020-01-12 21:27:37 +01:00
saveSlot2Status = SAVE_STATUS_EMPTY ;
2017-09-03 14:13:01 +02:00
}
2020-01-12 21:27:37 +01:00
if ( saveSlot1Status = = SAVE_STATUS_OK & & saveSlot2Status = = SAVE_STATUS_OK )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
if ( ( saveSlot1Counter = = - 1 & & saveSlot2Counter = = 0 )
| | ( saveSlot1Counter = = 0 & & saveSlot2Counter = = - 1 ) )
2017-09-03 14:13:01 +02:00
{
if ( ( unsigned ) ( saveSlot1Counter + 1 ) < ( unsigned ) ( saveSlot2Counter + 1 ) )
gSaveCounter = saveSlot2Counter ;
else
gSaveCounter = saveSlot1Counter ;
}
else
{
if ( saveSlot1Counter < saveSlot2Counter )
gSaveCounter = saveSlot2Counter ;
else
gSaveCounter = saveSlot1Counter ;
}
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// One or both save slots are not OK
2020-01-12 21:27:37 +01:00
if ( saveSlot1Status = = SAVE_STATUS_OK )
2017-09-03 14:13:01 +02:00
{
gSaveCounter = saveSlot1Counter ;
2020-01-12 21:27:37 +01:00
if ( saveSlot2Status = = SAVE_STATUS_ERROR )
2021-10-29 04:54:41 +02:00
return SAVE_STATUS_ERROR ; // Slot 2 errored
return SAVE_STATUS_OK ; // Slot 1 is OK, slot 2 is empty
2017-09-03 14:13:01 +02:00
}
2020-01-12 21:27:37 +01:00
if ( saveSlot2Status = = SAVE_STATUS_OK )
2017-09-03 14:13:01 +02:00
{
gSaveCounter = saveSlot2Counter ;
2020-01-12 21:27:37 +01:00
if ( saveSlot1Status = = SAVE_STATUS_ERROR )
2021-10-29 04:54:41 +02:00
return SAVE_STATUS_ERROR ; // Slot 1 errored
return SAVE_STATUS_OK ; // Slot 2 is OK, slot 1 is empty
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// Neither slot is OK, check if both are empty
if ( saveSlot1Status = = SAVE_STATUS_EMPTY
& & saveSlot2Status = = SAVE_STATUS_EMPTY )
2017-09-03 14:13:01 +02:00
{
gSaveCounter = 0 ;
gLastWrittenSector = 0 ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_EMPTY ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// Both slots errored
2017-09-03 14:13:01 +02:00
gSaveCounter = 0 ;
gLastWrittenSector = 0 ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_CORRUPT ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
static u8 TryLoadSaveSector ( u8 sectorId , u8 * data , u16 size )
2017-09-03 14:13:01 +02:00
{
u16 i ;
2021-10-29 04:54:41 +02:00
struct SaveSector * sector = & gSaveDataBuffer ;
ReadFlashSector ( sectorId , sector ) ;
if ( sector - > security = = SECTOR_SECURITY_NUM )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
u16 checksum = CalculateChecksum ( sector - > data , size ) ;
if ( sector - > id = = checksum )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Security and checksum are correct, copy data
2017-09-03 14:13:01 +02:00
for ( i = 0 ; i < size ; i + + )
2021-10-29 04:54:41 +02:00
data [ i ] = sector - > data [ i ] ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Incorrect checksum
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_CORRUPT ;
2017-09-03 14:13:01 +02:00
}
}
else
{
2021-10-29 04:54:41 +02:00
// Incorrect security value
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_EMPTY ;
2017-09-03 14:13:01 +02:00
}
}
2020-01-12 21:27:37 +01:00
// Return value always ignored
2021-10-29 04:54:41 +02:00
static bool8 ReadFlashSector ( u8 sectorId , struct SaveSector * sector )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
ReadFlash ( sectorId , 0 , sector - > data , SECTOR_SIZE ) ;
2020-01-12 21:27:37 +01:00
return TRUE ;
2017-09-03 14:13:01 +02:00
}
Undo PokeCodec's PRs
This commit undoes most of PokeCodec's PRs after the debate in chat. Some
harmless or completely superseded PRs have been left alone, as there is not
much benefit in attempting to undo them.
Reverts #1104, #1108, #1115, #1118, #1119, #1124, #1126, #1127, #1132, #1136,
#1137, #1139, #1140, #1144, #1148, #1149, #1150, #1153, #1155, #1177, #1179,
#1180, #1181, #1182 and #1183.
2020-09-13 09:22:50 +02:00
static u16 CalculateChecksum ( void * data , u16 size )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u32 checksum = 0 ;
for ( i = 0 ; i < ( size / 4 ) ; i + + )
2019-06-26 14:13:38 +02:00
{
checksum + = * ( ( u32 * ) data ) ;
data + = sizeof ( u32 ) ;
}
2017-09-03 14:13:01 +02:00
return ( ( checksum > > 16 ) + checksum ) ;
}
2018-11-18 19:37:18 +01:00
static void UpdateSaveAddresses ( void )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
int i = SECTOR_ID_SAVEBLOCK2 ;
gRamSaveSectorLocations [ i ] . data = ( void * ) ( gSaveBlock2Ptr ) + sSaveSlotLayout [ i ] . offset ;
gRamSaveSectorLocations [ i ] . size = sSaveSlotLayout [ i ] . size ;
2017-09-29 10:06:36 +02:00
2020-01-12 21:27:37 +01:00
for ( i = SECTOR_ID_SAVEBLOCK1_START ; i < = SECTOR_ID_SAVEBLOCK1_END ; i + + )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
gRamSaveSectorLocations [ i ] . data = ( void * ) ( gSaveBlock1Ptr ) + sSaveSlotLayout [ i ] . offset ;
gRamSaveSectorLocations [ i ] . size = sSaveSlotLayout [ i ] . size ;
2017-09-03 14:13:01 +02:00
}
2020-10-24 04:12:09 +02:00
for ( ; i < = SECTOR_ID_PKMN_STORAGE_END ; i + + ) //setting i to SECTOR_ID_PKMN_STORAGE_START does not match
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
gRamSaveSectorLocations [ i ] . data = ( void * ) ( gPokemonStoragePtr ) + sSaveSlotLayout [ i ] . offset ;
gRamSaveSectorLocations [ i ] . size = sSaveSlotLayout [ i ] . size ;
2017-09-03 14:13:01 +02:00
}
}
u8 HandleSavingData ( u8 saveType )
{
u8 i ;
2019-03-01 07:49:11 +01:00
u32 * backupVar = gTrainerHillVBlankCounter ;
2017-09-03 14:13:01 +02:00
u8 * tempAddr ;
2019-03-01 07:49:11 +01:00
gTrainerHillVBlankCounter = NULL ;
2017-09-03 14:13:01 +02:00
UpdateSaveAddresses ( ) ;
switch ( saveType )
{
2021-10-29 04:54:41 +02:00
case SAVE_HALL_OF_FAME_ERASE_BEFORE :
// Unused. Erases the special save sectors (HOF, Trainer Hill, Recorded Battle)
// before overwriting HOF.
2018-11-18 19:37:18 +01:00
for ( i = SECTOR_ID_HOF_1 ; i < SECTORS_COUNT ; i + + )
2017-09-03 14:13:01 +02:00
EraseFlashSector ( i ) ;
2021-10-29 04:54:41 +02:00
// fallthrough
case SAVE_HALL_OF_FAME :
2017-09-03 14:13:01 +02:00
if ( GetGameStat ( GAME_STAT_ENTERED_HOF ) < 999 )
IncrementGameStat ( GAME_STAT_ENTERED_HOF ) ;
2021-10-29 04:54:41 +02:00
// Write the full save slot first
CopyPartyAndObjectsToSave ( ) ;
WriteSaveSectorOrSlot ( FULL_SAVE_SLOT , gRamSaveSectorLocations ) ;
// Save the Hall of Fame
2018-02-15 23:54:34 +01:00
tempAddr = gDecompressionBuffer ;
2020-01-12 21:27:37 +01:00
HandleWriteSectorNBytes ( SECTOR_ID_HOF_1 , tempAddr , SECTOR_DATA_SIZE ) ;
HandleWriteSectorNBytes ( SECTOR_ID_HOF_2 , tempAddr + SECTOR_DATA_SIZE , SECTOR_DATA_SIZE ) ;
2017-09-03 14:13:01 +02:00
break ;
2021-10-29 04:54:41 +02:00
case SAVE_NORMAL :
2017-09-03 14:13:01 +02:00
default :
2021-10-29 04:54:41 +02:00
CopyPartyAndObjectsToSave ( ) ;
WriteSaveSectorOrSlot ( FULL_SAVE_SLOT , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
break ;
2021-10-29 04:54:41 +02:00
case SAVE_LINK :
case SAVE_LINK2 :
// Used by link / Battle Frontier
// Write only SaveBlocks 1 and 2 (skips the PC)
CopyPartyAndObjectsToSave ( ) ;
2020-01-12 21:27:37 +01:00
for ( i = SECTOR_ID_SAVEBLOCK2 ; i < = SECTOR_ID_SAVEBLOCK1_END ; i + + )
2021-10-29 04:54:41 +02:00
HandleReplaceSector ( i , gRamSaveSectorLocations ) ;
2020-01-12 21:27:37 +01:00
for ( i = SECTOR_ID_SAVEBLOCK2 ; i < = SECTOR_ID_SAVEBLOCK1_END ; i + + )
2021-10-29 04:54:41 +02:00
WriteSectorSecurityByte_NoOffset ( i , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
break ;
2018-02-15 23:54:34 +01:00
case SAVE_OVERWRITE_DIFFERENT_FILE :
2021-10-29 04:54:41 +02:00
// Erase Hall of Fame
2018-11-18 19:37:18 +01:00
for ( i = SECTOR_ID_HOF_1 ; i < SECTORS_COUNT ; i + + )
2021-10-29 04:54:41 +02:00
EraseFlashSector ( i ) ;
// Overwrite save slot
CopyPartyAndObjectsToSave ( ) ;
WriteSaveSectorOrSlot ( FULL_SAVE_SLOT , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
break ;
}
2019-03-01 07:49:11 +01:00
gTrainerHillVBlankCounter = backupVar ;
2017-09-03 14:13:01 +02:00
return 0 ;
}
2018-11-18 19:37:18 +01:00
u8 TrySavingData ( u8 saveType )
2017-09-03 14:13:01 +02:00
{
2018-10-30 21:45:26 +01:00
if ( gFlashMemoryPresent ! = TRUE )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
gSaveAttemptStatus = SAVE_STATUS_ERROR ;
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
2018-10-30 21:45:26 +01:00
HandleSavingData ( saveType ) ;
if ( ! gDamagedSaveSectors )
{
2020-01-12 21:27:37 +01:00
gSaveAttemptStatus = SAVE_STATUS_OK ;
return SAVE_STATUS_OK ;
2018-10-30 21:45:26 +01:00
}
else
{
DoSaveFailedScreen ( saveType ) ;
2020-01-12 21:27:37 +01:00
gSaveAttemptStatus = SAVE_STATUS_ERROR ;
return SAVE_STATUS_ERROR ;
2018-10-30 21:45:26 +01:00
}
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
bool8 LinkFullSave_Init ( void )
2017-09-03 14:13:01 +02:00
{
if ( gFlashMemoryPresent ! = TRUE )
2018-10-30 21:45:26 +01:00
return TRUE ;
2017-09-03 14:13:01 +02:00
UpdateSaveAddresses ( ) ;
2021-10-29 04:54:41 +02:00
CopyPartyAndObjectsToSave ( ) ;
RestoreSaveBackupVarsAndIncrement ( gRamSaveSectorLocations ) ;
2018-10-30 21:45:26 +01:00
return FALSE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
bool8 LinkFullSave_WriteSector ( void )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
u8 status = HandleWriteIncrementalSector ( NUM_SECTORS_PER_SLOT , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
2020-01-12 21:27:37 +01:00
DoSaveFailedScreen ( SAVE_NORMAL ) ;
2021-11-13 23:22:01 +01:00
// In this case "error" either means that an actual error was encountered
// or that the given max sector has been reached (meaning it has finished successfully).
// If there was an actual error the save failed screen above will also be shown.
2020-01-12 21:27:37 +01:00
if ( status = = SAVE_STATUS_ERROR )
2018-10-30 21:45:26 +01:00
return TRUE ;
2017-09-03 14:13:01 +02:00
else
2018-10-30 21:45:26 +01:00
return FALSE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
bool8 LinkFullSave_ReplaceLastSector ( void )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
HandleReplaceSectorAndVerify ( NUM_SECTORS_PER_SLOT , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
2020-01-12 21:27:37 +01:00
DoSaveFailedScreen ( SAVE_NORMAL ) ;
return FALSE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
bool8 LinkFullSave_SetLastSectorSecurity ( void )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
CopySectorSecurityByte ( NUM_SECTORS_PER_SLOT , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
2020-01-12 21:27:37 +01:00
DoSaveFailedScreen ( SAVE_NORMAL ) ;
return FALSE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
u8 WriteSaveBlock2 ( void )
2017-09-03 14:13:01 +02:00
{
if ( gFlashMemoryPresent ! = TRUE )
2020-01-12 21:27:37 +01:00
return TRUE ;
2017-09-03 14:13:01 +02:00
UpdateSaveAddresses ( ) ;
2021-10-29 04:54:41 +02:00
CopyPartyAndObjectsToSave ( ) ;
RestoreSaveBackupVars ( gRamSaveSectorLocations ) ;
// Because RestoreSaveBackupVars is called immediately prior, gIncrementalSectorId will always be 0 below,
// so this function only saves the first sector (SECTOR_ID_SAVEBLOCK2)
HandleReplaceSectorAndVerify ( gIncrementalSectorId + 1 , gRamSaveSectorLocations ) ;
2020-01-12 21:27:37 +01:00
return FALSE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
// Used in conjunction with WriteSaveBlock2 to write both for certain link saves.
// This will be called repeatedly in a task, writing each sector of SaveBlock1 incrementally.
// It returns TRUE when finished.
bool8 WriteSaveBlock1Sector ( void )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
u8 finished = FALSE ;
u16 sectorId = + + gIncrementalSectorId ; // Because WriteSaveBlock2 will have been called prior, this will be SECTOR_ID_SAVEBLOCK1_START
2020-01-12 21:27:37 +01:00
if ( sectorId < = SECTOR_ID_SAVEBLOCK1_END )
2017-09-03 14:13:01 +02:00
{
2021-10-29 04:54:41 +02:00
// Write a single sector of SaveBlock1
HandleReplaceSectorAndVerify ( gIncrementalSectorId + 1 , gRamSaveSectorLocations ) ;
WriteSectorSecurityByte ( sectorId , gRamSaveSectorLocations ) ;
2017-09-03 14:13:01 +02:00
}
else
{
2021-10-29 04:54:41 +02:00
// Beyond SaveBlock1, don't write the sector.
// Does write 1 byte of the next sector's security field, but as these
// are the same for all valid sectors it doesn't matter.
WriteSectorSecurityByte ( sectorId , gRamSaveSectorLocations ) ;
finished = TRUE ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
2020-01-12 21:27:37 +01:00
DoSaveFailedScreen ( SAVE_LINK ) ;
2021-10-29 04:54:41 +02:00
return finished ;
2017-09-03 14:13:01 +02:00
}
2021-10-29 04:54:41 +02:00
u8 LoadGameSave ( u8 saveType )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
u8 status ;
2017-09-03 14:13:01 +02:00
if ( gFlashMemoryPresent ! = TRUE )
{
2020-01-12 21:27:37 +01:00
gSaveFileStatus = SAVE_STATUS_NO_FLASH ;
return SAVE_STATUS_ERROR ;
2017-09-03 14:13:01 +02:00
}
UpdateSaveAddresses ( ) ;
2020-01-12 21:27:37 +01:00
switch ( saveType )
2017-09-03 14:13:01 +02:00
{
2020-01-12 21:27:37 +01:00
case SAVE_NORMAL :
2017-09-03 14:13:01 +02:00
default :
2021-10-29 04:54:41 +02:00
status = TryLoadSaveSlot ( FULL_SAVE_SLOT , gRamSaveSectorLocations ) ;
CopyPartyAndObjectsFromSave ( ) ;
2020-01-12 21:27:37 +01:00
gSaveFileStatus = status ;
2017-09-03 14:13:01 +02:00
gGameContinueCallback = 0 ;
break ;
2020-01-12 21:27:37 +01:00
case SAVE_HALL_OF_FAME :
2021-10-29 04:54:41 +02:00
status = TryLoadSaveSector ( SECTOR_ID_HOF_1 , gDecompressionBuffer , SECTOR_DATA_SIZE ) ;
2020-01-12 21:27:37 +01:00
if ( status = = SAVE_STATUS_OK )
2021-10-29 04:54:41 +02:00
status = TryLoadSaveSector ( SECTOR_ID_HOF_2 , & gDecompressionBuffer [ SECTOR_DATA_SIZE ] , SECTOR_DATA_SIZE ) ;
2017-09-03 14:13:01 +02:00
break ;
}
2020-01-12 21:27:37 +01:00
return status ;
2017-09-03 14:13:01 +02:00
}
2017-09-03 15:39:33 +02:00
2021-08-16 00:26:09 +02:00
u16 GetSaveBlocksPointersBaseOffset ( void )
2017-09-03 15:39:33 +02:00
{
2021-08-16 00:26:09 +02:00
u16 i , slotOffset ;
2021-10-29 04:54:41 +02:00
struct SaveSector * sector ;
2017-09-03 15:39:33 +02:00
2021-10-29 04:54:41 +02:00
sector = gReadWriteSector = & gSaveDataBuffer ;
2018-04-29 14:21:59 +02:00
if ( gFlashMemoryPresent ! = TRUE )
2021-08-16 00:26:09 +02:00
return 0 ;
2017-09-03 15:39:33 +02:00
UpdateSaveAddresses ( ) ;
2021-10-29 04:54:41 +02:00
GetSaveValidStatus ( gRamSaveSectorLocations ) ;
2021-10-19 22:30:00 +02:00
slotOffset = NUM_SECTORS_PER_SLOT * ( gSaveCounter % NUM_SAVE_SLOTS ) ;
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
2017-09-03 15:39:33 +02:00
{
2021-10-29 04:54:41 +02:00
ReadFlashSector ( i + slotOffset , gReadWriteSector ) ;
2021-08-16 00:26:09 +02:00
// Base offset for SaveBlock2 is calculated using the trainer id
2021-10-29 04:54:41 +02:00
if ( gReadWriteSector - > id = = SECTOR_ID_SAVEBLOCK2 )
return sector - > data [ offsetof ( struct SaveBlock2 , playerTrainerId [ 0 ] ) ] +
sector - > data [ offsetof ( struct SaveBlock2 , playerTrainerId [ 1 ] ) ] +
sector - > data [ offsetof ( struct SaveBlock2 , playerTrainerId [ 2 ] ) ] +
sector - > data [ offsetof ( struct SaveBlock2 , playerTrainerId [ 3 ] ) ] ;
2017-09-03 15:39:33 +02:00
}
2021-08-16 00:26:09 +02:00
return 0 ;
2017-09-03 15:39:33 +02:00
}
2021-10-29 04:54:41 +02:00
u32 TryReadSpecialSaveSector ( u8 sector , u8 * dst )
2017-09-03 15:39:33 +02:00
{
s32 i ;
s32 size ;
u8 * savData ;
2018-11-18 19:37:18 +01:00
if ( sector ! = SECTOR_ID_TRAINER_HILL & & sector ! = SECTOR_ID_RECORDED_BATTLE )
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2021-10-29 04:54:41 +02:00
ReadFlash ( sector , 0 , ( u8 * ) & gSaveDataBuffer , SECTOR_SIZE ) ;
if ( * ( u32 * ) ( & gSaveDataBuffer . data [ 0 ] ) ! = SPECIAL_SECTOR_SENTINEL )
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2021-10-29 04:54:41 +02:00
// Copies whole save sector except u32 counter
2017-09-03 15:39:33 +02:00
i = 0 ;
2021-10-29 04:54:41 +02:00
size = SECTOR_COUNTER_OFFSET - 1 ;
savData = & gSaveDataBuffer . data [ 4 ] ; // data[4] to skip past SPECIAL_SECTOR_SENTINEL
2017-09-03 15:39:33 +02:00
for ( ; i < = size ; i + + )
dst [ i ] = savData [ i ] ;
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_OK ;
2017-09-03 15:39:33 +02:00
}
2021-10-29 04:54:41 +02:00
u32 TryWriteSpecialSaveSector ( u8 sector , u8 * src )
2017-09-03 15:39:33 +02:00
{
s32 i ;
s32 size ;
u8 * savData ;
void * savDataBuffer ;
2019-04-04 17:55:18 +02:00
if ( sector ! = SECTOR_ID_TRAINER_HILL & & sector ! = SECTOR_ID_RECORDED_BATTLE )
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
2017-11-13 18:07:23 +01:00
2017-09-03 15:39:33 +02:00
savDataBuffer = & gSaveDataBuffer ;
2021-10-29 04:54:41 +02:00
* ( u32 * ) ( savDataBuffer ) = SPECIAL_SECTOR_SENTINEL ;
2017-09-03 15:39:33 +02:00
2021-10-29 04:54:41 +02:00
// Copies whole save sector except u32 counter
2017-09-03 15:39:33 +02:00
i = 0 ;
2021-10-29 04:54:41 +02:00
size = SECTOR_COUNTER_OFFSET - 1 ;
savData = & gSaveDataBuffer . data [ 4 ] ; // data[4] to skip past SPECIAL_SECTOR_SENTINEL
2017-09-03 15:39:33 +02:00
for ( ; i < = size ; i + + )
savData [ i ] = src [ i ] ;
if ( ProgramFlashSectorAndVerify ( sector , savDataBuffer ) ! = 0 )
2020-01-12 21:27:37 +01:00
return SAVE_STATUS_ERROR ;
return SAVE_STATUS_OK ;
2017-09-03 15:39:33 +02:00
}
2017-09-29 10:06:36 +02:00
2021-10-29 04:54:41 +02:00
# define tState data[0]
# define tTimer data[1]
# define tInBattleTower data[2]
2020-06-04 00:00:53 +02:00
2021-10-29 04:54:41 +02:00
// Note that this is very different from TrySavingData(SAVE_LINK).
// Most notably it does save the PC data.
void Task_LinkFullSave ( u8 taskId )
2017-09-29 10:06:36 +02:00
{
2020-06-04 00:00:53 +02:00
s16 * data = gTasks [ taskId ] . data ;
2017-09-29 10:06:36 +02:00
2020-06-04 00:00:53 +02:00
switch ( tState )
2017-09-29 10:06:36 +02:00
{
case 0 :
gSoftResetDisabled = TRUE ;
2020-06-04 00:00:53 +02:00
tState = 1 ;
2017-09-29 10:06:36 +02:00
break ;
case 1 :
2020-08-13 09:09:47 +02:00
SetLinkStandbyCallback ( ) ;
2020-06-04 00:00:53 +02:00
tState = 2 ;
2017-09-29 10:06:36 +02:00
break ;
case 2 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2017-09-29 10:06:36 +02:00
{
2021-10-29 04:54:41 +02:00
if ( ! tInBattleTower )
2021-04-06 22:05:43 +02:00
SaveMapView ( ) ;
2020-06-04 00:00:53 +02:00
tState = 3 ;
2017-09-29 10:06:36 +02:00
}
break ;
case 3 :
2021-10-29 04:54:41 +02:00
if ( ! tInBattleTower )
2018-12-27 23:30:47 +01:00
SetContinueGameWarpStatusToDynamicWarp ( ) ;
2021-10-29 04:54:41 +02:00
LinkFullSave_Init ( ) ;
2020-06-04 00:00:53 +02:00
tState = 4 ;
2017-09-29 10:06:36 +02:00
break ;
case 4 :
2020-06-04 00:00:53 +02:00
if ( + + tTimer = = 5 )
2017-09-29 10:06:36 +02:00
{
2020-06-04 00:00:53 +02:00
tTimer = 0 ;
tState = 5 ;
2017-09-29 10:06:36 +02:00
}
break ;
case 5 :
2021-10-29 04:54:41 +02:00
if ( LinkFullSave_WriteSector ( ) )
2020-06-04 00:00:53 +02:00
tState = 6 ;
2017-09-29 10:06:36 +02:00
else
2021-10-29 04:54:41 +02:00
tState = 4 ; // Not finished, delay again
2017-09-29 10:06:36 +02:00
break ;
case 6 :
2021-10-29 04:54:41 +02:00
LinkFullSave_ReplaceLastSector ( ) ;
2020-06-04 00:00:53 +02:00
tState = 7 ;
2017-09-29 10:06:36 +02:00
break ;
case 7 :
2021-10-29 04:54:41 +02:00
if ( ! tInBattleTower )
2018-12-27 23:30:47 +01:00
ClearContinueGameWarpStatus2 ( ) ;
2020-08-13 09:09:47 +02:00
SetLinkStandbyCallback ( ) ;
2020-06-04 00:00:53 +02:00
tState = 8 ;
2017-09-29 10:06:36 +02:00
break ;
case 8 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2017-09-29 10:06:36 +02:00
{
2021-10-29 04:54:41 +02:00
LinkFullSave_SetLastSectorSecurity ( ) ;
2020-06-04 00:00:53 +02:00
tState = 9 ;
2017-09-29 10:06:36 +02:00
}
break ;
case 9 :
2020-08-13 09:09:47 +02:00
SetLinkStandbyCallback ( ) ;
2020-06-04 00:00:53 +02:00
tState = 10 ;
2017-09-29 10:06:36 +02:00
break ;
case 10 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2020-06-04 00:00:53 +02:00
tState + + ;
2017-09-29 10:06:36 +02:00
break ;
case 11 :
2020-06-04 00:00:53 +02:00
if ( + + tTimer > 5 )
2017-09-29 10:06:36 +02:00
{
gSoftResetDisabled = FALSE ;
DestroyTask ( taskId ) ;
}
break ;
}
}