SBSPSS/source/memcard/memcard.cpp
2001-08-03 19:10:36 +00:00

1404 lines
38 KiB
C++

/*=========================================================================
memcard.cpp
Author: Paul Grenfell @ Climax
Created: Febuary 1999
Project: Theme Park 2
Purpose: Memory card access
Copyright (c) 1999 Climax Development Ltd
===========================================================================*/
/*----------------------------------------------------------------------
Includes
-------- */
#include "memcard/memcard.h"
#ifndef __SYSTEM_DBG_H__
#include "system\dbg.h"
#endif
/* Std Lib
------- */
#include <sys\types.h>
#include <libmcrd.h>
/*----------------------------------------------------------------------
Tyepdefs && Defines
------------------- */
#define SAVENAMEHEADER "SpongeBob_SuperSponge"
// for prlsr - use slot 1 only
#define USE_SLOT_ONE_ONLY
/*----------------------------------------------------------------------
Structure defintions
-------------------- */
/*----------------------------------------------------------------------
Positional Vars
--------------- */
/*----------------------------------------------------------------------
Function Prototypes
------------------- */
/*----------------------------------------------------------------------
Vars
---- */
int MemCard::s_active=false;
int MemCard::s_currentChannel;
int MemCard::m_nextChannel;
MemCard::CMDTYPE MemCard::s_currentCommand;
MemCard::CMDTYPE MemCard::m_nextCommand;
int MemCard::s_activeSlot=-1;
MemCard::CARDDEF MemCard::s_cardData[MemCard::CARDSLOTSTOCHECK];
int MemCard::s_file;
int MemCard::s_blockCount;
char MemCard::s_tempFileInfoBuffer[128];
long int MemCard::s_syncStatus,MemCard::s_syncCmds,MemCard::s_syncResults;
void *MemCard::s_bufPtr;
char MemCard::s_userFileName[8+1];
char MemCard::s_fname[20+1];
char MemCard::s_fnameBase[40];
FileCallbackDef MemCard::s_callbackFunction;
/*****************************************************************************/
/*** SJIS Conversion Stuff ***************************************************/
/*****************************************************************************/
struct SjisS
{
char Ascii;
unsigned char Num;
unsigned short Sjis;
};
static struct SjisS SjisTable[]=
{
{'0',10, 0x824f },{'A',26, 0x8260 },{'a',26, 0x8281 },
{' ', 1,0x8140},{'!', 1,0x8149},{'"', 1,0x8168},{'#', 1,0x8194},{'$', 1,0x8190},
{'%', 1,0x8193},{'&', 1,0x8195},{'\'',1,0x8166},{'(', 1,0x8169},{')', 1,0x816a},
{'*', 1,0x8196},{'+', 1,0x817b},{',', 1,0x8143},{'-', 1,0x817c},{'.', 1,0x8144},
{'/', 1,0x815e},{':', 1,0x8146},{';', 1,0x8147},{'<', 1,0x8171},{'=', 1,0x8181},
{'>', 1,0x8172},{'?', 1,0x8148},{'@', 1,0x8197},{'[', 1,0x816d},{'\\',1,0x818f},
{']', 1,0x816e},{'^', 1,0x814f},{'_', 1,0x8151},{'`', 1,0x8165},{'{', 1,0x816f},
{'|', 1,0x8162},{'}', 1,0x8170},{'~', 1,0x8150},{0,0,0}
};
/*****************************************************************************/
char MCToAscii(unsigned short Sjis)
{
struct SjisS *Ptr = SjisTable;
while( Ptr->Ascii )
{
if( ( Sjis >= Ptr->Sjis ) && ( Sjis < ( Ptr->Sjis + Ptr->Num ) ) )
{
/* Found SJIS code, return ASCII equivalent */
return( Ptr->Ascii + ( Sjis - Ptr->Sjis ) );
}
Ptr++;
}
return '?'; // Unknown SJIS code
}
/*****************************************************************************/
unsigned short MCToSjis(char Ascii)
{
struct SjisS *Ptr=SjisTable;
while (Ptr->Ascii)
{
if ((Ascii>=Ptr->Ascii) && (Ascii<(Ptr->Ascii+Ptr->Num)))
{
/* Found ASCII char, return SJIS equivalent */
return (Ptr->Sjis+(Ascii-Ptr->Ascii));
}
Ptr++;
}
return( 0x8148 ); // Unknow char, return '?'
}
/*****************************************************************************/
// Returns: Pointer to a non-persistant containing ascii test
static char *JIStoASCII( unsigned short *Sjis )
{
unsigned short SwappedSjis[ 32+1 ];
unsigned short *Src, *Dst;
char *CDst;
static char Ascii[ 64+1 ];
Ascii[ 0 ] = '\0';
if( Sjis[ 0 ] & 0x0080 )
{
Src = Sjis;
Dst = SwappedSjis;
while( *Src )
{
*Dst = ( ( *Src & 0xff ) << 8 ) + ( *Src >> 8 );
Src++;
Dst++;
}
*Dst++ = 0;
// Convert to ASCII
Src = SwappedSjis;
CDst = Ascii;
while( *Src )
{
*CDst++ = MCToAscii( *Src++ );
}
*CDst++ = 0;
}
else
{
// Old style ASCII string - Just copy it across
// strcpy( (char *)sjis, ascii );
return( "ASCII" );
}
return( Ascii );
}
/*****************************************************************************/
static unsigned short *ASCIItoJIS( char *Str )
{
static unsigned short SjisText[ 32+1 ];
unsigned short *Sjis = SjisText;
while( *Str )
{
if( ( Str[ 0 ] ) & 0x80 )
{
*Sjis++ = ( Str[ 0 ] << 8 ) | Str[ 1 ];
Str += 2;
}
else
{
*Sjis++ = MCToSjis( *Str++ );
}
}
// Byte swap
unsigned short temp;
for( int i = 0; i < 32; i++ )
{
temp = SjisText[ i ];
SjisText[ i ] = ( ( temp & 0x00ff ) << 8 ) + ( ( temp & 0xff00 ) >> 8 );
}
*Sjis++=0; *Sjis++=0;
return( SjisText );
}
/*****************************************************************************/
/*** SJIS Conversion Stuff ***************************************************/
/*****************************************************************************/
/*----------------------------------------------------------------------
Function: Start
Purpose: Starts up memory card services. MemCardInit() should have already been called.
Params: None
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::Start( void )
{
long int Dummy;
// Don't allow the sytem to be activated twice!
if( s_active == true )
{
return( false );
}
// Startup system memcard services
MemCardStart();
// Make sure that the system memcard interface is idle
MemCardSync( 0, &Dummy, &Dummy );
// Initialisation
s_currentCommand=CmdNone;
m_nextCommand=CmdNone;
m_nextChannel=-1;
InvalidateCard( 0 );
InvalidateCard( 1 );
s_currentChannel = 1;
s_activeSlot = -1;
CreateFName("\0");
// It's alive!
s_active = true;
// MEMCARD_DBGMSG( "[PMC] PMC started" );
return( true );
}
/*----------------------------------------------------------------------
Function: Stop
Purpose: Closes down memcard services. Call MemCardEnd() afterwards if you _really_ want to
shut the sytem memcard services down.
Params: None
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::Stop( void )
{
long int Dummy;
// Check that the system is active
if( s_active != true )
{
return( false );
}
s_active = false;
// Make sure that the system memcard interface is idle
MemCardSync( 0, &Dummy, &Dummy );
// Close down PSX memory card services
MemCardStop();
// Invalidate all data
InvalidateCard( 0 );
InvalidateCard( 1 );
// Finished!
// MEMCARD_DBGMSG( "[PMC] PMC ended" );
return( true );
}
/*----------------------------------------------------------------------
Function: InvalidateCard
Purpose: Invalidates a card by clearing internal flags, also frees its memory
Params: Channel of card to invalidate
Returns: None
---------------------------------------------------------------------- */
void MemCard::InvalidateCard( int Channel )
{
// Clear data on the card
s_cardData[ Channel ].m_cardStatus = CS_NoCard;
s_cardData[ Channel ].m_slotStatus = SS_Idle;
s_cardData[ Channel ].m_totalFileCount = 0;
s_cardData[ Channel ].m_totalFreeBlocks = 0;
s_cardData[ Channel ].m_nativeFileCount = 0;
MemCardClose();
// Stop whatever command was in operation and set the system to idle, the existance
// checks will then re-find this card if it is ok
if( Channel == m_nextChannel/*PKGm_nextCommand*/ )
{
m_nextCommand = CmdNone;
m_nextChannel = -1;
}
s_currentCommand = CmdNone;
s_currentChannel = Channel;
MEMCARD_DBGMSG( "[PMC] Invalidate card %d", Channel );
}
/*----------------------------------------------------------------------
Function: GetErrorString
Purpose: Returns a pointer to a string which describes a memcard error code
Params: Error code
Returns: Pointer to error string
---------------------------------------------------------------------- */
static const char *GetErrorString( int ErrorCode )
{
switch( ErrorCode )
{
case McErrNone:
return( "No error(!?)" );
break;
case McErrCardNotExist:
return( "Not connected" );
case McErrCardInvalid:
return( "Bad card" );
case McErrNewCard:
return( "New card ( card was replaced )" );
case McErrNotFormat:
return( "Not formatted" );
case McErrFileNotExist:
return( "File not found" );
case McErrAlreadyExist:
return( "File already exists" );
case McErrBlockFull:
return( "Not enough memory blocks" );
default:
return( "UNKNOWN MEMCARD ERROR CODE!" );
}
}
/*----------------------------------------------------------------------
Function: GetCardStatus
Purpose: Access function to a cards status
Params: Channel of card
Returns: Status
---------------------------------------------------------------------- */
MemCard::CARDSTATUS MemCard::GetCardStatus( int Channel )
{
return( s_cardData[ Channel ].m_cardStatus );
}
/*----------------------------------------------------------------------
Function: GetSlotStatus
Purpose: Access function to a slots status
Params: Channel of slot
Returns: Status
---------------------------------------------------------------------- */
MemCard::SLOTSTATUS MemCard::GetSlotStatus( int Channel )
{
return( s_cardData[ Channel ].m_slotStatus );
}
/*----------------------------------------------------------------------
Function: GetFileCountOnCard
Purpose: Access function that gives the number of files on a cardthat belong to this app
Params: Channel of card
Returns: Number of files on card
---------------------------------------------------------------------- */
int MemCard::GetFileCountOnCard( int Channel )
{
if( s_cardData[ Channel ].m_cardStatus == CS_ValidCard )
{
return( s_cardData[ Channel ].m_nativeFileCount );
}
else
{
// If the card is not valid then return a file count of 0
return( 0 );
}
}
/*----------------------------------------------------------------------
Function: GetFreeBlocksOnCard
Purpose: Access function that gives the number of free blocks on a card
Params: Channel of card
Returns: Free block count
---------------------------------------------------------------------- */
int MemCard::GetFreeBlocksOnCard( int Channel )
{
if( s_cardData[ Channel ].m_cardStatus == CS_ValidCard )
{
return( s_cardData[ Channel ].m_totalFreeBlocks );
}
else
{
// If the card is not valid then return a free block count of 0
return( 0 );
}
}
/*----------------------------------------------------------------------
Function: GetFileName
Purpose: Access function to return a files name
Params: Channel of card and native file number
Returns: A non-persistant pointer to ASCII filename or NULL on error
---------------------------------------------------------------------- */
const char *MemCard::GetFileName( int Channel, int File )
{
if( File >= s_cardData[ Channel ].m_nativeFileCount )
{
// Woah, we don't have this many files
return( NULL );
}
else
{
// Convert name from SJIS and return
return( &JIStoASCII( s_cardData[ Channel ].m_nativeFileInfo[ File ].m_sjisName )[strlen(SAVENAMEHEADER)] );
}
}
/*----------------------------------------------------------------------
Function: GetFileSizeInBlocks
Purpose: Access function to return the number of 8k blocks in a file
Params: Channel of card and file number
Returns: Number of blocks in file
---------------------------------------------------------------------- */
int MemCard::GetFileSizeInBlocks( int Channel, int File )
{
if( File >= s_cardData[ Channel ].m_nativeFileCount )
{
// Woah, we don't have this many files
return( 0 );
}
else
{
return( s_cardData[ Channel ].m_nativeFileInfo[ File ].m_blocks );
}
}
/*----------------------------------------------------------------------
Function: FillHeaderDetails
Purpose: Takes a pointer to a file header and fills in filename and block count. This assumes
that you have used Sonys PDATool.exe to generate the header
Params: Number of blocks to save
ASCII filename ( max 32 chars )
Returns: None
---------------------------------------------------------------------- */
void MemCard::FillHeaderDetails( unsigned char *HeaderBase, int FileLength, char *Filename )
{
int BlockCount=((FileLength-1)/BLOCKSIZE)+1;
char realFilename[32+1];
if( HeaderBase == NULL ||
BlockCount < 1 || BlockCount > 15 ||
strlen( Filename ) > 32-8 ||
Filename[ 0 ] == '\0' )
{
// Damn fool!
return;
}
// sprintf(realFilename,"%s%s",SAVENAMEHEADER,Filename);
sprintf(realFilename,"%s",SAVENAMEHEADER); // (pkg) SBSP modification
// Fill in the details
HeaderBase[ 3 ] = (char)BlockCount;
memcpy( &HeaderBase[ 4 ], ASCIItoJIS( realFilename ), 64 );
return;
}
/*----------------------------------------------------------------------
Function: ReadFile
Purpose: Starts file reading.
Params: Channel of card and file number
Callback gets called when the file reading has finsihed
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::ReadFile( int Channel, int File, void *Dest, FileCallbackDef Callback )
{
// Check for valid input and that the card is idle etc...
if( s_cardData[ Channel ].m_cardStatus != CS_ValidCard ||
File < 0 || File >= s_cardData[ Channel ].m_nativeFileCount ||
Callback == NULL )
{
return( false );
}
// Setup the load
m_nextChannel = Channel;
m_nextCommand = CmdReadFile;
s_file = File;//s_cardData[ Channel ].NativeFileInfo[ File ].DirEntry;
s_callbackFunction = Callback;
s_bufPtr = Dest;
VSync( 0 );
// MEMCARD_DBGMSG( "[PMC] File read registered for card %d file %d", Channel, File );
return( true );
}
/*----------------------------------------------------------------------
Function: WriteFile
Purpose: Starts file writing. If file supplied does not exist then a new one will be created first
Params: Channel of card and file number ( pass -1 as file number for new file )
Source points to the source data
Blocks says how many blocks to wirte ( 1 block = 8k )
Callback gets called when the file writing has finsihed
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::WriteFile( int Channel, int File, u8 *FName, void *Source, int FileLength, FileCallbackDef Callback )
{
int Blocks=((FileLength-1)/BLOCKSIZE)+1;
// Check for valid input and that the card is idle etc...
MEMCARD_DBGMSG("s: %d",s_cardData[ Channel ].m_cardStatus);
if( s_cardData[ Channel ].m_cardStatus != CS_ValidCard ||
File < -1 || File >= s_cardData[ Channel ].m_nativeFileCount ||
Blocks < 1 ||// Blocks > s_cardData[ Channel ].TotalFreeBlocks ||
Callback == NULL )
{
return( false );
}
// Setup the write as the next command
if( FName )
strncpy( s_userFileName, (char*)FName, 8 );
else
s_userFileName[0]='\0';
m_nextChannel = Channel;
m_nextCommand = CmdWriteFile;
s_file = File;
s_callbackFunction = Callback;
s_blockCount = Blocks;
s_bufPtr = Source;
VSync( 0 );
// MEMCARD_DBGMSG( "[PMC] File write registered" );
return( true );
}
/*----------------------------------------------------------------------
Function: FormatCard
Purpose: Starts file formatting. This will fail is the requested card is not unformatted
Params: Channel of card to format
Callback gets called when the format has finsihed
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::FormatCard( int Channel, FileCallbackDef Callback )
{
// Check for valid input and that the card is idle etc...
if( s_cardData[ Channel ].m_cardStatus != CS_UnformattedCard ||
Callback == NULL )
{
// MEMCARD_DBGMSG( "[PMC] Card format not registered" );
return( false );
}
// Setup the format as the next command
m_nextChannel = Channel;
m_nextCommand = CmdFormatCard;
s_callbackFunction = Callback;
s_cardData[ Channel ].m_slotStatus = SS_Formatting;
// MEMCARD_DBGMSG( "[PMC] Card format registered" );
return( true );
}
/*----------------------------------------------------------------------
Function: DeleteFile
Purpose: Starts file delete.
Params: Channel of card and file number
Callback gets called when the file writing has finsihed
Returns: Success
---------------------------------------------------------------------- */
bool MemCard::DeleteFile( int Channel, int File, FileCallbackDef Callback )
{
// Check for valid input etc...
if( s_cardData[ Channel ].m_cardStatus != CS_ValidCard ||
File < 0 || File >= s_cardData[ Channel ].m_nativeFileCount ||
Callback == NULL )
{
return( false );
}
// Setup the load as the next command
m_nextChannel = Channel;
m_nextCommand = CmdDeleteFile;
strcpy( s_fname, s_cardData[ Channel ].m_totalDirEntry[ s_cardData[ Channel ].m_nativeFileInfo[ File ].m_dirEntry ].name );
s_callbackFunction = Callback;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Writing;
// MEMCARD_DBGMSG( "[PMC] File delete registered for card %d file %d", Channel, File );
return( true );
}
/*----------------------------------------------------------------------
Function: HandleCmd_None
Purpose: Card command handler for CmdNone
In idle moments ( like these ) the memcard system checks both cards to
see if they have been inserted/removed.
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_None( void )
{
// System is idle?
if( s_syncStatus != -1 )
{
// MEMCARD_DBGMSG( "[PMC] Not quite idle...!" );
return;
}
// MEMCARD_DBGMSG( "[PMC] ..HandleCmd_None()" );
if( m_nextCommand != CmdNone &&
s_currentChannel == m_nextChannel )
{
// Execute buffered command
// MEMCARD_DBGMSG( "[PMC] Set next command.. ss:%d ch:%d", (int)s_syncStatus, s_currentChannel );
s_currentCommand = m_nextCommand;
s_currentChannel = m_nextChannel;
m_nextCommand = CmdNone;
m_nextChannel = -1;
}
else
#ifdef USE_SLOT_ONE_ONLY
{
s_currentChannel=0;
s_currentCommand = CmdExist;
}
#else
{
// Do checks that the cards are still there
// MEMCARD_DBGMSG( "[PMC] check existance of other card.." );
switch( s_activeSlot )
{
case 1:
s_currentChannel=0;
break;
case 2:
s_currentChannel=1;
break;
default:
s_currentChannel^=1;
break;
}
s_currentCommand = CmdExist;
}
#endif
}
/*----------------------------------------------------------------------
Function: HandleCmd_Exist
Purpose: Card command handler for CmdExist
If a valid card is found then it is left ( it has already been accepted ).
If a new card is found then acceptance is started next frame.
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_Exist( void )
{
if( s_syncStatus == -1 )
{
// No process active
MemCardExist( s_currentChannel << 4 );
s_cardData[ s_currentChannel ].m_slotStatus = SS_Scanning;
// MEMCARD_DBGMSG( "[PMC] called MemCardExist()" );
}
else
{
// MEMCARD_DBGMSG( "[PMC] returned from MemCardExist()" );
// Process just ended
switch( s_syncResults )
{
// Card exists
case McErrNone:
case McErrNewCard:
if( s_cardData[ s_currentChannel ].m_cardStatus == CS_ValidCard )
{
// Lovely, the old valid card is still there
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
}
else if( s_cardData[ s_currentChannel ].m_cardStatus == CS_UnformattedCard )
{
// Found a card that has previously been marked as un-formatted
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
s_currentCommand = CmdNone;
}
else
{
// A card that hasn't yet been validated is there
//MEMCARD_DBGMSG( "[PMC] PMCmdExist says card exists but not yet valid" );
s_cardData[ s_currentChannel ].m_cardStatus = CS_CardInserted;
s_currentCommand = CmdAccept;
}
break;
// Error - Invalidate the card and go back to scanning
default:
// MEMCARD_DBGMSG( "[PMC] PMCmdExist returned error '%s'", GetErrorString( s_syncResults ) );
InvalidateCard( s_currentChannel );
break;
}
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_Accept
Purpose: Card command handler for CmdAccept
If a card is accepted then start reading the directories next frame.
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_Accept( void )
{
if( s_syncStatus == -1 )
{
// No process active
MemCardAccept( s_currentChannel << 4 );
s_cardData[ s_currentChannel ].m_slotStatus = SS_Scanning;
// MEMCARD_DBGMSG( "[PMC] called MemCardAccept()" );
}
else
{
// MEMCARD_DBGMSG( "[PMC] returned from MemCardAccept()" );
// Process just ended
switch( s_syncResults )
{
// Accepted ok
case McErrNone:
case McErrNewCard:
{
//MEMCARD_DBGMSG( "[PMC] PMCmdAccept says card ok but not yet valid" );
s_currentCommand = CmdReadDir;
}
break;
// Wa-hey, an unformatted card!
case McErrNotFormat:
s_cardData[ s_currentChannel ].m_cardStatus = CS_UnformattedCard;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
s_currentCommand = CmdNone;
break;
// Other error - Invalidate the card and continue searching
default:
//MEMCARD_DBGMSG( "[PMC] PMCmdAccept returned error '%s'", GetErrorString( s_syncResults ) );
InvalidateCard( s_currentChannel );
break;
}
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_ReadDir
Purpose: Card command handler for CmdReadDir
NB: This function contains blocking calls to the system memcard library and _will_ slow down game code.
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_ReadDir( void )
{
int i,
BlockCount = 0;
// memCardGetDirectory() is blocking and returns after it has finsihed
switch( MemCardGetDirentry( s_currentChannel << 4, "*",
s_cardData[ s_currentChannel ].m_totalDirEntry,
(long int *)&s_cardData[ s_currentChannel ].m_totalFileCount,
0, MAXFILES ) )
{
// OK, now we have the cards file details
case McErrNone:
//MEMCARD_DBGMSG( "[PMC] PMCmdReadDir is valid" );
if( s_cardData[ s_currentChannel ].m_totalFileCount == 0 )
{
// Card has no files - Accept the card now I guess...
s_cardData[ s_currentChannel ].m_totalFileCount = 0;
s_cardData[ s_currentChannel ].m_totalFreeBlocks = MAXBLOCKS;
s_cardData[ s_currentChannel ].m_nativeFileCount = 0;
s_file = 0;
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_cardStatus = CS_ValidCard;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
}
else
{
// Calculate number of blocks free on this card
for( i = 0; i < s_cardData[ s_currentChannel ].m_totalFileCount; i++ )
{
BlockCount += s_cardData[ s_currentChannel ].m_totalDirEntry[ i ].size / BLOCKSIZE;
}
s_cardData[ s_currentChannel ].m_totalFreeBlocks = MAXBLOCKS - BlockCount;
// Start getting the file details from file 0
s_file = 0;
s_currentCommand = CmdReadFileInfo;
s_cardData[ s_currentChannel ].m_nativeFileCount = 0;
}
break;
// Error!
default:
//MEMCARD_DBGMSG( "[PMC] PMCmdReadDir is not valid!" );
InvalidateCard( s_currentChannel );
break;
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_ReadFileInfo
Purpose: Card command handler for Cmd
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_ReadFileInfo( void )
{
if( s_syncStatus == -1 )
{
// No process active - Start reading file info
if( MemCardReadFile( s_currentChannel << 4,
s_cardData[ s_currentChannel ].m_totalDirEntry[ s_file ].name,
(long unsigned int *)s_tempFileInfoBuffer, 0, 128 ) == 0 )
{
MEMCARD_DBGMSG( "[PMC] Reading file info for file %d", s_file );
}
}
else
{
// Must have just finished a MemCardReadFile()
switch( s_syncResults )
{
// File read ok
case McErrNone:
// Does the file we have just read look like a valid native file?
if( strncmp( s_fnameBase, s_cardData[ s_currentChannel ].m_totalDirEntry[ s_file ].name, 12 ) == 0 &&
(char)s_tempFileInfoBuffer[ 0 ] == 'S' & (char)s_tempFileInfoBuffer[ 1 ] == 'C' /*&& // Magic 'SC'
(char)s_tempFileInfoBuffer[ 68 ] == 0 && // Pad area all 0s
memcmp( (char *)&s_tempFileInfoBuffer[ 68 ], (char *)&s_tempFileInfoBuffer[ 68 + 1 ], 27 ) == 0 */ )
// Pad area check removed since some save files ( RidgeRacerR4 f'rinstance ) have crap in there :(
// Actually... This turns out to be PDA data :)
{
// Yup
int FileNumber = s_cardData[ s_currentChannel ].m_nativeFileCount++;
memcpy( s_cardData[ s_currentChannel ].m_nativeFileInfo[ FileNumber ].m_sjisName, &s_tempFileInfoBuffer[ 4 ], COMPRESSEDNAMESIZE );
s_cardData[ s_currentChannel ].m_nativeFileInfo[ FileNumber ].m_blocks = (char)s_tempFileInfoBuffer[ 3 ];
s_cardData[ s_currentChannel ].m_nativeFileInfo[ FileNumber ].m_dirEntry = s_file;
}
else
{
// Nope, ignore this file!
}
// Have we read all of the files yet?
if( s_file < s_cardData[ s_currentChannel ].m_totalFileCount - 1 )
{
// Nope - Check the next one
s_file++;
}
else
{
// Yes - Finished reading file info of all files on this card so it is now, at last, valid ( Huzzah! )
s_file = 0;
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_cardStatus = CS_ValidCard;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
}
break;
// Error - Invalidate the card and go back to scanning
default:
//MEMCARD_DBGMSG( "[PMC] PMCmdReadFileInfo returned error '%s'", GetErrorString( s_syncResults ) );
InvalidateCard( s_currentChannel );
break;
}
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_ReadFile
Purpose: Card command handler for CmdReadFile
Starts file loading, which is done in one big go.
When file reading is finished, a callback function is executed
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_ReadFile( void )
{
if( s_syncStatus == -1 )
{
// No process active - Start the load
int FileNumber = s_cardData[ s_currentChannel ].m_nativeFileInfo[ s_file ].m_dirEntry;
if( MemCardReadFile( s_currentChannel << 4, s_cardData[ s_currentChannel ].m_totalDirEntry[ FileNumber ].name,
(unsigned long *)s_bufPtr,
0, GetFileSizeInBlocks( s_currentChannel, s_file ) * BLOCKSIZE ) == 0 )
{
// MEMCARD_DBGMSG( "[PMC] Couldn't register MemCardReadFile!" );
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
}
else
{
// MEMCARD_DBGMSG( "[PMC] Called MemCardReadFile ( %d %d )", s_currentChannel, s_file );
s_cardData[ s_currentChannel ].m_slotStatus = SS_Reading;
}
}
else
{
// Must have just finished reading a file in
switch( s_syncResults )
{
case McErrNone:
// MEMCARD_DBGMSG( "[PMC] File read ok for card %d file %d", s_currentChannel, s_file );
s_file = 0;
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
// Call the callback
s_callbackFunction( CCS_ReadFileOK );
s_callbackFunction = NULL;
break;
default:
// MEMCARD_DBGMSG( "[PMC] PMCmdReadFile returned error '%s'", GetErrorString( s_syncResults ) );
// InvalidateCard( s_currentChannel );
// Call the callback
s_currentCommand = CmdExist;
s_callbackFunction( CCS_ReadFileFail );
s_callbackFunction = NULL;
break;
}
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_WriteFile
Purpose: Card command handler for CmdWriteFile
Starts file writing, which is done in one big go.
When file writing is finished, a callback function is executed
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_WriteFile( void )
{
// MEMCARD_DBGMSG( "[PMC] HandleCmd_WriteFile sync=%d", s_syncStatus );
if( s_syncStatus == -1 )
{
// No process active
int CRes;
// bool NameValid = false;
// Does the specified file exist?
if( s_file == -1 )
{
// Nope, need to create it
// do
{
// Create filename
char NewFName[ 20 + 1 ] = "\0";
CreateFName(s_fname);
strcat( NewFName, s_fnameBase ); // Base filename
strcpy( s_fname, NewFName );
// The creation ( blocking function )
CRes = MemCardCreateFile( s_currentChannel << 4, s_fname, s_blockCount );
switch( CRes )
{
case McErrNone:
MEMCARD_DBGMSG( "[PMC] Created new file '%s'", s_fname );
s_currentCommand = CmdWriteFile;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Writing;
// NameValid = true;
break;
// case McErrAlreadyExist:
// // File already exists, try next
// CreateFName();
// break;
default:
MEMCARD_DBGMSG( "[PMC] Couldn't MemCardCreateFile due to '%s'", GetErrorString( CRes ) );
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
s_callbackFunction( CCS_WriteFileFail );
s_callbackFunction = NULL;
return;
}
}
// while( NameValid == false );
}
else
{
strcpy( s_fname, s_cardData[ s_currentChannel ].m_totalDirEntry[ s_cardData[ s_currentChannel ].m_nativeFileInfo[ s_file ].m_dirEntry ].name );
}
// Start the write
if( MemCardWriteFile( s_currentChannel << 4, s_fname, (unsigned long *)s_bufPtr,
0, s_blockCount * BLOCKSIZE ) == 0 )
{
MEMCARD_DBGMSG( "[PMC] Couldn't register MemCardWriteFile due to '%s'", GetErrorString( s_syncResults ) );
s_currentCommand = CmdNone;
s_cardData[ s_currentChannel ].m_slotStatus = SS_Idle;
}
else
{
MEMCARD_DBGMSG( "[PMC] Called MemCardWriteFile" );
s_cardData[ s_currentChannel ].m_slotStatus = SS_Writing;
}
}
else
{
// Must have just finished writing a file
switch( s_syncResults )
{
case McErrNone:
MEMCARD_DBGMSG( "[PMC] File write completed!" );
// Call the callback
s_currentCommand = CmdReadDir;
s_callbackFunction( CCS_WriteFileOK );
s_callbackFunction = NULL;
break;
default:
MEMCARD_DBGMSG( "[PMC] PMCmdWriteFile returned error '%s'", GetErrorString( s_syncResults ) );
// Call the callback
s_currentCommand = CmdReadDir;
s_callbackFunction( CCS_WriteFileFail );
s_callbackFunction = NULL;
break;
}
}
}
/*----------------------------------------------------------------------
Function: HandleCmd_FormatCard
Purpose: Card command handler for CmdFormatCard
Starts card formatting, which is done in one big go and _is_ a blocking function!
When card formatting is finished, a callback function is executed
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_FormatCard( void )
{
int FRes;
long int Dummy;
MemCardSync( 0, &Dummy, &Dummy );
// Format is a blocking function
MemCardClose();
MemCardUnformat( s_currentChannel << 4 );
MemCardClose();
FRes = MemCardFormat( s_currentChannel << 4 );
// MEMCARD_DBGMSG( "[PMC] FRes = %d", FRes );
switch( FRes )
{
case McErrNone:
//MEMCARD_DBGMSG( "[PMC] Format OK!" );
s_callbackFunction( CCS_FormatCardOK );
break;
default:
//MEMCARD_DBGMSG( "[PMC] Format not ok - Returned error '%s'", GetErrorString( FRes ) );
s_callbackFunction( CCS_FormatCardFail );
break;
}
MemCardClose();
s_callbackFunction = NULL;
s_currentCommand = CmdExist;
}
/*----------------------------------------------------------------------
Function: HandleCmd_DeleteFile
Purpose: Card command handler for CmdDeleteFile
Starts file deletion, which is done in one big go and _is_ a blocking function!
When file deletion is finished, a callback function is executed
Params: None ( Uses global PMC variables :)
Returns: None
---------------------------------------------------------------------- */
void MemCard::HandleCmd_DeleteFile( void )
{
int DRes;
// Delete is a blocking function
DRes = MemCardDeleteFile( s_currentChannel << 4, s_fname );
switch( DRes )
{
case McErrNone:
//MEMCARD_DBGMSG( "[PMC] Delete OK!" );
s_callbackFunction( CCS_DeleteFileOK );
s_callbackFunction = NULL;
// Force a re-scan of the directories
s_currentCommand = CmdReadDir;
break;
default:
//MEMCARD_DBGMSG( "[PMC] Delete not ok - Returned error '%s'", GetErrorString( DRes ) );
s_callbackFunction( CCS_DeleteFileFail );
s_callbackFunction = NULL;
// Force a re-scan of the directories
s_currentCommand = CmdReadDir;
break;
}
}
/*----------------------------------------------------------------------
Function: Handler
Purpose: Main memory card handler. Call from main loop somewhere once every vsync ( or less if you dare :)
Params: None
Returns: None
---------------------------------------------------------------------- */
//#define DEBUG_MEMCARD_STATUS
#ifdef DEBUG_MEMCARD_STATUS
static int dinfo=true;
static char *cardtext[]= { "nocard", "cardinserted", "validcard", "unformattedcard" };
//static char *slottext[]= { "idle", "scanning", "reading", "writing", "formatting" };
static MemCard::CARDSTATUS lastCardStatus[2]={MemCard::CS_UnformattedCard,MemCard::CS_UnformattedCard};
static MemCard::SLOTSTATUS lastSlotStatus[2]={MemCard::SS_Formatting,MemCard::SS_Formatting};
#endif
void MemCard::Handler( void )
{
// Ensure that memory cards are active
if( s_active )
{
// debug info
#ifdef DEBUG_MEMCARD_STATUS
if(dinfo)
{
for(int i=0;i<2;i++)
{
if( lastCardStatus[i] != s_cardData[i].m_cardStatus )
{
MEMCARD_DBGMSG("card %d status changed to %s", i, cardtext[s_cardData[i].m_cardStatus] );
lastCardStatus[i] = s_cardData[i].m_cardStatus;
}
/*
if( lastSlotStatus[i] != s_cardData[i].SlotStatus )
{
MEMCARD_DBGMSG("slot %d status changed to %s", i, slottext[s_cardData[i].SlotStatus] );
lastSlotStatus[i] = s_cardData[i].SlotStatus;
}
*/
}
}
#endif
// See what the system is doing
s_syncStatus = MemCardSync( 1, &s_syncCmds, &s_syncResults );
// Don't bother calling the command function if status is 0 ( still processing )
if( s_syncStatus != 0 )
{
//MEMCARD_DBGMSG( "[PMC] run command (syn:%c%d com:%d)", s_syncStatus<0?'-':'+', s_syncStatus, s_currentCommand );
switch(s_currentCommand)
{
case CmdNone:
HandleCmd_None();
break;
case CmdExist:
HandleCmd_Exist();
break;
case CmdAccept:
HandleCmd_Accept();
break;
case CmdReadDir:
HandleCmd_ReadDir();
break;
case CmdReadFileInfo:
HandleCmd_ReadFileInfo();
break;
case CmdReadFile:
HandleCmd_ReadFile();
break;
case CmdWriteFile:
HandleCmd_WriteFile();
break;
case CmdFormatCard:
HandleCmd_FormatCard();
break;
case CmdDeleteFile:
HandleCmd_DeleteFile();
break;
}
}
}
}
void MemCard::SetActiveCardSlot( int Channel )
{
s_activeSlot = Channel;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params: *result should point to a 16byte buffer
Returns:
---------------------------------------------------------------------- */
void MemCard::GiveCheckSum(unsigned char *_result,unsigned char *_data,u32 _size)
{
MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx,_data,_size);
MD5Final(_result,&ctx);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params: *result is the checksum generated when the data was saved
Returns:
---------------------------------------------------------------------- */
int MemCard::TestCheckSum(unsigned char *_result,unsigned char *_data,u32 _size)
{
MD5_CTX ctx;
unsigned char calcedResult[MD5_CHECKSUM_SIZE];
int i;
MD5Init(&ctx);
MD5Update(&ctx,_data,_size);
MD5Final(calcedResult,&ctx);
for(i=0;i<MD5_CHECKSUM_SIZE;i++)
{
if(calcedResult[i]!=_result[i])
{
return false;
}
}
return true;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void MemCard::CreateFName(char *fname)
{
int i;
sprintf( s_fnameBase, "%s", MEMCARD_BASE_FILENAME);
// Finally, add the users savename
for(i=0;i<strlen(s_userFileName);i++)
{
s_fnameBase[12+i]=s_userFileName[i];
}
s_fnameBase[12+i]='\0';
MEMCARD_DBGMSG("MemCard filename set to '%s'", s_fnameBase );
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
bool MemCard::IsValidSavename(int _slot,char *_name)
{
int i;
CARDDEF *cdata=&s_cardData[_slot];
if(_name==NULL||_name[0]=='\0')
return false;
for(i=0;i<cdata->m_nativeFileCount;i++)
{
if(strncmp(GetFileName(_slot,i),_name,8)==0)
return false;
}
return true;
}
// returns -1 on error
int MemCard::FindFilenumberFromFilename(int _slot,char *_name)
{
int i;
CARDDEF *cdata=&s_cardData[_slot];
if(_name==NULL||_name[0]=='\0')
return -1;
for(i=0;i<cdata->m_nativeFileCount;i++)
{
if(strncmp(GetFileName(_slot,i),_name,8)==0)
return i;
}
return -1;
}
/*===========================================================================
end */