mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
769 lines
21 KiB
C++
769 lines
21 KiB
C++
/*****************************************************************************
|
|
** **
|
|
** Neversoft Entertainment. **
|
|
** **
|
|
** Copyright (C) 2000 - All Rights Reserved **
|
|
** **
|
|
******************************************************************************
|
|
** **
|
|
** Project: SYS **
|
|
** **
|
|
** Module: Mc **
|
|
** **
|
|
** File name: memcard.cpp **
|
|
** **
|
|
** Created by: 03/06/01 - spg **
|
|
** **
|
|
** Description: Memcard - platform-specific implementations **
|
|
** **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Includes **
|
|
*****************************************************************************/
|
|
|
|
#include <core/defines.h>
|
|
#include <core/singleton.h>
|
|
|
|
#include <sys/mcman.h>
|
|
|
|
#include <libmc.h>
|
|
#include <sifdev.h>
|
|
#include <libscf.h>
|
|
|
|
/*****************************************************************************
|
|
** DBG Information **
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
namespace Mc
|
|
{
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Externals **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Defines **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Types **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Data **
|
|
*****************************************************************************/
|
|
|
|
DefineSingletonClass( Manager, "MemCard Manager" );
|
|
|
|
/*****************************************************************************
|
|
** Public Data **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Prototypes **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Functions **
|
|
*****************************************************************************/
|
|
|
|
Manager::Manager( void )
|
|
{
|
|
|
|
|
|
int result;
|
|
int i, j;
|
|
|
|
result = sceMcInit();
|
|
Dbg_MsgAssert( result == 0,( "Failed to initialize memory card system\n" ));
|
|
|
|
for( i = 0; i < vMAX_PORT; i++ )
|
|
{
|
|
for( j = 0; j < vMAX_SLOT; j++ )
|
|
{
|
|
m_card[i][j].m_port = i;
|
|
m_card[i][j].m_slot = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
Manager::~Manager( void )
|
|
{
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** Public Functions **
|
|
*****************************************************************************/
|
|
|
|
int Manager::GetMaxSlots( int port )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcGetSlotMax( port );
|
|
if( result >= 0 )
|
|
{
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
Card* Manager::GetCard( int port, int slot )
|
|
{
|
|
|
|
int type;
|
|
|
|
Dbg_Assert( port < vMAX_PORT );
|
|
Dbg_Assert( slot < vMAX_SLOT );
|
|
|
|
Card* card = &m_card[port][slot];
|
|
|
|
type = card->GetDeviceType();
|
|
if( ( type == Card::vDEV_PS1_CARD ) ||
|
|
( type == Card::vDEV_PS2_CARD ))
|
|
{
|
|
return card;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int wait_for_async_call_to_finish( Card* card )
|
|
{
|
|
int ret_val;
|
|
|
|
|
|
|
|
sceMcSync( 0, NULL, &ret_val );
|
|
if( ret_val < 0 )
|
|
{
|
|
switch( ret_val )
|
|
{
|
|
case -1:
|
|
Dbg_Printf( "Switched to formated card\n" );
|
|
card->SetError( Card::vERROR_FORMATTED );
|
|
break;
|
|
case -2:
|
|
Dbg_Printf( "Error: Unformatted Card\n" );
|
|
card->SetError( Card::vERROR_UNFORMATTED );
|
|
break;
|
|
case -3:
|
|
Dbg_Printf( "Error: Insufficient Space\n" );
|
|
card->SetError( Card::vINSUFFICIENT_SPACE );
|
|
break;
|
|
case -4:
|
|
Dbg_Printf( "Error: Invalid Path\n" );
|
|
card->SetError( Card::vINVALID_PATH );
|
|
break;
|
|
case -5:
|
|
Dbg_Printf( "Error: Access Error\n" );
|
|
card->SetError( Card::vACCESS_ERROR );
|
|
break;
|
|
case -6:
|
|
Dbg_Printf( "Error: Subdir not empty\n" );
|
|
card->SetError( Card::vSUBDIR_NOT_EMPTY );
|
|
break;
|
|
case -7:
|
|
Dbg_Printf( "Error: Too many open files\n" );
|
|
card->SetError( Card::vTOO_MANY_OPEN_FILES );
|
|
break;
|
|
case -10:
|
|
Dbg_Printf( "Error: No card\n" );
|
|
card->SetError( Card::vNO_CARD );
|
|
break;
|
|
default:
|
|
card->SetError( Card::vUNKNOWN );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::MakeDirectory( const char* dir_name )
|
|
{
|
|
int result;
|
|
|
|
|
|
|
|
result = sceMcMkdir( m_port, m_slot, dir_name );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
int ErrorCode=wait_for_async_call_to_finish( this );
|
|
if (ErrorCode==-4)
|
|
{
|
|
// If, the directory exists already, don't make it an error
|
|
ErrorCode=0;
|
|
}
|
|
|
|
return( ErrorCode == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::DeleteDirectory( const char* dir_name )
|
|
{
|
|
// On the PS2, the Delete() function used for files can also
|
|
// be used to delete a directory.
|
|
return Delete(dir_name);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
const char *Card::ConvertDirectory( const char* dir_name )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::ChangeDirectory( const char* dir_name )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcChdir( m_port, m_slot, dir_name, NULL );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( this ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::Format( void )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcFormat( m_port, m_slot );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( this ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::Unformat( void )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcUnformat( m_port, m_slot );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( this ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::IsFormatted( void )
|
|
{
|
|
int formatted;
|
|
int result;
|
|
|
|
result = sceMcGetInfo( m_port, m_slot, NULL, NULL, &formatted );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
result = wait_for_async_call_to_finish( this );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( formatted == 1 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int Card::GetDeviceType( void )
|
|
{
|
|
int type;
|
|
int result;
|
|
|
|
result = sceMcGetInfo( m_port, m_slot, &type, NULL, NULL );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return vDEV_NONE;
|
|
}
|
|
|
|
result = wait_for_async_call_to_finish( this );
|
|
if( result != 0 )
|
|
{
|
|
if( ( GetLastError() != vERROR_UNFORMATTED ) &&
|
|
( GetLastError() != vERROR_FORMATTED ))
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return vDEV_NONE;
|
|
}
|
|
}
|
|
|
|
switch( type )
|
|
{
|
|
case 1:
|
|
return vDEV_PS1_CARD;
|
|
case 2:
|
|
return vDEV_PS2_CARD;
|
|
case 3:
|
|
return vDEV_POCKET_STATION;
|
|
default:
|
|
return vDEV_NONE;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int Card::GetNumFreeClusters( void )
|
|
{
|
|
int free_clusters;
|
|
int result;
|
|
|
|
result = sceMcGetInfo( m_port, m_slot, NULL, &free_clusters, NULL );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
result = wait_for_async_call_to_finish( this );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return free_clusters;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int Card::GetNumFreeEntries( const char* path )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcGetEntSpace( m_port, m_slot, path );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( this ));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::Delete( const char* filename )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcDelete( m_port, m_slot, filename );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
int ErrorCode=wait_for_async_call_to_finish( this );
|
|
if (ErrorCode==-4)
|
|
{
|
|
// The the error was that the file already did not exist, don't report this as an error.
|
|
ErrorCode=0;
|
|
}
|
|
|
|
return( ErrorCode==0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool Card::Rename( const char* old_name, const char* new_name )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcRename( m_port, m_slot, old_name, new_name );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( this ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
File* Card::Open( const char* filename, int mode, int size )
|
|
{
|
|
int result, fd;
|
|
int open_mode;
|
|
File* file;
|
|
|
|
|
|
|
|
//Dbg_MsgAssert( strlen( filename ) < 31,( "Filename exceeds max length\n" ));
|
|
|
|
file = NULL;
|
|
open_mode = 0;
|
|
if( mode & File::mMODE_READ )
|
|
{
|
|
open_mode |= SCE_RDONLY;
|
|
}
|
|
if( mode & File::mMODE_WRITE )
|
|
{
|
|
open_mode |= SCE_WRONLY;
|
|
}
|
|
if( mode & File::mMODE_CREATE )
|
|
{
|
|
open_mode |= SCE_CREAT;
|
|
}
|
|
result = sceMcOpen( m_port, m_slot, filename, open_mode );
|
|
if( result != 0 )
|
|
{
|
|
Dbg_Printf( "Unknown Error\n" );
|
|
SetError( vUNKNOWN );
|
|
return NULL;
|
|
}
|
|
|
|
fd = wait_for_async_call_to_finish( this );
|
|
if( fd >= 0 )
|
|
{
|
|
file = new File( fd, this );
|
|
}
|
|
return file;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// Ken: This used to be a local variable in Card::GetFileList, but sceMcGetDir requires that
|
|
// this buffer be 64 byte aligned, and occasionally it would not be, causing the contents of
|
|
// file_table to be undefined after the call to sceMcGetDir.
|
|
// Unfortunately, Sony did not make sceMcGetDir return an error code in this case, the blighters.
|
|
//
|
|
static sceMcTblGetDir file_table[Card::vMAX_FILES_PER_DIRECTORY];
|
|
|
|
bool Card::GetFileList( const char* mask, Lst::Head< File > &file_list )
|
|
{
|
|
|
|
int result, i;
|
|
|
|
Dbg_MsgAssert( mask != NULL,( "Mask must be a valid search string. You may use wildcards\n" ));
|
|
|
|
Dbg_MsgAssert( (((uint32)file_table)&63)==0, ("file_table not 64 byte aligned"));
|
|
|
|
|
|
int CallNumber=0;
|
|
while (true)
|
|
{
|
|
result = sceMcGetDir( m_port, m_slot, mask, CallNumber, vMAX_FILES_PER_DIRECTORY, file_table );
|
|
if( result != 0 )
|
|
{
|
|
SetError( vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
int NumFilesReturned = wait_for_async_call_to_finish( this );
|
|
for( i = 0; i < NumFilesReturned; i++ )
|
|
{
|
|
File *new_file;
|
|
|
|
new_file = new File( 0, this );
|
|
|
|
strcpy( new_file->m_Filename, (char *) file_table[i].EntryName );
|
|
new_file->m_Size = file_table[i].FileSizeByte;
|
|
|
|
new_file->m_Attribs = 0;
|
|
if( file_table[i].AttrFile & sceMcFileAttrReadable )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_READABLE;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrWriteable )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_WRITEABLE;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrExecutable )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_EXECUTABLE;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrDupProhibit )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_DUP_PROHIBITED;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrSubdir )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_DIRECTORY;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrClosed )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_CLOSED;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrPDAExec )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_PDA_APP;
|
|
}
|
|
if( file_table[i].AttrFile & sceMcFileAttrPS1 )
|
|
{
|
|
new_file->m_Attribs |= File::mATTRIB_PS1_FILE;
|
|
}
|
|
|
|
sceCdCLOCK myClock;
|
|
myClock.day=itob(file_table[i]._Modify.Day);
|
|
myClock.hour=itob(file_table[i]._Modify.Hour);
|
|
myClock.minute=itob(file_table[i]._Modify.Min);
|
|
myClock.month=itob(file_table[i]._Modify.Month);
|
|
myClock.second=itob(file_table[i]._Modify.Sec);
|
|
myClock.year=itob(file_table[i]._Modify.Year%100);
|
|
sceScfGetLocalTimefromRTC(&myClock);
|
|
new_file->m_Modified.m_Seconds = btoi(myClock.second);
|
|
new_file->m_Modified.m_Minutes = btoi(myClock.minute);
|
|
new_file->m_Modified.m_Hour = btoi(myClock.hour);
|
|
new_file->m_Modified.m_Day = btoi(myClock.day);
|
|
new_file->m_Modified.m_Month = btoi(myClock.month);
|
|
new_file->m_Modified.m_Year = btoi(myClock.year)+2000;
|
|
|
|
myClock.day=itob(file_table[i]._Create.Day);
|
|
myClock.hour=itob(file_table[i]._Create.Hour);
|
|
myClock.minute=itob(file_table[i]._Create.Min);
|
|
myClock.month=itob(file_table[i]._Create.Month);
|
|
myClock.second=itob(file_table[i]._Create.Sec);
|
|
myClock.year=itob(file_table[i]._Create.Year%100);
|
|
sceScfGetLocalTimefromRTC(&myClock);
|
|
new_file->m_Created.m_Seconds = btoi(myClock.second);
|
|
new_file->m_Created.m_Minutes = btoi(myClock.minute);
|
|
new_file->m_Created.m_Hour = btoi(myClock.hour);
|
|
new_file->m_Created.m_Day = btoi(myClock.day);
|
|
new_file->m_Created.m_Month = btoi(myClock.month);
|
|
new_file->m_Created.m_Year = btoi(myClock.year)+2000;
|
|
|
|
file_list.AddToTail( new_file );
|
|
}
|
|
|
|
if (NumFilesReturned<vMAX_FILES_PER_DIRECTORY)
|
|
{
|
|
break;
|
|
}
|
|
++CallNumber;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
File::File( int fd, Card* card ) : Lst::Node< File > ( this ), m_fd( fd ), m_card( card )
|
|
{
|
|
}
|
|
|
|
File::~File()
|
|
{
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool File::Close( void )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcClose( m_fd );
|
|
if( result != 0 )
|
|
{
|
|
m_card->SetError( Card::vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( m_card ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int File::Seek( int offset, FilePointerBase base )
|
|
{
|
|
int result;
|
|
int mode;
|
|
|
|
|
|
|
|
switch( base )
|
|
{
|
|
case BASE_START:
|
|
mode = 0;
|
|
break;
|
|
case BASE_CURRENT:
|
|
mode = 1;
|
|
break;
|
|
case BASE_END:
|
|
mode = 2;
|
|
break;
|
|
default:
|
|
mode = 1;
|
|
Dbg_MsgAssert( 0,( "Invalid FilePointerBase\n" ));
|
|
break;
|
|
}
|
|
|
|
result = sceMcSeek( m_fd, offset, mode );
|
|
if( result != 0 )
|
|
{
|
|
m_card->SetError( Card::vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( m_card ));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool File::Flush( void )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcFlush( m_fd );
|
|
if( result != 0 )
|
|
{
|
|
m_card->SetError( Card::vUNKNOWN );
|
|
return false;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( m_card ) == 0 );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int File::Write( void* data, int len )
|
|
{
|
|
int result;
|
|
|
|
result = sceMcWrite( m_fd, data, len );
|
|
if( result != 0 )
|
|
{
|
|
m_card->SetError( Card::vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( m_card ));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int File::Read( void* buff, int len )
|
|
{
|
|
int result;
|
|
|
|
|
|
|
|
result = sceMcRead( m_fd, buff, len );
|
|
if( result != 0 )
|
|
{
|
|
Dbg_Printf( "Error: Could not read data\n" );
|
|
m_card->SetError( Card::vUNKNOWN );
|
|
return 0;
|
|
}
|
|
|
|
return( wait_for_async_call_to_finish( m_card ));
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
} // namespace Mc
|
|
|
|
|
|
|
|
|