HRESULT WaveBankReader::Impl::Open()

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,
                                                  &params ) ) );
#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,
                               &params2 );
#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;
}