2017-10-06 23:33:35 +01:00
# include "global.h"
# include "text.h"
# include "main.h"
# include "palette.h"
# include "gpu_regs.h"
# include "bg.h"
# include "task.h"
# include "window.h"
# include "menu.h"
# include "save.h"
# include "gba/flash_internal.h"
# define MSG_WIN_TOP 12
# define CLOCK_WIN_TOP (MSG_WIN_TOP - 4)
extern void AddTextPrinterParametrized2 ( u8 windowId , u8 fontId , u8 x , u8 y , u8 letterSpacing , u8 lineSpacing , struct TextColor * color , u8 speed , u8 * str ) ;
extern void ( * gGameContinueCallback ) ( void ) ;
extern u32 gDamagedSaveSectors ;
2017-10-07 00:48:18 +01:00
extern u16 gSaveFailedType ;
extern const u8 gBirchHelpGfx [ ] ;
extern const u8 gBirchBagTilemap [ ] ;
extern const u8 gBirchGrassTilemap [ ] ;
extern const u8 gSaveFailedClockGfx [ ] ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
extern const struct OamData gClockOamData ; // sClockOamData
extern const u8 gClockFrames [ 8 ] [ 3 ] ; // sClockFrames
2017-10-06 23:33:35 +01:00
extern const struct BgTemplate gUnknown_085EFD88 [ ] ;
extern const struct WindowTemplate gUnknown_085EFD94 [ ] ;
extern struct WindowTemplate gUnknown_085EFD9C ;
extern struct WindowTemplate gUnknown_085EFDA4 ;
extern struct SaveSection gSaveDataBuffer ;
extern const u32 gUnknown_0850E87C [ ] ;
2017-10-07 00:48:18 +01:00
extern const u16 gBirchBagGrassPal [ ] ;
extern const u16 gSaveFailedClockPal [ ] ;
2017-10-06 23:33:35 +01:00
extern const u16 gUnknown_0850FEFC [ ] ;
extern const u16 gUnknown_0860F074 [ ] ;
extern u8 gText_SaveFailedCheckingBackup [ ] ;
extern u8 gText_BackupMemoryDamaged [ ] ;
extern u8 gText_CheckCompleted [ ] ;
extern u8 gText_SaveCompleteGameCannotContinue [ ] ;
extern u8 gText_SaveCompletePressA [ ] ;
extern u8 gText_GamePlayCannotBeContinued [ ] ;
extern u8 gDecompressionBuffer [ ] ;
2017-10-07 00:48:18 +01:00
struct ClockInfo
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
bool16 clockRunning ;
u16 debugTimer ; // appears to be unused; it's only set to 0 and never used within the game. perhaps it was a volatile-like timer expected to be modified by an external tool?
2017-10-06 23:33:35 +01:00
} ;
2017-10-07 00:48:18 +01:00
extern struct ClockInfo gSaveFailedClockInfo ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
struct WindowIds
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
u8 textWindowId ;
u8 clockWindowId ;
2017-10-06 23:33:35 +01:00
} ;
2017-10-07 00:48:18 +01:00
extern struct WindowIds gSaveFailedWindowIds ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
static void CB2_SaveFailedScreen ( void ) ;
static void CB2_WipeSave ( void ) ;
static void CB2_GameplayCannotBeContinued ( void ) ;
static void CB2_FadeAndReturnToTitleScreen ( void ) ;
static void CB2_ReturnToTitleScreen ( void ) ;
static void VBlankCB_UpdateClockGraphics ( void ) ;
static bool8 VerifySectorWipe ( u16 sector ) ;
static bool8 WipeSectors ( u32 ) ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
// although this is a general text printer, it's only used in this file.
static void SaveFailedScreenTextPrint ( u8 * text , u8 var1 , u8 var2 )
2017-10-06 23:33:35 +01:00
{
struct TextColor color ;
color . fgColor = 0 ;
color . bgColor = 15 ;
color . shadowColor = 3 ;
2017-10-07 00:48:18 +01:00
AddTextPrinterParametrized2 ( gSaveFailedWindowIds . textWindowId , 1 , var1 * 8 , var2 * 8 + 1 , 0 , 0 , & color , 0 , text ) ;
2017-10-06 23:33:35 +01:00
}
void DoSaveFailedScreen ( u8 saveType )
{
2017-10-07 00:48:18 +01:00
SetMainCallback2 ( CB2_SaveFailedScreen ) ;
gSaveFailedType = saveType ;
gSaveFailedClockInfo . clockRunning = FALSE ;
gSaveFailedClockInfo . debugTimer = 0 ;
gSaveFailedWindowIds . textWindowId = 0 ;
gSaveFailedWindowIds . clockWindowId = 0 ;
2017-10-06 23:33:35 +01:00
}
2017-10-07 00:48:18 +01:00
static void VBlankCB ( void )
2017-10-06 23:33:35 +01:00
{
LoadOam ( ) ;
ProcessSpriteCopyRequests ( ) ;
TransferPlttBuffer ( ) ;
}
2017-10-07 00:48:18 +01:00
static void CB2_SaveFailedScreen ( void )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
switch ( gMain . state )
2017-10-06 23:33:35 +01:00
{
case 0 :
default :
SetVBlankCallback ( NULL ) ;
SetGpuReg ( REG_OFFSET_DISPCNT , 0 ) ;
SetGpuReg ( REG_OFFSET_BG3CNT , 0 ) ;
SetGpuReg ( REG_OFFSET_BG2CNT , 0 ) ;
SetGpuReg ( REG_OFFSET_BG1CNT , 0 ) ;
SetGpuReg ( REG_OFFSET_BG0CNT , 0 ) ;
SetGpuReg ( REG_OFFSET_BG3HOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG3VOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG2HOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG2VOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG1HOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG1VOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG0HOFS , 0 ) ;
SetGpuReg ( REG_OFFSET_BG0VOFS , 0 ) ;
// how come this doesnt use the Dma manager?
DmaFill16 ( 3 , 0 , VRAM , VRAM_SIZE ) ;
DmaFill32 ( 3 , 0 , OAM , OAM_SIZE ) ;
DmaFill16 ( 3 , 0 , PLTT , PLTT_SIZE ) ;
2017-10-07 00:48:18 +01:00
LZ77UnCompVram ( gBirchHelpGfx , ( void * ) VRAM ) ;
LZ77UnCompVram ( gBirchBagTilemap , ( void * ) ( VRAM + 0x7000 ) ) ;
LZ77UnCompVram ( gBirchGrassTilemap , ( void * ) ( VRAM + 0x7800 ) ) ;
LZ77UnCompVram ( gSaveFailedClockGfx , ( void * ) ( VRAM + 0x10020 ) ) ;
2017-10-06 23:33:35 +01:00
ResetBgsAndClearDma3BusyFlags ( 0 ) ;
InitBgsFromTemplates ( 0 , gUnknown_085EFD88 , 3 ) ;
SetBgTilemapBuffer ( 0 , ( void * ) & gDecompressionBuffer [ 0x2000 ] ) ;
CpuFill32 ( 0 , & gDecompressionBuffer [ 0x2000 ] , 0x800 ) ;
LoadBgTiles ( 0 , gUnknown_0850E87C , 0x120 , 0x214 ) ;
InitWindows ( gUnknown_085EFD94 ) ;
// AddWindowWithoutTileMap returns a u16/integer, but the info is clobbered into a u8 here resulting in lost info. Bug?
2017-10-07 00:48:18 +01:00
gSaveFailedWindowIds . textWindowId = AddWindowWithoutTileMap ( & gUnknown_085EFD9C ) ;
SetWindowAttribute ( gSaveFailedWindowIds . textWindowId , 7 , ( u32 ) & gDecompressionBuffer [ 0x2800 ] ) ;
gSaveFailedWindowIds . clockWindowId = AddWindowWithoutTileMap ( & gUnknown_085EFDA4 ) ;
SetWindowAttribute ( gSaveFailedWindowIds . clockWindowId , 7 , ( u32 ) & gDecompressionBuffer [ 0x3D00 ] ) ;
2017-10-06 23:33:35 +01:00
DeactivateAllTextPrinters ( ) ;
ResetSpriteData ( ) ;
ResetTasks ( ) ;
ResetPaletteFade ( ) ;
2017-10-07 00:48:18 +01:00
LoadPalette ( gBirchBagGrassPal , 0 , 0x40 ) ;
LoadPalette ( gSaveFailedClockPal , 0x100 , 0x20 ) ;
2017-10-06 23:33:35 +01:00
LoadPalette ( gUnknown_0850FEFC , 0xE0 , 0x20 ) ;
LoadPalette ( gUnknown_0860F074 , 0xF0 , 0x20 ) ;
2017-10-07 00:48:18 +01:00
SetWindowBorderStyle ( gSaveFailedWindowIds . textWindowId , FALSE , 0x214 , 0xE ) ;
SetWindowBorderStyle ( gSaveFailedWindowIds . clockWindowId , FALSE , 0x214 , 0xE ) ;
FillWindowPixelBuffer ( gSaveFailedWindowIds . clockWindowId , 0x11 ) ; // backwards?
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
CopyWindowToVram ( gSaveFailedWindowIds . clockWindowId , 2 ) ; // again?
CopyWindowToVram ( gSaveFailedWindowIds . textWindowId , 1 ) ;
SaveFailedScreenTextPrint ( gText_SaveFailedCheckingBackup , 1 , 0 ) ;
2017-10-06 23:33:35 +01:00
BeginNormalPaletteFade ( 0xFFFFFFFF , 0 , 16 , 0 , 0 ) ;
EnableInterrupts ( 1 ) ;
2017-10-07 00:48:18 +01:00
SetVBlankCallback ( VBlankCB ) ;
2017-10-06 23:33:35 +01:00
SetGpuReg ( 0 , 0x1040 ) ;
ShowBg ( 0 ) ;
ShowBg ( 2 ) ;
ShowBg ( 3 ) ;
gMain . state + + ;
break ;
case 1 :
2017-10-07 00:48:18 +01:00
if ( ! UpdatePaletteFade ( ) )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
SetMainCallback2 ( CB2_WipeSave ) ;
SetVBlankCallback ( VBlankCB_UpdateClockGraphics ) ;
2017-10-06 23:33:35 +01:00
}
break ;
}
}
2017-10-07 00:48:18 +01:00
static void CB2_WipeSave ( void )
2017-10-06 23:33:35 +01:00
{
u8 wipeTries = 0 ;
2017-10-07 00:48:18 +01:00
gSaveFailedClockInfo . clockRunning = TRUE ;
2017-10-06 23:33:35 +01:00
while ( gDamagedSaveSectors ! = 0 & & wipeTries < 3 )
{
2017-10-07 00:48:18 +01:00
if ( WipeSectors ( gDamagedSaveSectors ) ! = FALSE )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
SaveFailedScreenTextPrint ( gText_BackupMemoryDamaged , 1 , 0 ) ;
SetMainCallback2 ( CB2_GameplayCannotBeContinued ) ;
2017-10-06 23:33:35 +01:00
return ;
}
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
SaveFailedScreenTextPrint ( gText_CheckCompleted , 1 , 0 ) ;
HandleSavingData ( gSaveFailedType ) ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
if ( gDamagedSaveSectors ! = 0 )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
SaveFailedScreenTextPrint ( gText_SaveFailedCheckingBackup , 1 , 0 ) ;
2017-10-06 23:33:35 +01:00
}
wipeTries + + ;
}
2017-10-07 00:48:18 +01:00
if ( wipeTries = = 3 )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
SaveFailedScreenTextPrint ( gText_BackupMemoryDamaged , 1 , 0 ) ;
2017-10-06 23:33:35 +01:00
}
else
{
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
if ( gGameContinueCallback = = NULL )
SaveFailedScreenTextPrint ( gText_SaveCompleteGameCannotContinue , 1 , 0 ) ;
2017-10-06 23:33:35 +01:00
else
2017-10-07 00:48:18 +01:00
SaveFailedScreenTextPrint ( gText_SaveCompletePressA , 1 , 0 ) ;
2017-10-06 23:33:35 +01:00
}
2017-10-07 00:48:18 +01:00
SetMainCallback2 ( CB2_FadeAndReturnToTitleScreen ) ;
2017-10-06 23:33:35 +01:00
}
2017-10-07 00:48:18 +01:00
static void CB2_GameplayCannotBeContinued ( void )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
gSaveFailedClockInfo . clockRunning = FALSE ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
if ( gMain . newKeys & A_BUTTON )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
FillWindowPixelBuffer ( gSaveFailedWindowIds . textWindowId , 0x11 ) ;
SaveFailedScreenTextPrint ( gText_GamePlayCannotBeContinued , 1 , 0 ) ;
SetVBlankCallback ( VBlankCB ) ;
SetMainCallback2 ( CB2_FadeAndReturnToTitleScreen ) ;
2017-10-06 23:33:35 +01:00
}
}
2017-10-07 00:48:18 +01:00
static void CB2_FadeAndReturnToTitleScreen ( void )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
gSaveFailedClockInfo . clockRunning = FALSE ;
2017-10-06 23:33:35 +01:00
2017-10-07 00:48:18 +01:00
if ( gMain . newKeys & A_BUTTON )
2017-10-06 23:33:35 +01:00
{
BeginNormalPaletteFade ( 0xFFFFFFFF , 0 , 0 , 16 , 0 ) ;
2017-10-07 00:48:18 +01:00
SetVBlankCallback ( VBlankCB ) ;
SetMainCallback2 ( CB2_ReturnToTitleScreen ) ;
2017-10-06 23:33:35 +01:00
}
}
2017-10-07 00:48:18 +01:00
void CB2_ReturnToTitleScreen ( void )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
if ( ! UpdatePaletteFade ( ) )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
if ( gGameContinueCallback = = NULL ) // no callback exists, so do a soft reset.
2017-10-06 23:33:35 +01:00
{
DoSoftReset ( ) ;
}
else
{
SetMainCallback2 ( ( MainCallback ) gGameContinueCallback ) ;
gGameContinueCallback = NULL ;
}
}
}
2017-10-07 00:48:18 +01:00
static void VBlankCB_UpdateClockGraphics ( void )
2017-10-06 23:33:35 +01:00
{
unsigned int n = ( gMain . vblankCounter2 > > 3 ) & 7 ;
2017-10-07 00:48:18 +01:00
gMain . oamBuffer [ 0 ] = gClockOamData ;
2017-10-06 23:33:35 +01:00
gMain . oamBuffer [ 0 ] . x = 112 ;
gMain . oamBuffer [ 0 ] . y = ( CLOCK_WIN_TOP + 1 ) * 8 ; ;
2017-10-07 00:48:18 +01:00
if ( gSaveFailedClockInfo . clockRunning ! = FALSE )
2017-10-06 23:33:35 +01:00
{
2017-10-07 00:48:18 +01:00
gMain . oamBuffer [ 0 ] . tileNum = gClockFrames [ n ] [ 0 ] ;
gMain . oamBuffer [ 0 ] . matrixNum = ( gClockFrames [ n ] [ 2 ] < < 4 ) | ( gClockFrames [ n ] [ 1 ] < < 3 ) ;
2017-10-06 23:33:35 +01:00
}
else
{
gMain . oamBuffer [ 0 ] . tileNum = 1 ;
}
CpuFastCopy ( gMain . oamBuffer , ( void * ) OAM , 4 ) ;
2017-10-07 00:48:18 +01:00
if ( gSaveFailedClockInfo . debugTimer )
gSaveFailedClockInfo . debugTimer - - ;
2017-10-06 23:33:35 +01:00
}
2017-10-07 00:48:18 +01:00
static bool8 VerifySectorWipe ( u16 sector )
2017-10-06 23:33:35 +01:00
{
u32 * ptr = ( u32 * ) & gSaveDataBuffer ;
u16 i ;
ReadFlash ( sector , 0 , ( u8 * ) ptr , 4096 ) ;
for ( i = 0 ; i < 0x400 ; i + + , ptr + + )
if ( * ptr )
return TRUE ;
return FALSE ;
}
2017-10-07 00:48:18 +01:00
static bool8 WipeSector ( u16 sector )
2017-10-06 23:33:35 +01:00
{
u16 i , j ;
bool8 failed = TRUE ;
for ( i = 0 ; failed & & i < 130 ; i + + )
{
for ( j = 0 ; j < 0x1000 ; j + + )
ProgramFlashByte ( sector , j , 0 ) ;
2017-10-07 00:48:18 +01:00
failed = VerifySectorWipe ( sector ) ;
2017-10-06 23:33:35 +01:00
}
return failed ;
}
2017-10-07 00:48:18 +01:00
static bool8 WipeSectors ( u32 sectorBits )
2017-10-06 23:33:35 +01:00
{
u16 i ;
for ( i = 0 ; i < 0x20 ; i + + )
2017-10-07 00:48:18 +01:00
if ( ( sectorBits & ( 1 < < i ) ) & & ! WipeSector ( i ) )
2017-10-06 23:33:35 +01:00
sectorBits & = ~ ( 1 < < i ) ;
if ( sectorBits = = 0 )
return FALSE ;
else
return TRUE ;
}