465 lines
13 KiB
C
465 lines
13 KiB
C
#include "global.h"
|
||
#include "text.h"
|
||
#include "main.h"
|
||
#include "malloc.h"
|
||
#include "palette.h"
|
||
#include "graphics.h"
|
||
#include "gpu_regs.h"
|
||
#include "bg.h"
|
||
#include "decompress.h"
|
||
#include "task.h"
|
||
#include "window.h"
|
||
#include "menu.h"
|
||
#include "save.h"
|
||
#include "starter_choose.h"
|
||
#include "gba/flash_internal.h"
|
||
#include "text_window.h"
|
||
#include "constants/rgb.h"
|
||
|
||
#define MSG_WIN_TOP 12
|
||
#define CLOCK_WIN_TOP (MSG_WIN_TOP - 4)
|
||
|
||
extern const u8 gText_SaveFailedCheckingBackup[];
|
||
extern const u8 gText_BackupMemoryDamaged[];
|
||
extern const u8 gText_CheckCompleted[];
|
||
extern const u8 gText_SaveCompleteGameCannotContinue[];
|
||
extern const u8 gText_SaveCompletePressA[];
|
||
extern const u8 gText_GamePlayCannotBeContinued[];
|
||
|
||
// sClockInfo enum
|
||
enum
|
||
{
|
||
CLOCK_RUNNING,
|
||
DEBUG_TIMER
|
||
};
|
||
|
||
// sWindowIds enum
|
||
enum
|
||
{
|
||
TEXT_WIN_ID,
|
||
CLOCK_WIN_ID
|
||
};
|
||
|
||
static EWRAM_DATA u16 sSaveFailedType = {0};
|
||
static EWRAM_DATA u16 sClockInfo[2] = {0};
|
||
static EWRAM_DATA u8 sWindowIds[2] = {0};
|
||
|
||
static const struct OamData sClockOamData =
|
||
{
|
||
.y = DISPLAY_HEIGHT,
|
||
.affineMode = ST_OAM_AFFINE_OFF,
|
||
.objMode = ST_OAM_OBJ_NORMAL,
|
||
.mosaic = FALSE,
|
||
.bpp = ST_OAM_4BPP,
|
||
.shape = SPRITE_SHAPE(16x16),
|
||
.x = 0,
|
||
.matrixNum = 0,
|
||
.size = SPRITE_SIZE(16x16),
|
||
.tileNum = 0,
|
||
.priority = 0,
|
||
.paletteNum = 0,
|
||
.affineParam = 0
|
||
};
|
||
|
||
static const struct BgTemplate sBgTemplates[3] =
|
||
{
|
||
{
|
||
.bg = 0,
|
||
.charBaseIndex = 2,
|
||
.mapBaseIndex = 31,
|
||
.screenSize = 0,
|
||
.paletteMode = 0,
|
||
.priority = 0,
|
||
.baseTile = 0,
|
||
},
|
||
{
|
||
.bg = 2,
|
||
.charBaseIndex = 0,
|
||
.mapBaseIndex = 14,
|
||
.screenSize = 0,
|
||
.paletteMode = 0,
|
||
.priority = 2,
|
||
.baseTile = 0,
|
||
},
|
||
{
|
||
.bg = 3,
|
||
.charBaseIndex = 0,
|
||
.mapBaseIndex = 15,
|
||
.screenSize = 0,
|
||
.paletteMode = 0,
|
||
.priority = 3,
|
||
.baseTile = 0,
|
||
},
|
||
};
|
||
|
||
static const struct WindowTemplate sDummyWindowTemplate[] = { DUMMY_WIN_TEMPLATE };
|
||
|
||
static const struct WindowTemplate sWindowTemplate_Text[] =
|
||
{
|
||
{
|
||
.bg = 0,
|
||
.tilemapLeft = 1,
|
||
.tilemapTop = 13,
|
||
.width = 28,
|
||
.height = 6,
|
||
.paletteNum = 15,
|
||
.baseBlock = 1,
|
||
}
|
||
};
|
||
|
||
static const struct WindowTemplate sWindowTemplate_Clock[] =
|
||
{
|
||
{
|
||
.bg = 0,
|
||
.tilemapLeft = 14,
|
||
.tilemapTop = 9,
|
||
.width = 2,
|
||
.height = 2,
|
||
.paletteNum = 15,
|
||
.baseBlock = 169,
|
||
}
|
||
};
|
||
|
||
static const u8 sClockFrames[8][3] =
|
||
{
|
||
{ 1, 0, 0 },
|
||
{ 5, 0, 0 },
|
||
{ 9, 0, 0 },
|
||
{ 5, 0, 1 },
|
||
{ 1, 0, 1 },
|
||
{ 5, 1, 1 },
|
||
{ 9, 1, 0 },
|
||
{ 5, 1, 0 },
|
||
};
|
||
|
||
static const u8 sSaveFailedClockPal[] = INCBIN_U8("graphics/misc/clock_small.gbapal");
|
||
static const u32 sSaveFailedClockGfx[] = INCBIN_U32("graphics/misc/clock_small.4bpp.smol");
|
||
|
||
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);
|
||
|
||
// Although this is a general text printer, it's only used in this file.
|
||
static void SaveFailedScreenTextPrint(const u8 *text, u8 x, u8 y)
|
||
{
|
||
u8 color[3];
|
||
|
||
color[0] = TEXT_COLOR_TRANSPARENT;
|
||
color[1] = TEXT_DYNAMIC_COLOR_6;
|
||
color[2] = TEXT_COLOR_LIGHT_GRAY;
|
||
AddTextPrinterParameterized4(sWindowIds[TEXT_WIN_ID], FONT_NORMAL, x * 8, y * 8 + 1, 0, 0, color, 0, text);
|
||
}
|
||
|
||
void DoSaveFailedScreen(u8 saveType)
|
||
{
|
||
SetMainCallback2(CB2_SaveFailedScreen);
|
||
sSaveFailedType = saveType;
|
||
sClockInfo[CLOCK_RUNNING] = FALSE;
|
||
sClockInfo[DEBUG_TIMER] = 0;
|
||
sWindowIds[TEXT_WIN_ID] = 0;
|
||
sWindowIds[CLOCK_WIN_ID] = 0;
|
||
}
|
||
|
||
static void VBlankCB(void)
|
||
{
|
||
LoadOam();
|
||
ProcessSpriteCopyRequests();
|
||
TransferPlttBuffer();
|
||
}
|
||
|
||
struct SaveFailedBuffers
|
||
{
|
||
ALIGNED(4) u8 tilemapBuffer[BG_SCREEN_SIZE];
|
||
ALIGNED(4) u8 window1TileData[0x200];
|
||
ALIGNED(4) u8 window2TileData[0x200];
|
||
};
|
||
|
||
static EWRAM_DATA struct SaveFailedBuffers *sSaveFailedBuffers = NULL;
|
||
|
||
static void CB2_SaveFailedScreen(void)
|
||
{
|
||
switch (gMain.state)
|
||
{
|
||
case 0:
|
||
default:
|
||
SetVBlankCallback(NULL);
|
||
sSaveFailedBuffers = Alloc(sizeof(*sSaveFailedBuffers));
|
||
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);
|
||
DmaFill16(3, 0, VRAM, VRAM_SIZE);
|
||
DmaFill32(3, 0, OAM, OAM_SIZE);
|
||
DmaFill16(3, 0, PLTT, PLTT_SIZE);
|
||
DecompressDataWithHeaderVram(gBirchBagGrass_Gfx, (void *)VRAM);
|
||
DecompressDataWithHeaderVram(gBirchBagTilemap, (void *)(BG_SCREEN_ADDR(14)));
|
||
DecompressDataWithHeaderVram(gBirchGrassTilemap, (void *)(BG_SCREEN_ADDR(15)));
|
||
DecompressDataWithHeaderVram(sSaveFailedClockGfx, (void *)(OBJ_VRAM0 + 0x20));
|
||
ResetBgsAndClearDma3BusyFlags(0);
|
||
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
|
||
SetBgTilemapBuffer(0, sSaveFailedBuffers->tilemapBuffer);
|
||
CpuFill32(0, sSaveFailedBuffers->tilemapBuffer, BG_SCREEN_SIZE);
|
||
LoadBgTiles(0, gTextWindowFrame1_Gfx, 0x120, 0x214);
|
||
InitWindows(sDummyWindowTemplate);
|
||
sWindowIds[TEXT_WIN_ID] = AddWindowWithoutTileMap(sWindowTemplate_Text);
|
||
SetWindowAttribute(sWindowIds[TEXT_WIN_ID], 7, (u32)&sSaveFailedBuffers->window1TileData);
|
||
sWindowIds[CLOCK_WIN_ID] = AddWindowWithoutTileMap(sWindowTemplate_Clock);
|
||
SetWindowAttribute(sWindowIds[CLOCK_WIN_ID], 7, (u32)&sSaveFailedBuffers->window2TileData);
|
||
DeactivateAllTextPrinters();
|
||
ResetSpriteData();
|
||
ResetTasks();
|
||
ResetPaletteFade();
|
||
LoadPalette(gBirchBagGrass_Pal, BG_PLTT_ID(0), 2 * PLTT_SIZE_4BPP);
|
||
LoadPalette(sSaveFailedClockPal, OBJ_PLTT_ID(0), PLTT_SIZE_4BPP);
|
||
LoadPalette(gTextWindowFrame1_Pal, BG_PLTT_ID(14), PLTT_SIZE_4BPP);
|
||
LoadPalette(gStandardMenuPalette, BG_PLTT_ID(15), PLTT_SIZE_4BPP);
|
||
DrawStdFrameWithCustomTileAndPalette(sWindowIds[TEXT_WIN_ID], FALSE, 0x214, 0xE);
|
||
DrawStdFrameWithCustomTileAndPalette(sWindowIds[CLOCK_WIN_ID], FALSE, 0x214, 0xE);
|
||
FillWindowPixelBuffer(sWindowIds[CLOCK_WIN_ID], PIXEL_FILL(1)); // backwards?
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
CopyWindowToVram(sWindowIds[CLOCK_WIN_ID], COPYWIN_GFX); // again?
|
||
CopyWindowToVram(sWindowIds[TEXT_WIN_ID], COPYWIN_MAP);
|
||
SaveFailedScreenTextPrint(gText_SaveFailedCheckingBackup, 1, 0);
|
||
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
|
||
EnableInterrupts(1);
|
||
SetVBlankCallback(VBlankCB);
|
||
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
|
||
ShowBg(0);
|
||
ShowBg(2);
|
||
ShowBg(3);
|
||
gMain.state++;
|
||
break;
|
||
case 1:
|
||
if (!UpdatePaletteFade())
|
||
{
|
||
SetMainCallback2(CB2_WipeSave);
|
||
SetVBlankCallback(VBlankCB_UpdateClockGraphics);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void CB2_WipeSave(void)
|
||
{
|
||
u8 wipeTries = 0;
|
||
|
||
sClockInfo[CLOCK_RUNNING] = TRUE;
|
||
|
||
while (gDamagedSaveSectors != 0 && wipeTries < 3)
|
||
{
|
||
if (WipeSectors(gDamagedSaveSectors))
|
||
{
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
SaveFailedScreenTextPrint(gText_BackupMemoryDamaged, 1, 0);
|
||
SetMainCallback2(CB2_GameplayCannotBeContinued);
|
||
return;
|
||
}
|
||
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
SaveFailedScreenTextPrint(gText_CheckCompleted, 1, 0);
|
||
HandleSavingData(sSaveFailedType);
|
||
|
||
if (gDamagedSaveSectors != 0)
|
||
{
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
SaveFailedScreenTextPrint(gText_SaveFailedCheckingBackup, 1, 0);
|
||
}
|
||
|
||
wipeTries++;
|
||
}
|
||
|
||
if (wipeTries == 3)
|
||
{
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
SaveFailedScreenTextPrint(gText_BackupMemoryDamaged, 1, 0);
|
||
}
|
||
else
|
||
{
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
|
||
if (gGameContinueCallback == NULL)
|
||
SaveFailedScreenTextPrint(gText_SaveCompleteGameCannotContinue, 1, 0);
|
||
else
|
||
SaveFailedScreenTextPrint(gText_SaveCompletePressA, 1, 0);
|
||
}
|
||
|
||
SetMainCallback2(CB2_FadeAndReturnToTitleScreen);
|
||
}
|
||
|
||
static void CB2_GameplayCannotBeContinued(void)
|
||
{
|
||
sClockInfo[CLOCK_RUNNING] = FALSE;
|
||
|
||
if (JOY_NEW(A_BUTTON))
|
||
{
|
||
FillWindowPixelBuffer(sWindowIds[TEXT_WIN_ID], PIXEL_FILL(1));
|
||
SaveFailedScreenTextPrint(gText_GamePlayCannotBeContinued, 1, 0);
|
||
SetVBlankCallback(VBlankCB);
|
||
SetMainCallback2(CB2_FadeAndReturnToTitleScreen);
|
||
}
|
||
}
|
||
|
||
static void CB2_FadeAndReturnToTitleScreen(void)
|
||
{
|
||
sClockInfo[CLOCK_RUNNING] = FALSE;
|
||
|
||
if (JOY_NEW(A_BUTTON))
|
||
{
|
||
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
||
SetVBlankCallback(VBlankCB);
|
||
SetMainCallback2(CB2_ReturnToTitleScreen);
|
||
}
|
||
}
|
||
|
||
static void CB2_ReturnToTitleScreen(void)
|
||
{
|
||
if (!UpdatePaletteFade())
|
||
{
|
||
TRY_FREE_AND_SET_NULL(sSaveFailedBuffers);
|
||
if (gGameContinueCallback == NULL) // no callback exists, so do a soft reset.
|
||
{
|
||
DoSoftReset();
|
||
}
|
||
else
|
||
{
|
||
SetMainCallback2((MainCallback)gGameContinueCallback);
|
||
gGameContinueCallback = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void VBlankCB_UpdateClockGraphics(void)
|
||
{
|
||
u32 n = (gMain.vblankCounter2 >> 3) & 7;
|
||
|
||
gMain.oamBuffer[0] = sClockOamData;
|
||
gMain.oamBuffer[0].x = 112;
|
||
gMain.oamBuffer[0].y = (CLOCK_WIN_TOP + 1) * 8;
|
||
|
||
if (sClockInfo[CLOCK_RUNNING])
|
||
{
|
||
gMain.oamBuffer[0].tileNum = sClockFrames[n][0];
|
||
gMain.oamBuffer[0].matrixNum = (sClockFrames[n][2] << 4) | (sClockFrames[n][1] << 3);
|
||
}
|
||
else
|
||
{
|
||
gMain.oamBuffer[0].tileNum = 1;
|
||
}
|
||
|
||
CpuFastCopy(gMain.oamBuffer, (void *)OAM, 4);
|
||
|
||
if (sClockInfo[DEBUG_TIMER])
|
||
sClockInfo[DEBUG_TIMER]--;
|
||
}
|
||
|
||
static bool8 VerifySectorWipe(u16 sector)
|
||
{
|
||
u32 *ptr = (u32 *)&gSaveDataBuffer;
|
||
u16 i;
|
||
|
||
ReadFlash(sector, 0, (u8 *)ptr, SECTOR_SIZE);
|
||
|
||
// 1/4 because ptr is u32
|
||
for (i = 0; i < SECTOR_SIZE / 4; i++, ptr++)
|
||
if (*ptr)
|
||
return TRUE; // Sector has nonzero data, failed
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static bool8 WipeSector(u16 sector)
|
||
{
|
||
u16 i, j;
|
||
bool8 failed = TRUE;
|
||
|
||
// Attempt to wipe sector with an arbitrary attempt limit of 130
|
||
for (i = 0; failed && i < 130; i++)
|
||
{
|
||
for (j = 0; j < SECTOR_SIZE; j++)
|
||
ProgramFlashByte(sector, j, 0);
|
||
|
||
failed = VerifySectorWipe(sector);
|
||
}
|
||
|
||
return failed;
|
||
}
|
||
|
||
static bool8 WipeSectors(u32 sectorBits)
|
||
{
|
||
u16 i;
|
||
|
||
for (i = 0; i < SECTORS_COUNT; i++)
|
||
if ((sectorBits & (1 << i)) && !WipeSector(i))
|
||
sectorBits &= ~(1 << i);
|
||
|
||
if (sectorBits == 0)
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
void CB2_FlashNotDetectedScreen(void)
|
||
{
|
||
static const struct WindowTemplate textWin[] =
|
||
{
|
||
{
|
||
.bg = 0,
|
||
.tilemapLeft = 3,
|
||
.tilemapTop = 2,
|
||
.width = 24,
|
||
.height = 16,
|
||
.paletteNum = 15,
|
||
.baseBlock = 1,
|
||
}
|
||
};
|
||
|
||
if (gMain.state)
|
||
return;
|
||
|
||
SetGpuReg(REG_OFFSET_DISPCNT, 0);
|
||
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
||
SetGpuReg(REG_OFFSET_BG0CNT, 0);
|
||
SetGpuReg(REG_OFFSET_BG0HOFS, 0);
|
||
SetGpuReg(REG_OFFSET_BG0VOFS, 0);
|
||
DmaFill16(3, 0, VRAM, VRAM_SIZE);
|
||
DmaFill32(3, 0, OAM, OAM_SIZE);
|
||
DmaFill16(3, 0, PLTT, PLTT_SIZE);
|
||
ResetBgsAndClearDma3BusyFlags(0);
|
||
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
|
||
LoadBgTiles(0, gTextWindowFrame1_Gfx, 0x120, 0x214);
|
||
DeactivateAllTextPrinters();
|
||
ResetTasks();
|
||
ResetPaletteFade();
|
||
LoadPalette(gTextWindowFrame1_Pal, 0xE0, 0x20);
|
||
LoadPalette(gStandardMenuPalette, 0xF0, 0x20);
|
||
InitWindows(textWin);
|
||
DrawStdFrameWithCustomTileAndPalette(0, TRUE, 0x214, 0xE);
|
||
static const u8 saveFailedMessage[] =_(
|
||
"{COLOR RED}发生错误!{COLOR DARK_GRAY}未检测到闪存!\n"
|
||
"\n"
|
||
"如果在模拟器上运行,请将存档类型\n"
|
||
"设置为1Mb/128K,然后重新加载ROM。\n"
|
||
"\n"
|
||
"如果在硬件上运行,那么您的卡带\n"
|
||
"没有可用的闪存芯片!");
|
||
SaveFailedScreenTextPrint(saveFailedMessage, 1, 0);
|
||
TransferPlttBuffer();
|
||
*(u16*)PLTT = RGB(17, 18, 31);
|
||
ShowBg(0);
|
||
gMain.state++;
|
||
}
|