in Kits/ATGTK/WaveBankReader.cpp [498:910]
HRESULT WaveBankReader::Impl::Open( const wchar_t* szFileName )
{
Close();
Clear();
m_prepared = false;
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
m_event.reset( CreateEventEx( nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE ) );
#else
m_event.reset( CreateEvent( nullptr, TRUE, FALSE, nullptr ) );
#endif
if ( !m_event )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0 };
params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
params.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN;
ScopedHandle hFile( safe_handle( CreateFile2( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
¶ms ) ) );
#else
ScopedHandle hFile( safe_handle( CreateFileW( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr ) ) );
#endif
if ( !hFile )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
// Read and verify header
OVERLAPPED request = {};
request.hEvent = m_event.get();
bool wait = false;
if( !ReadFile( hFile.get(), &m_header, sizeof( m_header ), nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
DWORD bytes;
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
BOOL result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
BOOL result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( bytes != sizeof( m_header ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( m_header.dwSignature != HEADER::SIGNATURE && m_header.dwSignature != HEADER::BE_SIGNATURE )
{
return E_FAIL;
}
bool be = ( m_header.dwSignature == HEADER::BE_SIGNATURE );
if ( be )
{
m_header.BigEndian();
}
if ( m_header.dwHeaderVersion != HEADER::VERSION )
{
return E_FAIL;
}
// Load bank data
memset( &request, 0, sizeof(request) );
request.Offset = m_header.Segments[HEADER::SEGIDX_BANKDATA].dwOffset;
request.hEvent = m_event.get();
wait = false;
if( !ReadFile( hFile.get(), &m_data, sizeof( m_data ), nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( bytes != sizeof( m_data ) ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( be )
m_data.BigEndian();
if ( !m_data.dwEntryCount )
{
return HRESULT_FROM_WIN32( ERROR_NO_DATA );
}
if ( m_data.dwFlags & BANKDATA::TYPE_STREAMING )
{
if ( m_data.dwAlignment < ALIGNMENT_DVD )
return E_FAIL;
if ( m_data.dwAlignment % DVD_SECTOR_SIZE )
return E_FAIL;
}
else if ( m_data.dwAlignment < ALIGNMENT_MIN )
{
return E_FAIL;
}
if ( m_data.dwFlags & BANKDATA::FLAGS_COMPACT )
{
if ( m_data.dwEntryMetaDataElementSize != sizeof(ENTRYCOMPACT) )
{
return E_FAIL;
}
if ( m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength > ( MAX_COMPACT_DATA_SEGMENT_SIZE * m_data.dwAlignment ) )
{
// Data segment is too large to be valid compact wavebank
return E_FAIL;
}
}
else
{
if ( m_data.dwEntryMetaDataElementSize != sizeof(ENTRY) )
{
return E_FAIL;
}
}
DWORD metadataBytes = m_header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwLength;
if ( metadataBytes != ( m_data.dwEntryCount * m_data.dwEntryMetaDataElementSize ) )
{
return E_FAIL;
}
// Load names
DWORD namesBytes = m_header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwLength;
if ( namesBytes > 0 )
{
if ( namesBytes >= ( m_data.dwEntryNameElementSize * m_data.dwEntryCount ) )
{
std::unique_ptr<char[]> temp( new (std::nothrow) char[ namesBytes ] );
if ( !temp )
return E_OUTOFMEMORY;
memset( &request, 0, sizeof(request) );
request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYNAMES].dwOffset;
request.hEvent = m_event.get();
wait = false;
if ( !ReadFile( hFile.get(), temp.get(), namesBytes, nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( namesBytes != bytes ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
for( uint32_t j = 0; j < m_data.dwEntryCount; ++j )
{
DWORD n = m_data.dwEntryNameElementSize * j;
char name[ 64 ] = {0};
strncpy_s( name, &temp[ n ], 64 );
m_names[ name ] = j;
}
}
}
// Load entries
if ( m_data.dwFlags & BANKDATA::FLAGS_COMPACT )
{
m_entries.reset( reinterpret_cast<uint8_t*>( new (std::nothrow) ENTRYCOMPACT[ m_data.dwEntryCount ] ) );
}
else
{
m_entries.reset( reinterpret_cast<uint8_t*>( new (std::nothrow) ENTRY[ m_data.dwEntryCount ] ) );
}
if ( !m_entries )
return E_OUTOFMEMORY;
memset( &request, 0, sizeof(request) );
request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYMETADATA].dwOffset;
request.hEvent = m_event.get();
wait = false;
if ( !ReadFile( hFile.get(), m_entries.get(), metadataBytes, nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( metadataBytes != bytes ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( be )
{
if ( m_data.dwFlags & BANKDATA::FLAGS_COMPACT )
{
auto ptr = reinterpret_cast<ENTRYCOMPACT*>( m_entries.get() );
for( size_t j = 0; j < m_data.dwEntryCount; ++j, ++ptr )
ptr->BigEndian();
}
else
{
auto ptr = reinterpret_cast<ENTRY*>( m_entries.get() );
for( size_t j = 0; j < m_data.dwEntryCount; ++j, ++ptr )
ptr->BigEndian();
}
}
// Load seek tables (XMA2 / xWMA)
DWORD seekLen = m_header.Segments[HEADER::SEGIDX_SEEKTABLES].dwLength;
if ( seekLen > 0 )
{
m_seekData.reset( new (std::nothrow) uint8_t[ seekLen ] );
if ( !m_seekData )
return E_OUTOFMEMORY;
memset( &request, 0, sizeof(OVERLAPPED) );
request.Offset = m_header.Segments[HEADER::SEGIDX_SEEKTABLES].dwOffset;
request.hEvent = m_event.get();
wait = false;
if ( !ReadFile( hFile.get(), m_seekData.get(), seekLen, nullptr, &request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
wait = true;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
result = GetOverlappedResultEx( hFile.get(), &request, &bytes, INFINITE, FALSE );
#else
if ( wait )
(void)WaitForSingleObject( m_event.get(), INFINITE );
result = GetOverlappedResult( hFile.get(), &request, &bytes, FALSE );
#endif
if ( !result || ( seekLen != bytes ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( be )
{
auto ptr = reinterpret_cast<uint32_t*>( m_seekData.get() );
for( size_t j = 0; j < seekLen; j += 4, ++ptr )
{
*ptr = _byteswap_ulong( *ptr );
}
}
}
DWORD waveLen = m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwLength;
if ( !waveLen )
{
return HRESULT_FROM_WIN32( ERROR_NO_DATA );
}
if ( m_data.dwFlags & BANKDATA::TYPE_STREAMING )
{
// If streaming, reopen without buffering
hFile.reset();
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0 };
params2.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
params2.dwFileFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING;
m_async = CreateFile2( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
¶ms2 );
#else
m_async = CreateFileW( szFileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
nullptr );
#endif
if ( m_async == INVALID_HANDLE_VALUE )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
m_prepared = true;
}
else
{
// If in-memory, kick off read of wave data
void *dest;
#if defined(_XBOX_ONE) && defined(_TITLE)
bool xma = false;
if ( m_data.dwFlags & BANKDATA::FLAGS_COMPACT )
{
if ( m_data.CompactFormat.wFormatTag == MINIWAVEFORMAT::TAG_XMA )
xma = true;
}
else
{
for( uint32_t j = 0; j < m_data.dwEntryCount; ++j )
{
auto& entry = reinterpret_cast<const ENTRY*>( m_entries.get() )[ j ];
if ( entry.Format.wFormatTag == MINIWAVEFORMAT::TAG_XMA )
{
xma = true;
break;
}
}
}
if ( xma )
{
HRESULT hr = ApuAlloc( &m_xmaMemory, nullptr, waveLen, SHAPE_XMA_INPUT_BUFFER_ALIGNMENT );
if ( FAILED(hr) )
{
return hr;
}
dest = m_xmaMemory;
}
else
#endif // _XBOX_ONE && _TITLE
{
m_waveData.reset( new (std::nothrow) uint8_t[ waveLen ] );
if ( !m_waveData )
return E_OUTOFMEMORY;
dest = m_waveData.get();
}
memset( &m_request, 0, sizeof(OVERLAPPED) );
m_request.Offset = m_header.Segments[HEADER::SEGIDX_ENTRYWAVEDATA].dwOffset;
m_request.hEvent = m_event.get();
if ( !ReadFile( hFile.get(), dest, waveLen, nullptr, &m_request ) )
{
DWORD error = GetLastError();
if ( error != ERROR_IO_PENDING )
return HRESULT_FROM_WIN32( error );
}
else
{
m_prepared = true;
memset( &m_request, 0, sizeof(OVERLAPPED) );
}
m_async = hFile.release();
}
return S_OK;
}