/***************************************************************************** ** ** ** 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 #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Mc { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ #define MAX_FILENAME_LENGTH 128 /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ DefineSingletonClass( Manager, "MemCard Manager" ); static char cardFilenameBuffer[MAX_FILENAME_LENGTH]; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ Manager::Manager( void ) { int i, j; 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; } } m_hard_drive.SetAsHardDrive(); } /******************************************************************/ /* */ /* */ /******************************************************************/ Manager::~Manager( void ) { } /***************************************************************************** ** Public Functions ** *****************************************************************************/ int Manager::GetMaxSlots( int port ) { return vMAX_SLOT; } /******************************************************************/ /* */ /* */ /******************************************************************/ Card* Manager::GetCard( int port, int slot ) { // Ignore port and slot, since on the XBox we're only using the hard drive. return &m_hard_drive; } // Skate3 code, disabled for now. # if 0 Card* Manager::GetHardDrive( ) { return &m_hard_drive; } const char* Manager::GetLastCardName( int port, int slot ) { Dbg_Assert( port < vMAX_PORT ); Dbg_Assert( slot < vMAX_SLOT ); Card *p_card = &m_card[port][slot]; return p_card->GetLastPersonalizedName(); } /******************************************************************/ /* */ /* */ /******************************************************************/ const char *Card::GetPersonalizedName() { // Only get the name if the card is mounted and is not the hard drive. if (m_mounted_drive_letter && m_mounted_drive_letter!='u') { sprintf(mp_personalized_name,""); WCHAR p_personalized_name[MAX_MUNAME+10]; DWORD rv=XMUNameFromDriveLetter(m_mounted_drive_letter,p_personalized_name,MAX_MUNAME); if (rv==ERROR_SUCCESS) { // Instead of doing a wsprintfA( mp_personalized_name, "%ls", p_personalized_name ); // which will terminate as soon as it hits a bad char, convert each WCHAR one at a // time so that all good characters come across. char *p_dest=mp_personalized_name; int count=0; WCHAR p_temp[2]; // A buffer for holding one WCHAR at a time for sending to wsprintfA p_temp[1]=0; const WCHAR *p_scan=p_personalized_name; while (*p_scan) // WCHAR strings are terminated by a 0 just like normal strings, except its a 2byte 0. { p_temp[0]=*p_scan++; char p_one_char[10]; wsprintfA( p_one_char, "%ls", p_temp); // p_one_char now contains a one char string. if (count=75) { return true; } return false; } # endif // Note: dir_name must no longer start with a backslash, it should just be "Career2-Career" etc. bool Card::MakeDirectory( const char* dir_name ) { char output_dir[vDIRECTORY_NAME_BUF_SIZE]; WCHAR input_name[vDIRECTORY_NAME_BUF_SIZE]; wsprintfW( input_name, L"%hs", dir_name ); DWORD rv = XCreateSaveGame( "u:\\", // Root of device on which to create the save game. input_name, // Name of save game (effectively directory name). OPEN_ALWAYS, // Open disposition. 0, // Creation flags. output_dir, // String to take resultant directory name buffer. vDIRECTORY_NAME_BUF_SIZE ); // Size of directory name buffer. if( rv == ERROR_SUCCESS ) { return true; } if (rv==ERROR_DISK_FULL) { m_last_error=vINSUFFICIENT_SPACE; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ const char *Card::ConvertDirectory( const char* dir_name ) { static char output_dir[vDIRECTORY_NAME_BUF_SIZE]; WCHAR input_name[vDIRECTORY_NAME_BUF_SIZE]; wsprintfW( input_name, L"%hs", dir_name ); DWORD rv = XCreateSaveGame( "u:\\", // Root of device on which to create the save game. input_name, // Name of save game (effectively directory name). OPEN_EXISTING, // Open disposition. 0, // Creation flags. output_dir, // String to take resultant directory name buffer. vDIRECTORY_NAME_BUF_SIZE ); // Size of directory name buffer. if( rv == ERROR_SUCCESS ) { // Remove the initial "u:\" and the final "\" strcpy(output_dir,output_dir+3); output_dir[strlen(output_dir)-1]=0; return output_dir; } return NULL; } // Skate3 code, disabled for now. #if 0 /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::ConvertDirectory( const char* dir_name, char* output_name ) { // K: This used to assert. if (m_mounted_drive_letter==0) { return false; } // Seems incoming filenames are of the form /foo etc. cardFilenameBuffer[0] = m_mounted_drive_letter; cardFilenameBuffer[1] = ':'; cardFilenameBuffer[2] = '\\'; cardFilenameBuffer[3] = 0; ++dir_name; int index = 4; while( cardFilenameBuffer[index] = *dir_name ) { // Switch forward slash directory separators to the supported backslash. if( cardFilenameBuffer[index] == '/' ) { cardFilenameBuffer[index] = '\\'; } ++index; ++dir_name; } char output_dir[64]; WCHAR input_name[64]; wsprintfW( input_name, L"%hs", &cardFilenameBuffer[4] ); DWORD rv = XCreateSaveGame( cardFilenameBuffer, // Root of device on which to create the save game. input_name, // Name of save game (effectively directory name). OPEN_EXISTING, // Open disposition. 0, // Creation flags. output_dir, // String to take resultant directory name buffer. 64 ); // Size of directory name buffer. if( rv == ERROR_SUCCESS ) { // Copy over output directory, stripping leading drive, colon and backslash, and removing trailing backslash. strcpy( output_name, &output_dir[3] ); output_name[strlen( output_name ) - 1] = 0; return true; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::DeleteDirectory( const char* dir_name ) { Dbg_Assert( m_mounted_drive_letter != 0 ); // Seems incoming filenames are of the form /foo etc. cardFilenameBuffer[0] = m_mounted_drive_letter; cardFilenameBuffer[1] = ':'; cardFilenameBuffer[2] = '\\'; cardFilenameBuffer[3] = 0; ++dir_name; int index = 4; while( cardFilenameBuffer[index] = *dir_name ) { // Switch forward slash directory separators to the supported backslash. if( cardFilenameBuffer[index] == '/' ) { cardFilenameBuffer[index] = '\\'; } ++index; ++dir_name; } WCHAR input_name[64]; wsprintfW( input_name, L"%hs", &cardFilenameBuffer[4] ); DWORD rv = XDeleteSaveGame( cardFilenameBuffer, // Root of device on which to create the save game. input_name ); if( rv == ERROR_SUCCESS ) { return true; } return false; } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ // Given the name of a file, this will delete it's directory, which // will result in the deletion of the directory, the file, and the icon. // The name must not be preceded with a backslash, ie should be "Career12-Career" for example. bool Card::DeleteDirectory( const char* dir_name ) { WCHAR input_name[64]; wsprintfW( input_name, L"%hs", dir_name ); DWORD rv = XDeleteSaveGame( "u:\\", input_name ); if( rv == ERROR_SUCCESS ) { return true; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::ChangeDirectory( const char* dir_name ) { return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::Format( void ) { // Not supported from within an Xbox title. return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::Unformat( void ) { // Not supported from within an Xbox title. return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::IsFormatted( void ) { // Must be formatted to have got this far on an Xbox title. return true; } void Card::SetAsHardDrive() { m_mounted_drive_letter='u'; } // Skate3 code, disabled for now. #if 0 bool Card::IsHardDrive() { return m_mounted_drive_letter=='u'; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Card::UnMount( void ) { // Can't unmount the hard drive. if( m_mounted_drive_letter=='u' ) { return; } m_mount_error=false; m_mount_failed_due_to_card_full=false; m_mount_failed_due_to_card_unformatted=false; if( m_mounted_drive_letter ) { DWORD rv = XUnmountMU(m_port, ( m_slot == 0 ) ? XDEVICE_TOP_SLOT : XDEVICE_BOTTOM_SLOT); if (rv != ERROR_SUCCESS) { m_mount_error=true; if (rv==ERROR_DISK_FULL) { m_mount_failed_due_to_card_full=true; } if (rv==ERROR_UNRECOGNIZED_VOLUME) { m_mount_failed_due_to_card_unformatted=true; } } m_mounted_drive_letter=0; } } #endif int Card::GetDeviceType( void ) { return vDEV_XBOX_HARD_DRIVE; } // Skate3 code, disabled for now. #if 0 bool Card::GetMountError() { return m_mount_error; } bool Card::MountFailedDueToCardFull() { return m_mount_failed_due_to_card_full; } bool Card::MountFailedDueToCardUnformatted() { return m_mount_failed_due_to_card_unformatted; } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ int Card::GetNumFreeClusters( void ) { if( m_mounted_drive_letter ) { char p_drive[20]; strcpy(p_drive,"z:\\"); ULARGE_INTEGER uliFreeAvail; ULARGE_INTEGER uliTotal; p_drive[0] = m_mounted_drive_letter; BOOL br = GetDiskFreeSpaceEx( p_drive, &uliFreeAvail, &uliTotal, NULL ); if( br ) { // Each increment of HighPart represents 2^32 bytes, which is (2^32)/16384=262144 blocks. return uliFreeAvail.HighPart*262144 + uliFreeAvail.LowPart/16384; } else { return 0; } } return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ int Card::GetNumFreeEntries( const char* path ) { return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::Delete( const char* filename ) { return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::Rename( const char* old_name, const char* new_name ) { return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ File* Card::Open( const char* filename, int mode, int size ) { Dbg_Assert( m_mounted_drive_letter != 0 ); m_last_error=0; File* p_file = NULL; HANDLE handle; // Seems incoming filenames are of the form /foo/bar etc. cardFilenameBuffer[0] = m_mounted_drive_letter; cardFilenameBuffer[1] = ':'; int index = 2; while( cardFilenameBuffer[index] = *filename ) { // Switch forward slash directory separators to the supported backslash. if( cardFilenameBuffer[index] == '/' ) { cardFilenameBuffer[index] = '\\'; } ++index; ++filename; } DWORD dwDesiredAccess; DWORD dwCreationDisposition; switch( mode ) { case File::mMODE_READ: { dwDesiredAccess = GENERIC_READ; dwCreationDisposition = OPEN_EXISTING; break; } case File::mMODE_WRITE: { dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = OPEN_EXISTING; break; } case ( File::mMODE_WRITE | File::mMODE_CREATE ): { dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = OPEN_ALWAYS; break; } case File::mMODE_CREATE: { dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = CREATE_NEW; break; } case ( File::mMODE_READ | File::mMODE_WRITE ): { dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; dwCreationDisposition = OPEN_EXISTING; break; } default: { Dbg_Assert( 0 ); return NULL; } } handle = CreateFile( cardFilenameBuffer, // file name dwDesiredAccess, // access mode 0, // share mode NULL, // security attributes dwCreationDisposition, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes and flags NULL ); // handle to template file if( handle != INVALID_HANDLE_VALUE ) { p_file = new File( (int)handle, this ); WIN32_FILE_ATTRIBUTE_DATA file_attribute_data; if (GetFileAttributesEx(cardFilenameBuffer,GetFileExInfoStandard,&file_attribute_data)) { // Skate3 code, disabled for now. # if 0 p_file->m_file_time=file_attribute_data.ftLastWriteTime; # endif } return p_file; } else { if (GetLastError()==ERROR_DISK_FULL) { m_last_error=vINSUFFICIENT_SPACE; } } return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Card::GetFileList( const char* mask, Lst::Head< File > &file_list ) { HANDLE handle; XGAME_FIND_DATA find_data; cardFilenameBuffer[0] = m_mounted_drive_letter; cardFilenameBuffer[1] = ':'; cardFilenameBuffer[2] = '\\'; cardFilenameBuffer[3] = 0; if(( handle = XFindFirstSaveGame( cardFilenameBuffer, &find_data )) == INVALID_HANDLE_VALUE ) { return true; } do { File* new_file = new File( 0, this ); // Skip copying the drive stuff, just copy the directory, and strip the trailing '\'. strcpy( new_file->m_Filename, (char*)&find_data.szSaveGameDirectory[3] ); new_file->m_Filename[strlen( new_file->m_Filename ) - 1] = 0; wsprintfA( new_file->m_DisplayFilename, "%ls", find_data.szSaveGameName ); FILETIME local_file_time; FileTimeToLocalFileTime(&find_data.wfd.ftLastWriteTime,&local_file_time); SYSTEMTIME system_file_time; FileTimeToSystemTime(&local_file_time,&system_file_time); new_file->m_Modified.m_Year=system_file_time.wYear; new_file->m_Modified.m_Month=system_file_time.wMonth; new_file->m_Modified.m_Day=system_file_time.wDay; new_file->m_Modified.m_Hour=system_file_time.wHour; new_file->m_Modified.m_Minutes=system_file_time.wMinute; new_file->m_Modified.m_Seconds=system_file_time.wSecond; new_file->m_Size = find_data.wfd.nFileSizeLow; new_file->m_Attribs = 0; file_list.AddToTail( new_file ); } while( XFindNextSaveGame( handle, &find_data )); XFindClose( handle ); return true; } File::File( int fd, Card* card ) : Lst::Node< File > ( this ), m_fd( fd ), m_card( card ) { } File::~File() { } /******************************************************************/ /* */ /* */ /******************************************************************/ int File::Seek( int offset, FilePointerBase base ) { Dbg_Assert( m_fd != 0 ); DWORD dwMoveMethod; switch( base ) { case BASE_START: dwMoveMethod = FILE_BEGIN; break; case BASE_CURRENT: dwMoveMethod = FILE_CURRENT; break; case BASE_END: dwMoveMethod = FILE_END; break; default: dwMoveMethod = FILE_END; Dbg_MsgAssert( 0,( "Invalid FilePointerBase\n" )); break; } DWORD result = SetFilePointer( (HANDLE)m_fd, // handle to file offset, // bytes to move pointer NULL, // high-order bytes to move pointer dwMoveMethod ); // starting point return result; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool File::Flush( void ) { Dbg_Assert( m_fd != 0 ); FlushFileBuffers((HANDLE)m_fd ); // The FlushFileBuffers() is pretty strict about what types of files wmay be flushed, // whereas the PS2 equivalent doesn't really care. Just return a positive response always, // no critical stuff predicated on this return anway. return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ int File::Write( void* data, int len ) { Dbg_Assert( m_fd != 0 ); // Skate3 code, disabled for now. # if 0 m_not_enough_space_to_write_file=false; # endif DWORD bytes_written; BOOL rv = WriteFile( (HANDLE)m_fd, // handle to file data, // data buffer len, // number of bytes to write &bytes_written, // number of bytes written NULL ); // overlapped buffer // Skate3 code, disabled for now. # if 0 if (rv==ERROR_NOT_ENOUGH_MEMORY) { m_not_enough_space_to_write_file=true; } if (GetLastError()==ERROR_DISK_FULL) { m_not_enough_space_to_write_file=true; } # endif if( rv ) { return (int)bytes_written; } return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ int File::Read( void* buff, int len ) { Dbg_Assert( m_fd != 0 ); DWORD bytes_read; BOOL rv = ReadFile( (HANDLE)m_fd, // handle to file buff, // data buffer len, // number of bytes to read &bytes_read, // number of bytes read NULL ); // overlapped buffer if( rv ) { return (int)bytes_read; } return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool File::Close( void ) { Dbg_Assert( m_fd != 0 ); return CloseHandle((HANDLE)m_fd ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Mc