/////////////////////////////////////////////////////////////////////////////////////// // // p_AsyncFilesys.cpp GRJ 11 Oct 2002 // // Asynchronous file system // /////////////////////////////////////////////////////////////////////////////////////// #include #include namespace File { /******************************************************************/ /* */ /* */ /******************************************************************/ static uint32 RoundToNearestSectorSize( uint32 size ) { // Round up to the nearest sector size of a DVD, which is 2048 bytes. return ( size + 0x7FF ) & ~0x7FF; } /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxAsyncFileHandle::CXboxAsyncFileHandle( void ) { m_num_open_requests = 0; mp_non_aligned_buffer = NULL; mp_temp_aligned_buffer = NULL; mh_file = INVALID_HANDLE_VALUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ CXboxAsyncFileHandle::~CXboxAsyncFileHandle() { } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxAsyncFileHandle::plat_open( const char *filename ) { m_position = 0; // The following code duplicates that in prefopen(). At some point the filenme modification code // should probably be abstracted out. // Used for prepending the root data directory on filesystem calls. char nameConversionBuffer[256] = "d:\\data\\"; int index = 8; const char* p_skip; if(( filename[0] == 'c' ) && ( filename[1] == ':' )) { // Filename is of the form c:\skate4\data\foo... p_skip = filename + 15; } else { // Filename is of the form foo... p_skip = filename; } while( nameConversionBuffer[index] = *p_skip ) { // Switch forward slash directory separators to the supported backslash. if( nameConversionBuffer[index] == '/' ) { nameConversionBuffer[index] = '\\'; } ++index; ++p_skip; } nameConversionBuffer[index] = 0; // If this is a .tex file, switch to a .txx file. if((( nameConversionBuffer[index - 1] ) == 'x' ) && (( nameConversionBuffer[index - 2] ) == 'e' ) && (( nameConversionBuffer[index - 3] ) == 't' )) { nameConversionBuffer[index - 2] = 'x'; } // If this is a .pre file, switch to a .prx file. if((( nameConversionBuffer[index - 1] ) == 'e' ) && (( nameConversionBuffer[index - 2] ) == 'r' ) && (( nameConversionBuffer[index - 3] ) == 'p' )) { nameConversionBuffer[index - 1] = 'x'; } inc_busy_count(); mh_file = CreateFile( nameConversionBuffer, // File name GENERIC_READ, // Access mode FILE_SHARE_READ, // Share mode NULL, // SD OPEN_EXISTING, // How to create FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, // File attributes NULL ); // Handle to template file if( mh_file == INVALID_HANDLE_VALUE ) { return false; } // Immediately obtain the file size. m_file_size = ::GetFileSize( mh_file, NULL ); Dbg_Assert( m_file_size != -1 ); // Round to nearest sector size, since this will be required for async reads anyway. m_file_size = RoundToNearestSectorSize( m_file_size ); io_callback( m_current_function, (int)mh_file, m_file_size ); return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::io_callback( EAsyncFunctionType function, int result, uint32 data ) { // Do any special function processing switch (function) { case FUNC_OPEN: { if( result < 0 ) { // Dbg_MsgAssert( 0, ( "Error: Async open returned %d", result )); } else { m_file_size = data; } // ShowAsyncInfo("io_callback: Open returned handle index %d with filesize %d\n", result, data); break; } case FUNC_SEEK: if (result < 0) { Dbg_MsgAssert(0, ("Error: Async seek returned %d", result)); } else { m_position = result; } break; case FUNC_LOAD: case FUNC_READ: if (mp_temp_aligned_buffer && (result > 0)) { memcpy(mp_non_aligned_buffer, mp_temp_aligned_buffer, result); } // don't break, continue on below case FUNC_WRITE: if (result < 0) { Dbg_MsgAssert(0, ("Error: Async IO returned %d", result)); } else { m_position += result; if (mp_temp_aligned_buffer) { delete mp_temp_aligned_buffer; mp_non_aligned_buffer = NULL; mp_temp_aligned_buffer = NULL; } } break; case FUNC_IDLE: // Don't want m_busy to change or user-defined callback to be executed return; default: break; } // Check to make sure request buffer will be clear if (get_busy_count() > 0) { //Dbg_MsgAssert(m_num_open_requests == 1, ("Sill other open requests")); } // Now handle the above p-line stuff CAsyncFileHandle::io_callback(function, result, data); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxAsyncFileHandle::add_request_id( int request_id, EAsyncFunctionType function ) { if (m_num_open_requests >= NUM_REQUESTS) { Dbg_MsgAssert(0, ("Too many open requests for file handle")); return false; } m_open_requests[m_num_open_requests].m_request_id = request_id; m_open_requests[m_num_open_requests++].m_function = function; //scePrintf("Adding request %d to entry #%d for %x with busy %d\n", request_id, (m_num_open_requests-1), this, get_busy_count()); return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ EAsyncFunctionType CXboxAsyncFileHandle::get_request_function( int request_id ) const { // Find request id in list for (int i = 0; i < m_num_open_requests; i++) { if (m_open_requests[i].m_request_id == request_id) { return m_open_requests[i].m_function; } } // Request not found Dbg_MsgAssert(0, ("Can't find request %d on %x", request_id, this)); return FUNC_IDLE; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxAsyncFileHandle::clear_request_id( int request_id ) { if (m_num_open_requests <= 0) { Dbg_MsgAssert(0, ("Can't clear request: Open request list empty")); return false; } // Find request id in list for (int i = 0; i < m_num_open_requests; i++) { if (m_open_requests[i].m_request_id == request_id) { // Just move end of list to here m_open_requests[i] = m_open_requests[--m_num_open_requests]; //scePrintf("Clearing request %d from entry #%d\n", request_id, i); return true; } } // Request not found Dbg_MsgAssert(0, ("Can't clear request: Not found")); return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_init( void ) { m_num_open_requests = 0; mp_non_aligned_buffer = NULL; mp_temp_aligned_buffer = NULL; // Set up the overlapped structure. m_overlapped.Offset = 0; m_overlapped.OffsetHigh = 0; m_overlapped.hEvent = NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ volatile bool CXboxAsyncFileHandle::plat_is_done( void ) { bool is_done = false; if( m_last_result == ERROR_IO_PENDING ) { is_done = HasOverlappedIoCompleted( &m_overlapped ); if( is_done ) { // I/O operation is complete, so GetOverlappedResult() should retuen true. uint32 bt; bool result = GetOverlappedResult( mh_file, // handle to file, pipe, or device &m_overlapped, // overlapped structure &bt, // bytes transferred false ); // wait option Dbg_Assert( result ); m_last_result = ERROR_SUCCESS; // Must call this when we are done. io_callback( m_current_function, bt, 0 ); } } return is_done; } /******************************************************************/ /* */ /* */ /******************************************************************/ volatile bool CXboxAsyncFileHandle::plat_is_busy( void ) { return ( m_last_result == ERROR_IO_PENDING ); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxAsyncFileHandle::plat_is_eof( void ) const { return ( m_last_result == ERROR_HANDLE_EOF ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_set_priority( int priority ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_set_stream( bool stream ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_set_destination( EAsyncMemoryType destination ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_set_buffer_size( size_t buffer_size ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CXboxAsyncFileHandle::plat_set_blocking( bool block ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ size_t CXboxAsyncFileHandle::plat_load( void *p_buffer ) { // This function will just get the file size and do one big read. // Set up the overlapped structure. m_overlapped.Offset = 0; m_overlapped.OffsetHigh = 0; m_overlapped.hEvent = NULL; // And do the read. return plat_read( p_buffer, 1, GetFileSize()); } /******************************************************************/ /* */ /* */ /******************************************************************/ size_t CXboxAsyncFileHandle::plat_read( void *p_buffer, size_t size, size_t count ) { uint32 total_bytes = RoundToNearestSectorSize( size * count ); inc_busy_count(); bool result = ReadFile( mh_file, // Handle to file p_buffer, // Data buffer total_bytes, // Number of bytes to read NULL, // Number of bytes read &m_overlapped ); // Overlapped buffer // If there was a problem, or the async. operation's still pending... if( !result ) { // Deal with the error code. m_last_result = GetLastError(); switch( m_last_result ) { case ERROR_HANDLE_EOF: { // We've reached the end of the file during the call to ReadFile. break; } case ERROR_IO_PENDING: { break; } default: { Dbg_Assert( 0 ); break; } } } return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ size_t CXboxAsyncFileHandle::plat_write( void *p_buffer, size_t size, size_t count ) { Dbg_Assert( 0 ); return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ char *CXboxAsyncFileHandle::plat_get_s( char *p_buffer, int maxlen ) { Dbg_Assert( 0 ); return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ int CXboxAsyncFileHandle::plat_seek( long offset, int origin ) { Dbg_Assert( 0 ); return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CXboxAsyncFileHandle::plat_close( void ) { if( mh_file != INVALID_HANDLE_VALUE ) { inc_busy_count(); CloseHandle( mh_file ); mh_file = INVALID_HANDLE_VALUE; io_callback( m_current_function, 0, 0 ); return true; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CAsyncFileLoader::s_plat_init( void ) { for( int i = 0; i < MAX_FILE_HANDLES; ++i ) { if( s_file_handles[i] ) { s_file_handles[i]->init(); } else { s_file_handles[i] = new CXboxAsyncFileHandle; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CAsyncFileLoader::s_plat_cleanup( void ) { for( int i = 0; i < MAX_FILE_HANDLES; ++i ) { if( s_file_handles[i] ) { delete s_file_handles[i]; s_file_handles[i] = NULL; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CAsyncFileLoader::s_plat_async_supported( void ) { return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CAsyncFileLoader::s_plat_exist( const char *filename ) { HANDLE h_file = CreateFile( filename, // File name 0, // Access mode FILE_SHARE_READ, // Share mode NULL, // SD OPEN_EXISTING, // How to create FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, // File attributes NULL ); // Handle to template file if( h_file == INVALID_HANDLE_VALUE ) { return false; } CloseHandle( h_file ); return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ CAsyncFileHandle *CAsyncFileLoader::s_plat_open( const char *filename, int priority ) { CXboxAsyncFileHandle *p_handle = new CXboxAsyncFileHandle(); p_handle->plat_init(); bool opened = p_handle->plat_open( filename ); if( !opened ) { delete p_handle; return NULL; } return p_handle; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CAsyncFileLoader::s_plat_swap_callback_list( void ) { s_cur_callback_list_index = s_cur_callback_list_index ^ 1; // Swap indices... s_new_io_completion = false; // ...and clear flag. } /******************************************************************/ /* */ /* */ /******************************************************************/ void CAsyncFileLoader::s_plat_update( void ) { for( int h = 0; h < MAX_FILE_HANDLES; ++h ) { if( s_file_handles[h] ) { if( s_file_handles[h]->IsBusy()) { CXboxAsyncFileHandle* p_xbox_handle = static_cast( s_file_handles[h] ); p_xbox_handle->plat_is_done(); } } } } }