Airport/Source/MemCard.c

1179 lines
30 KiB
C

/* *************************************
* Includes
* *************************************/
#include "MemCard.h"
#include "System.h"
#include "Pad.h"
#include "Font.h"
/* *************************************
* Defines
* *************************************/
#define MEMCARD_SECTOR_SIZE 128
#define MEMCARD_SECTORS_PER_BLOCK 64
#define MEMCARD_SECTORS_PER_BLOCK_BITSHIFT 6
#define MEMCARD_BLOCK_MAX_ICONS 3
#define MEMCARD_ICON_INDEX_TIME 4
#define MEMCARD_MAXIMUM_SECTOR (1024 - 1)
#define MEMCARD_INVALID_CHECKSUM 0x4E
#define MEMCARD_CORRECT_RW 0x47
#define MEMCARD_BAD_SECTOR 0xFF
/* *************************************
* Structs and enums
* *************************************/
typedef enum t_Sectors
{
TITLE_FRAME = 0,
ICON_FRAME_1,
ICON_FRAME_2,
ICON_FRAME_3,
DATA_FRAME,
}MEMCARD_FILE_SECTORS;
enum
{
MEMCARD_BLOCK_IMAGE_X = 768 ,
MEMCARD_BLOCK_IMAGE_Y = 352 ,
MEMCARD_BLOCK_CLUT_X = 960 ,
MEMCARD_BLOCK_CLUT_Y = 352 ,
MEMCARD_BLOCK_CLUT_W = 16 ,
MEMCARD_BLOCK_CLUT_H = 1 ,
MEMCARD_BLOCK_IMAGE_W = 16 ,
MEMCARD_BLOCK_IMAGE_H = 16 ,
MEMCARD_BLOCK_IMAGE_W_BITSHIFT = 2,
MEMCARD_LOAD_DATA_TEXT_X = 96,
MEMCARD_LOAD_DATA_TEXT_Y = 192,
};
enum
{
MEMCARD_DIALOG_X = 64,
MEMCARD_DIALOG_Y = 28,
MEMCARD_DIALOG_W = 256,
MEMCARD_DIALOG_H = 184,
MEMCARD_DIALOG_R = 0,
MEMCARD_DIALOG_G = 128,
MEMCARD_DIALOG_B = 64,
MEMCARD_DIALOG_GAP_X = 24,
MEMCARD_DIALOG_GAP_SLOT = 128
};
enum
{
MEMCARD_BG_X = 64,
MEMCARD_BG_Y = 28,
MEMCARD_BG_W = 256,
MEMCARD_BG_H = 184,
MEMCARD_BG_R0 = 0,
MEMCARD_BG_R1 = MEMCARD_BG_R0,
MEMCARD_BG_R2 = MEMCARD_BG_R0,
MEMCARD_BG_R3 = MEMCARD_BG_R0,
MEMCARD_BG_G0 = 0,
MEMCARD_BG_G1 = MEMCARD_BG_G0,
MEMCARD_BG_G2 = NORMAL_LUMINANCE,
MEMCARD_BG_G3 = MEMCARD_BG_G2,
MEMCARD_BG_B0 = 0,
MEMCARD_BG_B1 = MEMCARD_BG_B0,
MEMCARD_BG_B2 = NORMAL_LUMINANCE >> 1,
MEMCARD_BG_B3 = MEMCARD_BG_B2
};
enum
{
MEMCARD_PROGRESS_BAR_X = 86,
MEMCARD_PROGRESS_BAR_Y = 148,
MEMCARD_PROGRESS_BAR_W = 226,
MEMCARD_PROGRESS_BAR_H = 16,
MEMCARD_PROGRESS_BAR_N_LINES = 4,
MEMCARD_PROGRESS_BAR_R = NORMAL_LUMINANCE,
MEMCARD_PROGRESS_BAR_G = NORMAL_LUMINANCE,
MEMCARD_PROGRESS_BAR_B = NORMAL_LUMINANCE
};
typedef enum t_MemcardProcess
{
MEMCARD_PROCESS_GET_FILENAME = 0,
MEMCARD_PROCESS_GET_INITIAL_FRAME,
MEMCARD_PROCESS_GET_ICON_FRAME,
MEMCARD_PROCESS_UPLOAD_TO_GPU
}MEMCARD_PROCESS;
typedef struct t_MemCardErrors
{
unsigned char ErrorByte;
MEMCARD_SLOTS Slot;
MEMCARD_BLOCKS Block;
MEMCARD_PROCESS Process;
}TYPE_MEMCARD_ERRORS;
/* *************************************
* Local Prototypes
* *************************************/
static bool MemCardGetInitialFrameInfo(TYPE_BLOCK_DATA * ptrBlockData);
static bool MemCardGetIconFrameInfo(TYPE_BLOCK_DATA * ptrBlockData);
static bool MemCardGetBlockStateFileName(TYPE_BLOCK_DATA * ptrBlockData);
static void ISR_MemCardDataHandling(void);
static bool MemCardReadSector(TYPE_BLOCK_DATA * ptrBlockData, int sector);
static void MemCardIconIndexHandler(void);
/* *************************************
* Local Variables
* *************************************/
static uint8_t DataBuffer[MEMCARD_SECTOR_SIZE];
static TYPE_MEMCARD_ERRORS MemCardErrors;
static GsSprite SecondDisplay;
static GsGPoly4 MemCardRect;
static GsRectangle MemCardProgressBar;
static GsLine MemCardProgressBarLines[MEMCARD_PROGRESS_BAR_N_LINES];
static uint8_t IconIndex;
static enum memcard_status MemCardStatus[MEMCARD_NUMBER_OF_SLOTS];
// Local variables used to communicate between functions and ISR.
// Names are pretty self-explanatory.
static volatile uint8_t TotalBlocks;
static volatile uint8_t CurrentReadBlock;
static volatile short ProgressBarXOffset;
/* *************************************
* Global Variables
* *************************************/
TYPE_BLOCK_DATA MemCardData[MEMCARD_BLOCKS_PER_CARD][MEMCARD_NUMBER_OF_SLOTS];
void MemCardInit(void)
{
TYPE_BLOCK_DATA * ptrBlockData;
uint8_t i;
uint8_t j;
for (j = SLOT_ONE; j <= SLOT_TWO; j++)
{
for (i = BLOCK_1; i <= BLOCK_15; i++)
{
ptrBlockData = &MemCardData[i - BLOCK_1][j];
ptrBlockData->IconTPoly.r = NORMAL_LUMINANCE;
ptrBlockData->IconTPoly.g = NORMAL_LUMINANCE;
ptrBlockData->IconTPoly.b = NORMAL_LUMINANCE;
}
}
bzero((TYPE_MEMCARD_ERRORS*)&MemCardErrors, sizeof (TYPE_MEMCARD_ERRORS) );
}
void ISR_MemCardDataHandling(void)
{
uint8_t i;
if ( (GfxIsGPUBusy()) || (SystemIsBusy()) )
{
return;
}
// Dim background
SecondDisplay.r = NORMAL_LUMINANCE >> 1;
SecondDisplay.g = NORMAL_LUMINANCE >> 1;
SecondDisplay.b = NORMAL_LUMINANCE >> 1;
MemCardRect.x[0] = MEMCARD_BG_X;
MemCardRect.x[1] = MEMCARD_BG_X + MEMCARD_BG_W;
MemCardRect.x[2] = MEMCARD_BG_X;
MemCardRect.x[3] = MEMCARD_BG_X + MEMCARD_BG_W;
MemCardRect.y[0] = MEMCARD_BG_Y;
MemCardRect.y[1] = MEMCARD_BG_Y;
MemCardRect.y[2] = MEMCARD_BG_Y + MEMCARD_BG_H;
MemCardRect.y[3] = MEMCARD_BG_Y + MEMCARD_BG_H;
MemCardRect.r[0] = MEMCARD_BG_R0;
MemCardRect.r[1] = MEMCARD_BG_R1;
MemCardRect.r[2] = MEMCARD_BG_R2;
MemCardRect.r[3] = MEMCARD_BG_R3;
MemCardRect.g[0] = MEMCARD_BG_G0;
MemCardRect.g[1] = MEMCARD_BG_G1;
MemCardRect.g[2] = MEMCARD_BG_G2;
MemCardRect.g[3] = MEMCARD_BG_G3;
MemCardRect.b[0] = MEMCARD_BG_B0;
MemCardRect.b[1] = MEMCARD_BG_B1;
MemCardRect.b[2] = MEMCARD_BG_B2;
MemCardRect.b[3] = MEMCARD_BG_B3;
MemCardRect.attribute |= ENABLE_TRANS | TRANS_MODE(0);
// "Loading" bar line 0 (up left - up right)
MemCardProgressBarLines[0].x[0] = MEMCARD_PROGRESS_BAR_X;
MemCardProgressBarLines[0].x[1] = MEMCARD_PROGRESS_BAR_X + MEMCARD_PROGRESS_BAR_W;
MemCardProgressBarLines[0].y[0] = MEMCARD_PROGRESS_BAR_Y;
MemCardProgressBarLines[0].y[1] = MEMCARD_PROGRESS_BAR_Y;
// "Loading" bar line 1 (up left - down left)
MemCardProgressBarLines[1].x[0] = MEMCARD_PROGRESS_BAR_X;
MemCardProgressBarLines[1].x[1] = MEMCARD_PROGRESS_BAR_X;
MemCardProgressBarLines[1].y[0] = MEMCARD_PROGRESS_BAR_Y;
MemCardProgressBarLines[1].y[1] = MEMCARD_PROGRESS_BAR_Y + MEMCARD_PROGRESS_BAR_H;
// "Loading" bar line 2 (down left - down right)
MemCardProgressBarLines[2].x[0] = MEMCARD_PROGRESS_BAR_X;
MemCardProgressBarLines[2].x[1] = MEMCARD_PROGRESS_BAR_X + MEMCARD_PROGRESS_BAR_W;
MemCardProgressBarLines[2].y[0] = MEMCARD_PROGRESS_BAR_Y + MEMCARD_PROGRESS_BAR_H;
MemCardProgressBarLines[2].y[1] = MEMCARD_PROGRESS_BAR_Y + MEMCARD_PROGRESS_BAR_H;
// "Loading" bar line 3 (up right - down right)
MemCardProgressBarLines[3].x[0] = MEMCARD_PROGRESS_BAR_X + MEMCARD_PROGRESS_BAR_W;
MemCardProgressBarLines[3].x[1] = MEMCARD_PROGRESS_BAR_X + MEMCARD_PROGRESS_BAR_W;
MemCardProgressBarLines[3].y[0] = MEMCARD_PROGRESS_BAR_Y;
MemCardProgressBarLines[3].y[1] = MEMCARD_PROGRESS_BAR_Y + MEMCARD_PROGRESS_BAR_H;
for (i = 0; i < MEMCARD_PROGRESS_BAR_N_LINES; i++)
{
MemCardProgressBarLines[i].r = NORMAL_LUMINANCE;
MemCardProgressBarLines[i].g = NORMAL_LUMINANCE;
MemCardProgressBarLines[i].b = NORMAL_LUMINANCE;
}
// Set progress bar attributes
MemCardProgressBar.x = MEMCARD_PROGRESS_BAR_X;
MemCardProgressBar.y = MEMCARD_PROGRESS_BAR_Y;
MemCardProgressBar.w = ProgressBarXOffset;
MemCardProgressBar.h = MEMCARD_PROGRESS_BAR_H;
MemCardProgressBar.r = MEMCARD_PROGRESS_BAR_R;
MemCardProgressBar.g = MEMCARD_PROGRESS_BAR_G;
MemCardProgressBar.b = MEMCARD_PROGRESS_BAR_B;
GfxSortSprite(&SecondDisplay);
GsSortGPoly4(&MemCardRect);
GsSortRectangle(&MemCardProgressBar);
for (i = 0; i < MEMCARD_PROGRESS_BAR_N_LINES; i++)
{
GsSortLine(&MemCardProgressBarLines[i]);
}
FontSetFlags(&SmallFont, FONT_BLEND_EFFECT);
FontPrintText(&SmallFont, MEMCARD_LOAD_DATA_TEXT_X, MEMCARD_LOAD_DATA_TEXT_Y, "Loading memory card data...");
FontSetFlags(&SmallFont, FONT_NOFLAGS);
GfxDrawScene_Fast();
}
void MemCardResetBlockData(TYPE_BLOCK_DATA * ptrBlockData)
{
bzero((TYPE_BLOCK_DATA*)ptrBlockData, sizeof (TYPE_BLOCK_DATA));
ptrBlockData->BlockCount = FIRST_OR_ONLY_BLOCK;
IconIndex = 0;
}
bool MemCardGetBlockInfo( TYPE_BLOCK_DATA * ptrBlockData,
MEMCARD_SLOTS slot,
MEMCARD_BLOCKS blockNumber )
{
MemCardResetBlockData(ptrBlockData);
ptrBlockData->Slot = slot;
ptrBlockData->Block = blockNumber;
Serial_printf("MemCardGetBlockStateFileName...\n");
if (MemCardGetBlockStateFileName(ptrBlockData) == false)
{
return false;
}
if (ptrBlockData->BlockCount == EMPTY_BLOCK)
{
// Stop looking for any other data.
return true;
}
Serial_printf("MemCardGetInitialFrameInfo...\n");
if (MemCardGetInitialFrameInfo(ptrBlockData) == false)
{
return false;
}
Serial_printf("MemCardGetIconFrameInfo...\n");
if (MemCardGetIconFrameInfo(ptrBlockData) == false)
{
return false;
}
// We will not get any block data information, we are only interested
// in basic info.
Serial_printf("MemCardUploadToGPU...\n");
if (MemCardUploadToGPU(ptrBlockData) == false)
{
return false;
}
return true;
}
bool MemCardGetBlockStateFileName(TYPE_BLOCK_DATA * ptrBlockData)
{
int sector = ptrBlockData->Block;
MemCardErrors.Block = ptrBlockData->Block;
MemCardErrors.Slot = ptrBlockData->Slot;
MemCardErrors.Process = MEMCARD_PROCESS_GET_FILENAME;
memset(DataBuffer, 0, MEMCARD_SECTOR_SIZE);
if (MemCardReadSector(ptrBlockData, sector) == false)
{
return false;
}
// 00h-03h Block Allocation State
Serial_printf("Block %d, slot %d, allocation state: 0x%02X.\n",
ptrBlockData->Block,
ptrBlockData->Slot,
DataBuffer[0] );
/* 00000051h - In use ;first-or-only block of a file
* 00000052h - In use ;middle block of a file (if 3 or more blocks)
* 00000053h - In use ;last block of a file (if 2 or more blocks)
* 000000A0h - Free ;freshly formatted
* 000000A1h - Free ;deleted (first-or-only block of file)
* 000000A2h - Free ;deleted (middle block of file)
* 000000A3h - Free ;deleted (last block of file) */
// Always take into account memory card data is little-endian,
// so if using a hex editor, you will read 51000000h.
switch(DataBuffer[0])
{
case 0x51:
ptrBlockData->BlockCount = FIRST_OR_ONLY_BLOCK;
break;
case 0x52:
ptrBlockData->BlockCount = INTERMEDIATE_BLOCK;
break;
case 0x53:
ptrBlockData->BlockCount = LAST_BLOCK;
break;
case 0xA0:
case 0xA1:
case 0xA2:
case 0xA3:
ptrBlockData->BlockCount = EMPTY_BLOCK;
return true;
default:
printf("Invalid block allocation state!\n");
return false;
}
// 0Ah-1Eh Filename in ASCII, terminated by 00h (max 20 chars, plus ending 00h)
// File name is only defined on first block of group (allocation state == 0x51)
if (ptrBlockData->BlockCount == FIRST_OR_ONLY_BLOCK)
{
memset(ptrBlockData->FileName, 0 , MEMCARD_FILENAME_SIZE);
memmove(ptrBlockData->FileName, &DataBuffer[0x0A], MEMCARD_FILENAME_SIZE);
Serial_printf("File name: %s\n", ptrBlockData->FileName);
}
return true;
}
bool MemCardGetInitialFrameInfo(TYPE_BLOCK_DATA * ptrBlockData)
{
unsigned int i;
int sector = ptrBlockData->Block << MEMCARD_SECTORS_PER_BLOCK_BITSHIFT;
MemCardErrors.Block = ptrBlockData->Block;
MemCardErrors.Slot = ptrBlockData->Slot;
MemCardErrors.Process = MEMCARD_PROCESS_GET_FILENAME;
if (ptrBlockData->BlockCount != FIRST_OR_ONLY_BLOCK)
{
// Icon data is only stored on first block (if game takes more
// than one block. Skip this step otherwise.
// When dealing with intermediate or last blocks of a file,
// we use a static pointer which points to first block of a file,
// and then image data is copied into other blocks.
return true;
}
// Pretty silly operation (TITLE_FRAME = 0), but used for
// conceptual purposes and better understanding.
sector += TITLE_FRAME;
memset(DataBuffer, 0, MEMCARD_SECTOR_SIZE);
if (MemCardReadSector(ptrBlockData, sector) == false)
{
return false;
}
Serial_printf("Magic number: '%c' '%c'\n",DataBuffer[0], DataBuffer[1]);
if (DataBuffer[0] != 'S' || DataBuffer[1] != 'C')
{
// Invalid magic number.
Serial_printf("Invalid magic number extracted from slot %d, block %d.\n",
ptrBlockData->Slot,
ptrBlockData->Block);
return false;
}
/* 02h Icon Display Flag
11h...Icon has 1 frame (static) (same image shown forever)
12h...Icon has 2 frames (animated) (changes every 16 PAL frames)
13h...Icon has 3 frames (animated) (changes every 11 PAL frames)
* */
switch(DataBuffer[2])
{
case 0x11:
ptrBlockData->IconNumber = 1;
break;
case 0x12:
ptrBlockData->IconNumber = 2;
break;
case 0x13:
ptrBlockData->IconNumber = 3;
break;
default:
// Invalid icon display flag! We can't know how many icons
// are used.
return false;
}
Serial_printf("Number of icons: %d\n", ptrBlockData->IconNumber);
// 60h-7Fh Icon 16 Color Palette Data (each entry is 16bit CLUT)
for (i = 0; i < ptrBlockData->IconNumber; i++)
{
memmove(ptrBlockData->CLUT[i],&DataBuffer[0x60], MEMCARD_CLUT_SIZE);
}
return true;
}
bool MemCardGetIconFrameInfo(TYPE_BLOCK_DATA * ptrBlockData)
{
int initial_sector = ptrBlockData->Block << MEMCARD_SECTORS_PER_BLOCK_BITSHIFT;
int sector;
unsigned int i;
unsigned int j;
uint8_t buffer_contents;
static TYPE_BLOCK_DATA * ptrReferenceBlock = NULL;
switch(ptrBlockData->BlockCount)
{
case EMPTY_BLOCK:
// Empty blocks are not of interest. Skip.
return true;
case INTERMEDIATE_BLOCK:
case LAST_BLOCK:
if (ptrReferenceBlock == NULL)
{
Serial_printf("No reference memory card block found yet!\n");
return false;
}
ptrBlockData->IconNumber = ptrReferenceBlock->IconNumber;
for (i = 0; i < MEMCARD_NUMBER_OF_ICONS; i++)
{
memmove(ptrBlockData->CLUT[i], ptrReferenceBlock->CLUT[i], MEMCARD_CLUT_SIZE);
memmove(ptrBlockData->Icons[i], ptrReferenceBlock->Icons[i], MEMCARD_ICON_SIZE);
}
if (ptrBlockData->BlockCount == LAST_BLOCK)
{
// Dereference pointer
ptrReferenceBlock = NULL;
}
return true;
case FIRST_OR_ONLY_BLOCK:
// Icon Frame(s) (Block 1..15, Frame 1..3) (in first block of file only)
for (i = ICON_FRAME_1; i <= ptrBlockData->IconNumber; i++)
{
Serial_printf("\tIcon %d out of %d\n",i, ptrBlockData->IconNumber);
buffer_contents = 0;
sector = initial_sector + i;
memset(DataBuffer, 0, MEMCARD_SECTOR_SIZE * sizeof (uint8_t) );
if (MemCardReadSector(ptrBlockData, sector) == false)
{
Serial_printf("Could not read memory sector!\n");
return false;
}
memmove(ptrBlockData->Icons[i - 1 /* ICON_FRAME_# - 1 */], DataBuffer, MEMCARD_SECTOR_SIZE);
for (j = 0; j < MEMCARD_SECTOR_SIZE; j++)
{
buffer_contents |= ptrBlockData->Icons[i - 1][j];
}
if (buffer_contents == 0)
{
// Icon buffer is empty!
Serial_printf("Invalid icon buffer for slot %d, block %d.\n",
ptrBlockData->Slot,
ptrBlockData->Block);
return false;
}
}
// Use current block as reference if file contains more than one block.
ptrReferenceBlock = ptrBlockData;
return true;
}
Serial_printf("Unknown error from MemCardGetIconFrameInfo()!\n");
return false;
}
bool MemCardUploadToGPU(TYPE_BLOCK_DATA * ptrBlockData)
{
uint8_t i;
short x_clut_offset;
short y_clut_offset;
short x_block_offset;
GsImage gs;
if ( (ptrBlockData->IconNumber < 1)
||
(ptrBlockData->IconNumber > MEMCARD_NUMBER_OF_ICONS) )
{
Serial_printf("Invalid number of icons.\n");
return false;
}
for (i = 0; i < ptrBlockData->IconNumber; i++)
{
gs.pmode = COLORMODE_4BPP;
gs.has_clut = 1;
x_clut_offset = i << 4;
y_clut_offset = ptrBlockData->Block - 1;
gs.clut_x = MEMCARD_BLOCK_CLUT_X + x_clut_offset;
gs.clut_y = MEMCARD_BLOCK_CLUT_Y + y_clut_offset + (ptrBlockData->Slot << 4);
gs.clut_w = MEMCARD_BLOCK_CLUT_W;
gs.clut_h = MEMCARD_BLOCK_CLUT_H;
Serial_printf("Gs Clut = {%d,%d,%d,%d}\n",
gs.clut_x,
gs.clut_y,
gs.clut_w,
gs.clut_h );
x_block_offset = MEMCARD_BLOCK_IMAGE_W * (ptrBlockData->Block - 1);
x_block_offset *= MEMCARD_BLOCK_MAX_ICONS;
x_block_offset >>= MEMCARD_BLOCK_IMAGE_W_BITSHIFT;
x_block_offset += i<<MEMCARD_BLOCK_IMAGE_W_BITSHIFT;
gs.x = MEMCARD_BLOCK_IMAGE_X + x_block_offset;
gs.y = MEMCARD_BLOCK_IMAGE_Y + (MEMCARD_BLOCK_IMAGE_H * ptrBlockData->Slot);
// Dimensions are 16x16 px, but since 4bpp is used, it actually
// takes 4x16 px on the framebuffer.
gs.w = MEMCARD_BLOCK_IMAGE_W >> MEMCARD_BLOCK_IMAGE_W_BITSHIFT;
gs.h = MEMCARD_BLOCK_IMAGE_H;
gs.clut_data = (uint8_t*)ptrBlockData->CLUT[i];
gs.data = (uint8_t*)ptrBlockData->Icons[i];
GsUploadImage(&gs);
if (i == 0)
{
ptrBlockData->IconTPoly.attribute = COLORMODE(COLORMODE_4BPP);
ptrBlockData->IconTPoly.tpage = (gs.x / 64) + ((gs.y/256)*16);
Serial_printf("\tTPAGE = %d\n", ptrBlockData->IconTPoly.tpage);
x_block_offset = MEMCARD_BLOCK_IMAGE_W * (ptrBlockData->Block - 1);
x_block_offset *= MEMCARD_BLOCK_MAX_ICONS;
ptrBlockData->IconTPoly.u[0] = MEMCARD_BLOCK_IMAGE_X + x_block_offset;
ptrBlockData->IconTPoly.u[1] = ptrBlockData->IconTPoly.u[0] + (gs.w << 2);
ptrBlockData->IconTPoly.u[2] = ptrBlockData->IconTPoly.u[0];
ptrBlockData->IconTPoly.u[3] = ptrBlockData->IconTPoly.u[1];
Serial_printf("\tu = {%d, %d, %d, %d}\n",
ptrBlockData->IconTPoly.u[0],
ptrBlockData->IconTPoly.u[1],
ptrBlockData->IconTPoly.u[2],
ptrBlockData->IconTPoly.u[3]);
ptrBlockData->IconTPoly.v[0] = gs.y % 256;
ptrBlockData->IconTPoly.v[1] = ptrBlockData->IconTPoly.v[0];
ptrBlockData->IconTPoly.v[2] = (gs.y % 256) + (gs.h);
ptrBlockData->IconTPoly.v[3] = ptrBlockData->IconTPoly.v[2];
Serial_printf("\tu = {%d, %d, %d, %d}\n",
ptrBlockData->IconTPoly.v[0],
ptrBlockData->IconTPoly.v[1],
ptrBlockData->IconTPoly.v[2],
ptrBlockData->IconTPoly.v[3]);
ptrBlockData->IconTPoly.r = NORMAL_LUMINANCE;
ptrBlockData->IconTPoly.g = NORMAL_LUMINANCE;
ptrBlockData->IconTPoly.b = NORMAL_LUMINANCE;
ptrBlockData->IconTPoly.cx = gs.clut_x;
ptrBlockData->IconTPoly.cy = gs.clut_y;
Serial_printf("\tclut = {%d, %d}\n",
ptrBlockData->IconTPoly.cx,
ptrBlockData->IconTPoly.cy);
}
}
return true;
}
bool MemCardReadSector(TYPE_BLOCK_DATA * ptrBlockData, int sector)
{
uint8_t result;
MemCardErrors.Block = ptrBlockData->Block;
MemCardErrors.Slot = ptrBlockData->Slot;
if ( (ptrBlockData->Slot != 0)
&&
(ptrBlockData->Slot != 1) )
{
MemCardErrors.ErrorByte = 'S';
Serial_printf("Incorrect slot %d! Block %d?\n",
ptrBlockData->Slot,
ptrBlockData->Block);
return false;
}
if ((sector < 0) || (sector > MEMCARD_MAXIMUM_SECTOR))
{
MemCardErrors.ErrorByte = 'T';
Serial_printf("Invalid memory card sector %d. Only values between"
" 0 and %d are allowed!\n", sector, MEMCARD_MAXIMUM_SECTOR);
return false;
}
while (GfxIsGPUBusy());
result = McReadSector(ptrBlockData->Slot, sector, DataBuffer);
// Fill char "MemCardErrors" for further error description.
MemCardErrors.ErrorByte = result;
switch(result)
{
case '1':
case '2':
case 'L':
case 'M':
return false;
case MEMCARD_INVALID_CHECKSUM:
Serial_printf("Invalid checksum for memory card sector %d"
" from block %d, slot %d",
sector,
ptrBlockData->Block,
ptrBlockData->Slot );
return false;
case MEMCARD_BAD_SECTOR:
Serial_printf("Invalid memory card sector %d. Only values between"
" 0 and 511 are allowed!\n", sector);
return false;
case MEMCARD_CORRECT_RW:
return true;
default:
return false;
}
return true;
}
bool MemCardGetAllData(void)
{
uint8_t i;
uint8_t j;
PadClearData();
CurrentReadBlock = 0;
TotalBlocks = MEMCARD_BLOCKS_PER_CARD * MEMCARD_NUMBER_OF_SLOTS;
SmallFont.spr.r = 0;
SmallFont.spr.g = 0;
SmallFont.spr.b = 0;
GfxSaveDisplayData(&SecondDisplay);
GfxSetGlobalLuminance(NORMAL_LUMINANCE);
// ISR_MemCardDataHandling draws a rectangle on top to show
// memory card loading progress.
SetVBlankHandler(&ISR_MemCardDataHandling);
for (j = SLOT_ONE; j <= SLOT_TWO; j++)
{
MemCardStatus[j] = McGetStatus(j);
if (MemCardStatus[j] == MEMCARD_STATUS_UNKNOWN)
{
// Memcard not connected and/or formatted.
continue;
}
for (i = BLOCK_1; i <= BLOCK_15; i++)
{
ProgressBarXOffset = (short)(CurrentReadBlock *
(MEMCARD_PROGRESS_BAR_W /
(MEMCARD_BLOCKS_PER_CARD * MEMCARD_NUMBER_OF_SLOTS) ) );
if (MemCardGetBlockInfo(&MemCardData[i - BLOCK_1][j], j, i) == false)
{
// Return to normal behaviour if anything fails
SetVBlankHandler(&ISR_SystemDefaultVBlank);
return false;
}
CurrentReadBlock++;
}
}
SetVBlankHandler(&ISR_SystemDefaultVBlank);
CurrentReadBlock = 0;
return true;
}
void MemCardHandler(void)
{
MemCardIconIndexHandler();
}
void MemCardIconIndexHandler(void)
{
static uint8_t iconTimer = 0;
if (System100msTick())
{
if (++iconTimer >= MEMCARD_ICON_INDEX_TIME)
{
iconTimer = 0;
if (++IconIndex >= MEMCARD_NUMBER_OF_ICONS)
{
IconIndex = 0;
}
}
}
}
void MemCardDrawIcon(TYPE_BLOCK_DATA * ptrBlockData, short x, short y)
{
uint8_t i;
// Auxiliar variable to keep original data
short orig_u[4];
short orig_clut_x;
static bool first_access = true;
if (ptrBlockData->BlockCount == EMPTY_BLOCK)
{
return;
}
ptrBlockData->IconTPoly.x[0] = x;
ptrBlockData->IconTPoly.x[1] = x + MEMCARD_BLOCK_IMAGE_W;
ptrBlockData->IconTPoly.x[2] = x;
ptrBlockData->IconTPoly.x[3] = ptrBlockData->IconTPoly.x[1];
ptrBlockData->IconTPoly.y[0] = y;
ptrBlockData->IconTPoly.y[1] = ptrBlockData->IconTPoly.y[0];
ptrBlockData->IconTPoly.y[2] = y + MEMCARD_BLOCK_IMAGE_H;
ptrBlockData->IconTPoly.y[3] = ptrBlockData->IconTPoly.y[2];
for (i = 0; i < 4; i++)
{
orig_u[i] = ptrBlockData->IconTPoly.u[i];
}
orig_clut_x = ptrBlockData->IconTPoly.cx;
if (ptrBlockData->IconNumber > IconIndex)
{
ptrBlockData->IconTPoly.u[0] += MEMCARD_BLOCK_IMAGE_W * IconIndex;
ptrBlockData->IconTPoly.u[1] = ptrBlockData->IconTPoly.u[0] + MEMCARD_BLOCK_IMAGE_W;
ptrBlockData->IconTPoly.u[2] = ptrBlockData->IconTPoly.u[0];
ptrBlockData->IconTPoly.u[3] = ptrBlockData->IconTPoly.u[1];
ptrBlockData->IconTPoly.cx += IconIndex * MEMCARD_BLOCK_CLUT_W;
}
if (first_access)
{
if (IconIndex == 0)
{
first_access = false;
Serial_printf("Icon index: %d\n",IconIndex);
Serial_printf("\tU = {%d,%d,%d,%d}\n",
ptrBlockData->IconTPoly.u[0],
ptrBlockData->IconTPoly.u[1],
ptrBlockData->IconTPoly.u[2],
ptrBlockData->IconTPoly.u[3]);
Serial_printf("\tV = {%d,%d,%d,%d}\n",
ptrBlockData->IconTPoly.v[0],
ptrBlockData->IconTPoly.v[1],
ptrBlockData->IconTPoly.v[2],
ptrBlockData->IconTPoly.v[3]);
Serial_printf("\tBlock number: %d\n",ptrBlockData->Block);
Serial_printf("\tBlock count: %d\n",ptrBlockData->BlockCount);
Serial_printf("\tX = {%d,%d,%d,%d}\n",
ptrBlockData->IconTPoly.x[0],
ptrBlockData->IconTPoly.x[1],
ptrBlockData->IconTPoly.x[2],
ptrBlockData->IconTPoly.x[3]);
Serial_printf("\tY = {%d,%d,%d,%d}\n",
ptrBlockData->IconTPoly.y[0],
ptrBlockData->IconTPoly.y[1],
ptrBlockData->IconTPoly.y[2],
ptrBlockData->IconTPoly.y[3]);
Serial_printf("\tTPAGE = %d\n", ptrBlockData->IconTPoly.tpage);
Serial_printf("\tCLUT = {%d, %d}\n",
ptrBlockData->IconTPoly.cx,
ptrBlockData->IconTPoly.cy);
}
}
GsSortTPoly4(&ptrBlockData->IconTPoly);
for (i = 0; i < 4; i++)
{
ptrBlockData->IconTPoly.u[i] = orig_u[i]; // Restore data
}
ptrBlockData->IconTPoly.cx = orig_clut_x;
}
TYPE_BLOCK_DATA * MemCardShowMap(void)
{
uint8_t i;
uint8_t j;
uint8_t selectedBlock = BLOCK_1;
uint8_t selectedSlot = SLOT_ONE;
TYPE_BLOCK_DATA * ptrBlockData;
GsRectangle emptyBlockRect;
short x;
short y;
unsigned char orig_r;
unsigned char orig_g;
unsigned char orig_b;
GsRectangle MemCardMapDialog;
if (MemCardGetAllData() == false)
{
return false;
}
bzero((GsRectangle*)&MemCardMapDialog, sizeof (GsRectangle));
bzero((GsRectangle*)&emptyBlockRect, sizeof (GsRectangle));
emptyBlockRect.attribute |= ENABLE_TRANS | TRANS_MODE(1);
MemCardMapDialog.x = MEMCARD_DIALOG_X;
MemCardMapDialog.y = MEMCARD_DIALOG_Y;
MemCardMapDialog.w = MEMCARD_DIALOG_W;
MemCardMapDialog.h = MEMCARD_DIALOG_H;
MemCardMapDialog.r = MEMCARD_DIALOG_R;
MemCardMapDialog.g = MEMCARD_DIALOG_G;
MemCardMapDialog.b = MEMCARD_DIALOG_B;
MemCardMapDialog.attribute |= ENABLE_TRANS | TRANS_MODE(0);
GfxSetGlobalLuminance(NORMAL_LUMINANCE);
while (1)
{
if (PadOneKeyReleased(PAD_TRIANGLE))
{
break;
}
else if (PadOneKeyReleased(PAD_CROSS))
{
return &MemCardData[selectedBlock - BLOCK_1][selectedSlot];
}
else if (PadOneKeyReleased(PAD_LEFT))
{
if (selectedSlot == SLOT_TWO)
{
selectedSlot = SLOT_ONE;
}
}
else if (PadOneKeyReleased(PAD_RIGHT))
{
if (selectedSlot == SLOT_ONE)
{
selectedSlot = SLOT_TWO;
}
}
else if (PadOneKeyReleased(PAD_UP))
{
if (selectedBlock > BLOCK_1)
{
selectedBlock--;
}
}
else if (PadOneKeyReleased(PAD_DOWN))
{
if (selectedBlock < BLOCK_15)
{
selectedBlock++;
}
}
// Dim background
SecondDisplay.r = NORMAL_LUMINANCE >> 1;
SecondDisplay.g = NORMAL_LUMINANCE >> 1;
SecondDisplay.b = NORMAL_LUMINANCE >> 1;
GfxSortSprite(&SecondDisplay);
GsSortRectangle(&MemCardMapDialog);
for (j = SLOT_ONE; j <= SLOT_TWO; j++)
{
if (MemCardStatus[j] == MEMCARD_STATUS_UNKNOWN)
{
FontSetFlags(&SmallFont, FONT_NOFLAGS);
x = MEMCARD_DIALOG_X;
x += MEMCARD_DIALOG_GAP_SLOT * j;
y = MEMCARD_DIALOG_Y;
y += (short)(MEMCARD_DIALOG_GAP_X << 1);
y += MEMCARD_DIALOG_GAP_X;
x += MEMCARD_DIALOG_GAP_X;
FontPrintText( &SmallFont,
x,
y,
"Disconnected" );
continue;
}
for (i = BLOCK_1; i <= BLOCK_15; i++)
{
ptrBlockData = &MemCardData[i - BLOCK_1][j];
x = MEMCARD_DIALOG_X;
x += MEMCARD_DIALOG_GAP_SLOT * j;
x += ( (i - BLOCK_1) % 3) * MEMCARD_DIALOG_GAP_X;
x += MEMCARD_DIALOG_GAP_X;
y = MEMCARD_DIALOG_Y;
y += (short)(MEMCARD_DIALOG_GAP_X * ((i - BLOCK_1) / 3));
y += MEMCARD_DIALOG_GAP_X;
if (ptrBlockData->BlockCount == EMPTY_BLOCK)
{
emptyBlockRect.x = x;
emptyBlockRect.y = y;
emptyBlockRect.w = MEMCARD_BLOCK_IMAGE_W;
emptyBlockRect.h = MEMCARD_BLOCK_IMAGE_H;
if ( (i == selectedBlock) && (j == selectedSlot) )
{
emptyBlockRect.r = FULL_LUMINANCE;
emptyBlockRect.g = FULL_LUMINANCE;
emptyBlockRect.b = FULL_LUMINANCE;
FontSetFlags(&SmallFont, FONT_NOFLAGS);
FontPrintText( &SmallFont,
MEMCARD_LOAD_DATA_TEXT_X,
MEMCARD_LOAD_DATA_TEXT_Y,
"Empty block" );
}
else
{
emptyBlockRect.r = NORMAL_LUMINANCE >> 1;
emptyBlockRect.g = NORMAL_LUMINANCE >> 1;
emptyBlockRect.b = NORMAL_LUMINANCE >> 1;
}
GsSortRectangle(&emptyBlockRect);
continue;
}
orig_r = ptrBlockData->IconTPoly.r;
orig_g = ptrBlockData->IconTPoly.g;
orig_b = ptrBlockData->IconTPoly.b;
if ( (i == selectedBlock) && (j == selectedSlot) )
{
ptrBlockData->IconTPoly.r = FULL_LUMINANCE;
ptrBlockData->IconTPoly.g = FULL_LUMINANCE;
ptrBlockData->IconTPoly.b = FULL_LUMINANCE;
if (ptrBlockData->BlockCount == FIRST_OR_ONLY_BLOCK)
{
FontPrintText( &SmallFont,
MEMCARD_LOAD_DATA_TEXT_X,
MEMCARD_LOAD_DATA_TEXT_Y,
(char*)ptrBlockData->FileName );
}
else if (ptrBlockData->BlockCount == INTERMEDIATE_BLOCK)
{
FontPrintText( &SmallFont,
MEMCARD_LOAD_DATA_TEXT_X,
MEMCARD_LOAD_DATA_TEXT_Y,
"Intermediate block" );
}
else if (ptrBlockData->BlockCount == LAST_BLOCK)
{
FontPrintText( &SmallFont,
MEMCARD_LOAD_DATA_TEXT_X,
MEMCARD_LOAD_DATA_TEXT_Y,
"Last block" );
}
}
else
{
ptrBlockData->IconTPoly.r = NORMAL_LUMINANCE >> 1;
ptrBlockData->IconTPoly.g = NORMAL_LUMINANCE >> 1;
ptrBlockData->IconTPoly.b = NORMAL_LUMINANCE >> 1;
}
MemCardDrawIcon(ptrBlockData, x, y);
ptrBlockData->IconTPoly.r = orig_r;
ptrBlockData->IconTPoly.g = orig_g;
ptrBlockData->IconTPoly.b = orig_b;
}
}
GfxDrawScene_Slow();
}
return NULL;
}
bool MemCardSaveData(TYPE_BLOCK_DATA * ptrBlockData)
{
uint32_t i;
uint32_t sz;
int sector = (ptrBlockData->Block << MEMCARD_SECTORS_PER_BLOCK_BITSHIFT) + DATA_FRAME;
// Always check whether current block is empty or not
if (ptrBlockData->BlockCount != EMPTY_BLOCK)
{
if (strncmp((char*)ptrBlockData->FileName, MEMCARD_GAME_FILENAME, MEMCARD_FILENAME_SIZE) != 0)
{
// Only our own blocks can be overwritten. NEVER overwrite other game blocks!
Serial_printf("I cannot erase blocks from other games!\n");
return false;
}
}
else if (ptrBlockData->BlockCount != FIRST_OR_ONLY_BLOCK)
{
Serial_printf("Please select first block of block array.\n");
return false;
}
else if (ptrBlockData->Data == NULL)
{
Serial_printf("No data on current block!\n");
return false;
}
else if (ptrBlockData->Block == DIRECTORY_BLOCK)
{
Serial_printf("Invalid block selected!\n");
return false;
}
// After all these checks, now we can save data!
sz = MEMCARD_FIRST_OR_LAST_DATA_SIZE;
for (i = 0; i < sz; i++)
{
McWriteSector(ptrBlockData->Slot, sector + i, &ptrBlockData->Data[i << 7 /* 128 */]);
}
return true;
}