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"
static u16 CalculateChecksum ( void * data , u16 size ) ;
static u8 DoReadFlashWholeSection ( u8 sector , struct SaveSection * section ) ;
static u8 GetSaveValidStatus ( const struct SaveSectionLocation * location ) ;
static u8 sub_8152E10 ( u16 a1 , const struct SaveSectionLocation * location ) ;
static u8 ClearSaveData_2 ( u16 a1 , const struct SaveSectionLocation * location ) ;
static u8 TryWriteSector ( u8 sector , u8 * data ) ;
static u8 HandleWriteSector ( u16 a1 , const struct SaveSectionLocation * location ) ;
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
// Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer
# define SECTOR_DATA_SIZE 3968
# define SECTOR_FOOTER_SIZE 128
/*
* 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 .
*/
// (u8 *)structure was removed from the first statement of the macro in Emerald.
// This is because malloc is used to allocate addresses so storing the raw
2018-04-29 14:21:59 +02:00
// addresses should not be done in the offsets information.
2018-01-31 21:26:57 +01:00
# define SAVEBLOCK_CHUNK(structure, chunkNum) \
{ \
chunkNum * SECTOR_DATA_SIZE , \
min ( sizeof ( structure ) - chunkNum * SECTOR_DATA_SIZE , SECTOR_DATA_SIZE ) \
} \
const struct SaveSectionOffsets gSaveSectionOffsets [ ] =
{
SAVEBLOCK_CHUNK ( gSaveblock2 , 0 ) ,
SAVEBLOCK_CHUNK ( gSaveblock1 , 0 ) ,
SAVEBLOCK_CHUNK ( gSaveblock1 , 1 ) ,
SAVEBLOCK_CHUNK ( gSaveblock1 , 2 ) ,
SAVEBLOCK_CHUNK ( gSaveblock1 , 3 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 0 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 1 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 2 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 3 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 4 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 5 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 6 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 7 ) ,
SAVEBLOCK_CHUNK ( gPokemonStorage , 8 ) ,
} ;
2017-09-03 15:39:33 +02:00
// iwram common
u16 gLastWrittenSector ;
u32 gLastSaveCounter ;
u16 gLastKnownGoodSector ;
u32 gDamagedSaveSectors ;
u32 gSaveCounter ;
struct SaveSection * gFastSaveSection ;
u16 gUnknown_03006208 ;
u16 gSaveUnusedVar ;
2017-09-03 22:50:17 +02:00
u16 gSaveFileStatus ;
2017-09-03 15:39:33 +02:00
void ( * gGameContinueCallback ) ( void ) ;
2018-09-10 01:01:39 +02:00
struct SaveSectionLocation gRamSaveSectionLocations [ 0xE ] ;
u16 gSaveUnusedVar2 ;
u16 gUnknown_03006294 ;
2017-09-03 15:39:33 +02:00
EWRAM_DATA struct SaveSection gSaveDataBuffer = { 0 } ;
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 ;
for ( i = 0 ; i < NUM_SECTORS_PER_SLOT ; i + + )
{
EraseFlashSector ( i ) ;
EraseFlashSector ( i + NUM_SECTORS_PER_SLOT ) ; // clear slot 2.
}
}
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 ;
}
2018-11-18 19:37:18 +01:00
static bool32 SetDamagedSectorBits ( u8 op , u8 bit )
2017-09-03 14:13:01 +02:00
{
bool32 retVal = FALSE ;
switch ( op )
{
case ENABLE :
gDamagedSaveSectors | = ( 1 < < bit ) ;
break ;
case DISABLE :
gDamagedSaveSectors & = ~ ( 1 < < bit ) ;
break ;
case CHECK : // unused
if ( gDamagedSaveSectors & ( 1 < < bit ) )
retVal = TRUE ;
break ;
}
return retVal ;
}
2018-11-18 19:37:18 +01:00
static u8 save_write_to_flash ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u32 retVal ;
u16 i ;
2017-09-03 15:39:33 +02:00
gFastSaveSection = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
if ( a1 ! = 0xFFFF ) // for link
{
retVal = HandleWriteSector ( a1 , location ) ;
}
else
{
gLastKnownGoodSector = gLastWrittenSector ; // backup the current written sector before attempting to write.
gLastSaveCounter = gSaveCounter ;
gLastWrittenSector + + ;
2018-11-18 19:37:18 +01:00
gLastWrittenSector = gLastWrittenSector % SECTOR_SAVE_SLOT_LENGTH ; // array count save sector locations
2017-09-03 14:13:01 +02:00
gSaveCounter + + ;
retVal = 1 ;
2018-11-18 19:37:18 +01:00
for ( i = 0 ; i < SECTOR_SAVE_SLOT_LENGTH ; i + + )
2017-09-03 14:13:01 +02:00
HandleWriteSector ( i , location ) ;
if ( gDamagedSaveSectors ! = 0 ) // skip the damaged sector.
{
retVal = 0xFF ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
}
return retVal ;
}
2018-11-18 19:37:18 +01:00
static u8 HandleWriteSector ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 sector ;
u8 * data ;
u16 size ;
sector = a1 + gLastWrittenSector ;
2018-11-18 19:37:18 +01:00
sector % = SECTOR_SAVE_SLOT_LENGTH ;
sector + = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
data = location [ a1 ] . data ;
size = location [ a1 ] . size ;
// clear save section.
for ( i = 0 ; i < sizeof ( struct SaveSection ) ; i + + )
( ( char * ) gFastSaveSection ) [ i ] = 0 ;
gFastSaveSection - > id = a1 ;
gFastSaveSection - > security = UNKNOWN_CHECK_VALUE ;
gFastSaveSection - > counter = gSaveCounter ;
for ( i = 0 ; i < size ; i + + )
gFastSaveSection - > data [ i ] = data [ i ] ;
gFastSaveSection - > checksum = CalculateChecksum ( data , size ) ;
return TryWriteSector ( sector , gFastSaveSection - > data ) ;
}
2018-11-18 19:37:18 +01:00
static u8 HandleWriteSectorNBytes ( u8 sector , u8 * data , u16 size )
2017-09-03 14:13:01 +02:00
{
u16 i ;
2017-09-03 15:39:33 +02:00
struct SaveSection * section = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
for ( i = 0 ; i < sizeof ( struct SaveSection ) ; i + + )
( ( char * ) section ) [ i ] = 0 ;
section - > security = UNKNOWN_CHECK_VALUE ;
for ( i = 0 ; i < size ; i + + )
section - > data [ i ] = data [ i ] ;
section - > 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 ( sector , section - > data ) ;
}
2018-11-18 19:37:18 +01:00
static u8 TryWriteSector ( u8 sector , u8 * data )
2017-09-03 14:13:01 +02:00
{
if ( ProgramFlashSectorAndVerify ( sector , data ) ! = 0 ) // is damaged?
{
SetDamagedSectorBits ( ENABLE , sector ) ; // set damaged sector bits.
return 0xFF ;
}
else
{
SetDamagedSectorBits ( DISABLE , sector ) ; // unset damaged sector bits. it's safe now.
return 1 ;
}
}
2018-11-18 19:37:18 +01:00
static u32 RestoreSaveBackupVarsAndIncrement ( const struct SaveSectionLocation * location ) // location is unused
2017-09-03 14:13:01 +02:00
{
2017-09-03 15:39:33 +02:00
gFastSaveSection = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
gLastKnownGoodSector = gLastWrittenSector ;
gLastSaveCounter = gSaveCounter ;
gLastWrittenSector + + ;
2018-11-18 19:37:18 +01:00
gLastWrittenSector % = SECTOR_SAVE_SLOT_LENGTH ;
2017-09-03 14:13:01 +02:00
gSaveCounter + + ;
gUnknown_03006208 = 0 ;
gDamagedSaveSectors = 0 ;
return 0 ;
}
2018-11-18 19:37:18 +01:00
static u32 RestoreSaveBackupVars ( const struct SaveSectionLocation * location ) // only ever called once, and gSaveBlock2 is passed to this function. location is unused
2017-09-03 14:13:01 +02:00
{
2017-09-03 15:39:33 +02:00
gFastSaveSection = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
gLastKnownGoodSector = gLastWrittenSector ;
gLastSaveCounter = gSaveCounter ;
gUnknown_03006208 = 0 ;
gDamagedSaveSectors = 0 ;
return 0 ;
}
2018-11-18 19:37:18 +01:00
static u8 sub_81529D4 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u8 retVal ;
if ( gUnknown_03006208 < a1 - 1 )
{
retVal = 1 ;
HandleWriteSector ( gUnknown_03006208 , location ) ;
gUnknown_03006208 + + ;
if ( gDamagedSaveSectors )
{
retVal = 0xFF ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
}
else
{
retVal = 0xFF ;
}
return retVal ;
}
2018-11-18 19:37:18 +01:00
static u8 sub_8152A34 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u8 retVal = 1 ;
ClearSaveData_2 ( a1 - 1 , location ) ;
if ( gDamagedSaveSectors )
{
retVal = 0xFF ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
}
return retVal ;
}
2018-11-18 19:37:18 +01:00
static u8 ClearSaveData_2 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 sector ;
u8 * data ;
u16 size ;
u8 status ;
sector = a1 + gLastWrittenSector ;
2018-11-18 19:37:18 +01:00
sector % = SECTOR_SAVE_SLOT_LENGTH ;
sector + = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
data = location [ a1 ] . data ;
size = location [ a1 ] . size ;
// clear temp save section.
for ( i = 0 ; i < sizeof ( struct SaveSection ) ; i + + )
( ( char * ) gFastSaveSection ) [ i ] = 0 ;
gFastSaveSection - > id = a1 ;
gFastSaveSection - > security = UNKNOWN_CHECK_VALUE ;
gFastSaveSection - > counter = gSaveCounter ;
// set temp section's data.
for ( i = 0 ; i < size ; i + + )
gFastSaveSection - > data [ i ] = data [ i ] ;
// calculate checksum.
gFastSaveSection - > checksum = CalculateChecksum ( data , size ) ;
EraseFlashSector ( sector ) ;
status = 1 ;
for ( i = 0 ; i < sizeof ( struct UnkSaveSection ) ; i + + )
{
if ( ProgramFlashByte ( sector , i , ( ( u8 * ) gFastSaveSection ) [ i ] ) )
{
status = 0xFF ;
break ;
}
}
if ( status = = 0xFF )
{
SetDamagedSectorBits ( ENABLE , sector ) ;
return 0xFF ;
}
else
{
status = 1 ;
for ( i = 0 ; i < 7 ; i + + )
{
if ( ProgramFlashByte ( sector , 0xFF9 + i , ( ( u8 * ) gFastSaveSection ) [ 0xFF9 + i ] ) )
{
status = 0xFF ;
break ;
}
}
if ( status = = 0xFF )
{
SetDamagedSectorBits ( ENABLE , sector ) ;
return 0xFF ;
}
else
{
SetDamagedSectorBits ( DISABLE , sector ) ;
return 1 ;
}
}
}
2018-11-18 19:37:18 +01:00
static u8 sav12_xor_get ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 sector ;
sector = a1 + gLastWrittenSector ; // no sub 1?
2018-11-18 19:37:18 +01:00
sector % = SECTOR_SAVE_SLOT_LENGTH ;
sector + = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
if ( ProgramFlashByte ( sector , sizeof ( struct UnkSaveSection ) , 0x25 ) )
{
// sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
return 0xFF ;
}
else
{
SetDamagedSectorBits ( DISABLE , sector ) ;
return 1 ;
}
}
2018-11-18 19:37:18 +01:00
static u8 sub_8152CAC ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 sector ;
sector = a1 + gLastWrittenSector - 1 ;
2018-11-18 19:37:18 +01:00
sector % = SECTOR_SAVE_SLOT_LENGTH ;
sector + = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
if ( ProgramFlashByte ( sector , sizeof ( struct UnkSaveSection ) , ( ( u8 * ) gFastSaveSection ) [ sizeof ( struct UnkSaveSection ) ] ) )
{
// sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
return 0xFF ;
}
else
{
SetDamagedSectorBits ( DISABLE , sector ) ;
return 1 ;
}
}
2018-11-18 19:37:18 +01:00
static u8 sub_8152D44 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 sector ;
sector = a1 + gLastWrittenSector - 1 ; // no sub 1?
2018-11-18 19:37:18 +01:00
sector % = SECTOR_SAVE_SLOT_LENGTH ;
sector + = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
if ( ProgramFlashByte ( sector , sizeof ( struct UnkSaveSection ) , 0x25 ) )
{
// sector is damaged, so enable the bit in gDamagedSaveSectors and restore the last written sector and save counter.
SetDamagedSectorBits ( ENABLE , sector ) ;
gLastWrittenSector = gLastKnownGoodSector ;
gSaveCounter = gLastSaveCounter ;
return 0xFF ;
}
else
{
SetDamagedSectorBits ( DISABLE , sector ) ;
return 1 ;
}
}
2018-11-18 19:37:18 +01:00
static u8 sub_8152DD0 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u8 retVal ;
2017-09-03 15:39:33 +02:00
gFastSaveSection = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
if ( a1 ! = 0xFFFF )
{
retVal = 0xFF ;
}
else
{
retVal = GetSaveValidStatus ( location ) ;
sub_8152E10 ( 0xFFFF , location ) ;
}
return retVal ;
}
2018-11-18 19:37:18 +01:00
static u8 sub_8152E10 ( u16 a1 , const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 checksum ;
2018-11-18 19:37:18 +01:00
u16 v3 = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
2017-09-03 14:13:01 +02:00
u16 id ;
2018-11-18 19:37:18 +01:00
for ( i = 0 ; i < SECTOR_SAVE_SLOT_LENGTH ; i + + )
2017-09-03 14:13:01 +02:00
{
DoReadFlashWholeSection ( i + v3 , gFastSaveSection ) ;
id = gFastSaveSection - > id ;
if ( id = = 0 )
gLastWrittenSector = i ;
checksum = CalculateChecksum ( gFastSaveSection - > data , location [ id ] . size ) ;
if ( gFastSaveSection - > security = = UNKNOWN_CHECK_VALUE
& & gFastSaveSection - > checksum = = checksum )
{
u16 j ;
for ( j = 0 ; j < location [ id ] . size ; j + + )
( ( u8 * ) location [ id ] . data ) [ j ] = gFastSaveSection - > data [ j ] ;
}
}
return 1 ;
}
2018-11-18 19:37:18 +01:00
static u8 GetSaveValidStatus ( const struct SaveSectionLocation * location )
2017-09-03 14:13:01 +02:00
{
u16 i ;
u16 checksum ;
u32 saveSlot1Counter = 0 ;
u32 saveSlot2Counter = 0 ;
u32 slotCheckField = 0 ;
bool8 securityPassed = FALSE ;
u8 saveSlot1Status ;
u8 saveSlot2Status ;
// check save slot 1.
2018-11-18 19:37:18 +01:00
for ( i = 0 ; i < SECTOR_SAVE_SLOT_LENGTH ; i + + )
2017-09-03 14:13:01 +02:00
{
DoReadFlashWholeSection ( i , gFastSaveSection ) ;
if ( gFastSaveSection - > security = = UNKNOWN_CHECK_VALUE )
{
securityPassed = TRUE ;
checksum = CalculateChecksum ( gFastSaveSection - > data , location [ gFastSaveSection - > id ] . size ) ;
if ( gFastSaveSection - > checksum = = checksum )
{
saveSlot1Counter = gFastSaveSection - > counter ;
slotCheckField | = 1 < < gFastSaveSection - > id ;
}
}
}
if ( securityPassed )
{
if ( slotCheckField = = 0x3FFF )
saveSlot1Status = 1 ;
else
saveSlot1Status = 255 ;
}
else
{
saveSlot1Status = 0 ;
}
slotCheckField = 0 ;
securityPassed = FALSE ;
// check save slot 2.
2018-11-18 19:37:18 +01:00
for ( i = 0 ; i < SECTOR_SAVE_SLOT_LENGTH ; i + + )
2017-09-03 14:13:01 +02:00
{
2018-11-18 19:37:18 +01:00
DoReadFlashWholeSection ( i + SECTOR_SAVE_SLOT_LENGTH , gFastSaveSection ) ;
2017-09-03 14:13:01 +02:00
if ( gFastSaveSection - > security = = UNKNOWN_CHECK_VALUE )
{
securityPassed = TRUE ;
checksum = CalculateChecksum ( gFastSaveSection - > data , location [ gFastSaveSection - > id ] . size ) ;
if ( gFastSaveSection - > checksum = = checksum )
{
saveSlot2Counter = gFastSaveSection - > counter ;
slotCheckField | = 1 < < gFastSaveSection - > id ;
}
}
}
if ( securityPassed )
{
if ( slotCheckField = = 0x3FFF )
saveSlot2Status = 1 ;
else
saveSlot2Status = 255 ;
}
else
{
saveSlot2Status = 0 ;
}
if ( saveSlot1Status = = 1 & & saveSlot2Status = = 1 )
{
if ( ( saveSlot1Counter = = - 1 & & saveSlot2Counter = = 0 ) | | ( saveSlot1Counter = = 0 & & saveSlot2Counter = = - 1 ) )
{
if ( ( unsigned ) ( saveSlot1Counter + 1 ) < ( unsigned ) ( saveSlot2Counter + 1 ) )
gSaveCounter = saveSlot2Counter ;
else
gSaveCounter = saveSlot1Counter ;
}
else
{
if ( saveSlot1Counter < saveSlot2Counter )
gSaveCounter = saveSlot2Counter ;
else
gSaveCounter = saveSlot1Counter ;
}
return 1 ;
}
if ( saveSlot1Status = = 1 )
{
gSaveCounter = saveSlot1Counter ;
if ( saveSlot2Status = = 255 )
return 255 ;
return 1 ;
}
if ( saveSlot2Status = = 1 )
{
gSaveCounter = saveSlot2Counter ;
if ( saveSlot1Status = = 255 )
return 255 ;
return 1 ;
}
if ( saveSlot1Status = = 0 & & saveSlot2Status = = 0 )
{
gSaveCounter = 0 ;
gLastWrittenSector = 0 ;
return 0 ;
}
gSaveCounter = 0 ;
gLastWrittenSector = 0 ;
return 2 ;
}
2018-11-18 19:37:18 +01:00
static u8 sub_81530DC ( u8 a1 , u8 * data , u16 size )
2017-09-03 14:13:01 +02:00
{
u16 i ;
2017-09-03 15:39:33 +02:00
struct SaveSection * section = & gSaveDataBuffer ;
2017-09-03 14:13:01 +02:00
DoReadFlashWholeSection ( a1 , section ) ;
if ( section - > security = = UNKNOWN_CHECK_VALUE )
{
u16 checksum = CalculateChecksum ( section - > data , size ) ;
if ( section - > id = = checksum )
{
for ( i = 0 ; i < size ; i + + )
data [ i ] = section - > data [ i ] ;
return 1 ;
}
else
{
return 2 ;
}
}
else
{
return 0 ;
}
}
2018-11-18 19:37:18 +01:00
static u8 DoReadFlashWholeSection ( u8 sector , struct SaveSection * section )
2017-09-03 14:13:01 +02:00
{
ReadFlash ( sector , 0 , section - > data , sizeof ( struct SaveSection ) ) ;
return 1 ;
}
2018-11-18 19:37:18 +01: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
{
2017-09-03 15:39:33 +02:00
int i = 0 ;
2017-09-03 14:13:01 +02:00
2017-09-29 10:06:36 +02:00
gRamSaveSectionLocations [ i ] . data = ( void * ) ( gSaveBlock2Ptr ) + gSaveSectionOffsets [ i ] . toAdd ;
gRamSaveSectionLocations [ i ] . size = gSaveSectionOffsets [ i ] . size ;
for ( i = 1 ; i < 5 ; i + + )
2017-09-03 14:13:01 +02:00
{
2017-09-29 10:06:36 +02:00
gRamSaveSectionLocations [ i ] . data = ( void * ) ( gSaveBlock1Ptr ) + gSaveSectionOffsets [ i ] . toAdd ;
2017-09-03 14:13:01 +02:00
gRamSaveSectionLocations [ i ] . size = gSaveSectionOffsets [ i ] . size ;
}
2017-09-29 10:06:36 +02:00
for ( i = 5 ; i < 14 ; i + + )
2017-09-03 14:13:01 +02:00
{
2017-09-29 10:06:36 +02:00
gRamSaveSectionLocations [ i ] . data = ( void * ) ( gPokemonStoragePtr ) + gSaveSectionOffsets [ i ] . toAdd ;
2017-09-03 14:13:01 +02:00
gRamSaveSectionLocations [ i ] . size = gSaveSectionOffsets [ i ] . size ;
2017-09-29 10:06:36 +02:00
i + + ; i - - ; // needed to match
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 )
{
2018-02-15 23:54:34 +01:00
case SAVE_HALL_OF_FAME_ERASE_BEFORE : // deletes HOF before overwriting HOF completely. unused
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 ) ;
2018-02-15 23:54:34 +01:00
case SAVE_HALL_OF_FAME : // hall of fame.
2017-09-03 14:13:01 +02:00
if ( GetGameStat ( GAME_STAT_ENTERED_HOF ) < 999 )
IncrementGameStat ( GAME_STAT_ENTERED_HOF ) ;
SaveSerializedGame ( ) ;
save_write_to_flash ( 0xFFFF , gRamSaveSectionLocations ) ;
2018-02-15 23:54:34 +01:00
tempAddr = gDecompressionBuffer ;
2018-11-18 19:37:18 +01:00
HandleWriteSectorNBytes ( SECTOR_ID_HOF_1 , tempAddr , 0xF80 ) ;
HandleWriteSectorNBytes ( SECTOR_ID_HOF_2 , tempAddr + 0xF80 , 0xF80 ) ;
2017-09-03 14:13:01 +02:00
break ;
2018-02-15 23:54:34 +01:00
case SAVE_NORMAL : // normal save. also called by overwriting your own save.
2017-09-03 14:13:01 +02:00
default :
SaveSerializedGame ( ) ;
save_write_to_flash ( 0xFFFF , gRamSaveSectionLocations ) ;
break ;
2018-02-15 23:54:34 +01:00
case SAVE_LINK : // _081532C4
case SAVE_LINK2 :
2017-09-03 14:13:01 +02:00
SaveSerializedGame ( ) ;
for ( i = 0 ; i < 5 ; i + + )
ClearSaveData_2 ( i , gRamSaveSectionLocations ) ;
for ( i = 0 ; i < 5 ; i + + )
sav12_xor_get ( i , gRamSaveSectionLocations ) ;
break ;
2018-11-18 19:37:18 +01:00
// Support for Ereader was removed in Emerald.
2017-09-03 14:13:01 +02:00
/*
case EREADER_SAVE : // used in mossdeep "game corner" before/after battling old man e-reader trainer
SaveSerializedGame ( ) ;
save_write_to_flash ( 0 , gRamSaveSectionLocations ) ;
break ;
*/
2018-02-15 23:54:34 +01:00
case SAVE_OVERWRITE_DIFFERENT_FILE :
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 ) ; // erase HOF.
SaveSerializedGame ( ) ;
save_write_to_flash ( 0xFFFF , gRamSaveSectionLocations ) ;
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
{
2018-10-30 21:45:26 +01:00
gUnknown_03006294 = 0xFF ;
return 0xFF ;
2017-09-03 14:13:01 +02:00
}
2018-10-30 21:45:26 +01:00
HandleSavingData ( saveType ) ;
if ( ! gDamagedSaveSectors )
{
gUnknown_03006294 = 1 ;
return 1 ;
}
else
{
DoSaveFailedScreen ( saveType ) ;
gUnknown_03006294 = 0xFF ;
return 0xFF ;
}
2017-09-03 14:13:01 +02:00
}
2018-10-30 21:45:26 +01:00
bool8 sub_8153380 ( void ) // trade.s save
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 ( ) ;
SaveSerializedGame ( ) ;
RestoreSaveBackupVarsAndIncrement ( gRamSaveSectionLocations ) ;
2018-10-30 21:45:26 +01:00
return FALSE ;
2017-09-03 14:13:01 +02:00
}
bool8 sub_81533AC ( void ) // trade.s save
{
2018-11-18 19:37:18 +01:00
u8 retVal = sub_81529D4 ( SECTOR_SAVE_SLOT_LENGTH , gRamSaveSectionLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
DoSaveFailedScreen ( 0 ) ;
if ( retVal = = 0xFF )
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
}
u8 sub_81533E0 ( void ) // trade.s save
{
2018-11-18 19:37:18 +01:00
sub_8152A34 ( SECTOR_SAVE_SLOT_LENGTH , gRamSaveSectionLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
DoSaveFailedScreen ( 0 ) ;
return 0 ;
}
u8 sub_8153408 ( void ) // trade.s save
{
2018-11-18 19:37:18 +01:00
sub_8152CAC ( SECTOR_SAVE_SLOT_LENGTH , gRamSaveSectionLocations ) ;
2017-09-03 14:13:01 +02:00
if ( gDamagedSaveSectors )
DoSaveFailedScreen ( 0 ) ;
return 0 ;
}
2019-02-11 02:42:50 +01:00
u8 FullSaveGame ( void )
2017-09-03 14:13:01 +02:00
{
if ( gFlashMemoryPresent ! = TRUE )
return 1 ;
UpdateSaveAddresses ( ) ;
SaveSerializedGame ( ) ;
RestoreSaveBackupVars ( gRamSaveSectionLocations ) ;
sub_8152A34 ( gUnknown_03006208 + 1 , gRamSaveSectionLocations ) ;
return 0 ;
}
2019-02-11 02:42:50 +01:00
bool8 CheckSaveFile ( void )
2017-09-03 14:13:01 +02:00
{
u8 retVal = FALSE ;
u16 val = + + gUnknown_03006208 ;
if ( val < = 4 )
{
sub_8152A34 ( gUnknown_03006208 + 1 , gRamSaveSectionLocations ) ;
sub_8152D44 ( val , gRamSaveSectionLocations ) ;
}
else
{
sub_8152D44 ( val , gRamSaveSectionLocations ) ;
retVal = TRUE ;
}
if ( gDamagedSaveSectors )
DoSaveFailedScreen ( 1 ) ;
return retVal ;
}
2018-02-15 23:54:34 +01:00
u8 Save_LoadGameData ( u8 a1 )
2017-09-03 14:13:01 +02:00
{
u8 result ;
if ( gFlashMemoryPresent ! = TRUE )
{
2017-09-03 22:50:17 +02:00
gSaveFileStatus = 4 ;
2017-09-03 14:13:01 +02:00
return 0xFF ;
}
UpdateSaveAddresses ( ) ;
switch ( a1 )
{
case 0 :
default :
result = sub_8152DD0 ( 0xFFFF , gRamSaveSectionLocations ) ;
LoadSerializedGame ( ) ;
2017-09-03 22:50:17 +02:00
gSaveFileStatus = result ;
2017-09-03 14:13:01 +02:00
gGameContinueCallback = 0 ;
break ;
case 3 :
result = sub_81530DC ( 0x1C , gDecompressionBuffer , 0xF80 ) ;
if ( result = = 1 )
result = sub_81530DC ( 0x1D , gDecompressionBuffer + 0xF80 , 0xF80 ) ;
break ;
}
return result ;
}
2017-09-03 15:39:33 +02:00
u16 sub_815355C ( void )
{
u16 i , v3 ;
struct SaveSection * savSection ;
savSection = gFastSaveSection = & gSaveDataBuffer ;
2018-04-29 14:21:59 +02:00
if ( gFlashMemoryPresent ! = TRUE )
2017-09-03 15:39:33 +02:00
return 0 ;
UpdateSaveAddresses ( ) ;
GetSaveValidStatus ( gRamSaveSectionLocations ) ;
2018-11-18 19:37:18 +01:00
v3 = SECTOR_SAVE_SLOT_LENGTH * ( gSaveCounter % 2 ) ;
for ( i = 0 ; i < SECTOR_SAVE_SLOT_LENGTH ; i + + )
2017-09-03 15:39:33 +02:00
{
DoReadFlashWholeSection ( i + v3 , gFastSaveSection ) ;
if ( gFastSaveSection - > id = = 0 )
return savSection - > data [ 10 ] +
savSection - > data [ 11 ] +
savSection - > data [ 12 ] +
savSection - > data [ 13 ] ;
}
return 0 ;
}
2019-04-04 17:55:18 +02:00
u32 TryReadSpecialSaveSection ( 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 )
2017-09-03 15:39:33 +02:00
return 0xFF ;
2017-10-07 05:30:35 +02:00
ReadFlash ( sector , 0 , ( u8 * ) & gSaveDataBuffer , sizeof ( struct SaveSection ) ) ;
2019-04-04 17:55:18 +02:00
if ( * ( u32 * ) ( & gSaveDataBuffer . data [ 0 ] ) ! = SPECIAL_SECTION_SENTINEL )
2017-09-03 15:39:33 +02:00
return 0xFF ;
// copies whole save section except u32 counter
i = 0 ;
size = 0xFFB ;
savData = & gSaveDataBuffer . data [ 4 ] ;
for ( ; i < = size ; i + + )
dst [ i ] = savData [ i ] ;
return 1 ;
}
2019-04-04 17:55:18 +02:00
u32 TryWriteSpecialSaveSection ( 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 )
2017-09-03 15:39:33 +02:00
return 0xFF ;
2017-11-13 18:07:23 +01:00
2017-09-03 15:39:33 +02:00
savDataBuffer = & gSaveDataBuffer ;
2019-04-04 17:55:18 +02:00
* ( u32 * ) ( savDataBuffer ) = SPECIAL_SECTION_SENTINEL ;
2017-09-03 15:39:33 +02:00
// copies whole save section except u32 counter
i = 0 ;
size = 0xFFB ;
savData = & gSaveDataBuffer . data [ 4 ] ;
for ( ; i < = size ; i + + )
savData [ i ] = src [ i ] ;
if ( ProgramFlashSectorAndVerify ( sector , savDataBuffer ) ! = 0 )
return 0xFF ;
return 1 ;
}
2017-09-29 10:06:36 +02:00
void sub_8153688 ( u8 taskId )
{
s16 * taskData = gTasks [ taskId ] . data ;
switch ( taskData [ 0 ] )
{
case 0 :
gSoftResetDisabled = TRUE ;
taskData [ 0 ] = 1 ;
break ;
case 1 :
sub_800ADF8 ( ) ;
taskData [ 0 ] = 2 ;
break ;
case 2 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2017-09-29 10:06:36 +02:00
{
if ( taskData [ 2 ] = = 0 )
save_serialize_map ( ) ;
taskData [ 0 ] = 3 ;
}
break ;
case 3 :
if ( taskData [ 2 ] = = 0 )
2018-12-27 23:30:47 +01:00
SetContinueGameWarpStatusToDynamicWarp ( ) ;
2017-09-29 10:06:36 +02:00
sub_8153380 ( ) ;
taskData [ 0 ] = 4 ;
break ;
case 4 :
if ( + + taskData [ 1 ] = = 5 )
{
taskData [ 1 ] = 0 ;
taskData [ 0 ] = 5 ;
}
break ;
case 5 :
if ( sub_81533AC ( ) )
taskData [ 0 ] = 6 ;
else
taskData [ 0 ] = 4 ;
break ;
case 6 :
sub_81533E0 ( ) ;
taskData [ 0 ] = 7 ;
break ;
case 7 :
if ( taskData [ 2 ] = = 0 )
2018-12-27 23:30:47 +01:00
ClearContinueGameWarpStatus2 ( ) ;
2017-09-29 10:06:36 +02:00
sub_800ADF8 ( ) ;
taskData [ 0 ] = 8 ;
break ;
case 8 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2017-09-29 10:06:36 +02:00
{
sub_8153408 ( ) ;
taskData [ 0 ] = 9 ;
}
break ;
case 9 :
sub_800ADF8 ( ) ;
taskData [ 0 ] = 10 ;
break ;
case 10 :
2018-12-31 09:22:21 +01:00
if ( IsLinkTaskFinished ( ) )
2017-09-29 10:06:36 +02:00
taskData [ 0 ] + + ;
break ;
case 11 :
if ( + + taskData [ 1 ] > 5 )
{
gSoftResetDisabled = FALSE ;
DestroyTask ( taskId ) ;
}
break ;
}
}