HRESULT WaveFindFormatAndData()

in Audio/WAVFileReader.cpp [135:301]


    HRESULT WaveFindFormatAndData(
        _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
        _In_ size_t wavDataSize,
        _Outptr_ const WAVEFORMATEX** pwfx,
        _Outptr_ const uint8_t** pdata,
        _Out_ uint32_t* dataSize,
        _Out_ bool& dpds,
        _Out_ bool& seek) noexcept
    {
        if (!wavData || !pwfx)
            return E_POINTER;

        dpds = seek = false;

        if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(uint32_t) + sizeof(WAVEFORMAT)))
        {
            return E_FAIL;
        }

        const uint8_t* wavEnd = wavData + wavDataSize;

        // Locate RIFF 'WAVE'
        auto riffChunk = FindChunk(wavData, wavDataSize, FOURCC_RIFF_TAG);
        if (!riffChunk || riffChunk->size < 4)
        {
            return E_FAIL;
        }

        auto riffHeader = reinterpret_cast<const RIFFChunkHeader*>(riffChunk);
        if (riffHeader->riff != FOURCC_WAVE_FILE_TAG && riffHeader->riff != FOURCC_XWMA_FILE_TAG)
        {
            return E_FAIL;
        }

        // Locate 'fmt '
        auto ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
        if ((ptr + sizeof(RIFFChunk)) > wavEnd)
        {
            return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        }

        auto fmtChunk = FindChunk(ptr, riffHeader->size, FOURCC_FORMAT_TAG);
        if (!fmtChunk || fmtChunk->size < sizeof(PCMWAVEFORMAT))
        {
            return E_FAIL;
        }

        ptr = reinterpret_cast<const uint8_t*>(fmtChunk) + sizeof(RIFFChunk);
        if (ptr + fmtChunk->size > wavEnd)
        {
            return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        }

        auto wf = reinterpret_cast<const WAVEFORMAT*>(ptr);

        // Validate WAVEFORMAT (focused on chunk size and format tag, not other data that XAUDIO2 will validate)
        switch (wf->wFormatTag)
        {
            case WAVE_FORMAT_PCM:
            case WAVE_FORMAT_IEEE_FLOAT:
                // Can be a PCMWAVEFORMAT (16 bytes) or WAVEFORMATEX (18 bytes)
                // We validiated chunk as at least sizeof(PCMWAVEFORMAT) above
                break;

            default:
            {
                if (fmtChunk->size < sizeof(WAVEFORMATEX))
                {
                    return E_FAIL;
                }

                auto wfx = reinterpret_cast<const WAVEFORMATEX*>(ptr);

                if (fmtChunk->size < (sizeof(WAVEFORMATEX) + wfx->cbSize))
                {
                    return E_FAIL;
                }

                switch (wfx->wFormatTag)
                {
                    case WAVE_FORMAT_WMAUDIO2:
                    case WAVE_FORMAT_WMAUDIO3:
                        dpds = true;
                        break;

                    case  0x166 /*WAVE_FORMAT_XMA2*/: // XMA2 is supported by Xbox One
                        if ((fmtChunk->size < 52 /*sizeof(XMA2WAVEFORMATEX)*/) || (wfx->cbSize < 34 /*( sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX) )*/))
                        {
                            return E_FAIL;
                        }
                        seek = true;
                        break;

                    case WAVE_FORMAT_ADPCM:
                        if ((fmtChunk->size < (sizeof(WAVEFORMATEX) + 32)) || (wfx->cbSize < 32 /*MSADPCM_FORMAT_EXTRA_BYTES*/))
                        {
                            return E_FAIL;
                        }
                        break;

                    case WAVE_FORMAT_EXTENSIBLE:
                        if ((fmtChunk->size < sizeof(WAVEFORMATEXTENSIBLE)) || (wfx->cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))))
                        {
                            return E_FAIL;
                        }
                        else
                        {
                            static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };

                            auto wfex = reinterpret_cast<const WAVEFORMATEXTENSIBLE*>(ptr);

                            if (memcmp(reinterpret_cast<const BYTE*>(&wfex->SubFormat) + sizeof(DWORD),
                                reinterpret_cast<const BYTE*>(&s_wfexBase) + sizeof(DWORD), sizeof(GUID) - sizeof(DWORD)) != 0)
                            {
                                return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                            }

                            switch (wfex->SubFormat.Data1)
                            {
                                case WAVE_FORMAT_PCM:
                                case WAVE_FORMAT_IEEE_FLOAT:
                                    break;

                                // MS-ADPCM and XMA2 are not supported as WAVEFORMATEXTENSIBLE

                                case WAVE_FORMAT_WMAUDIO2:
                                case WAVE_FORMAT_WMAUDIO3:
                                    dpds = true;
                                    break;

                                default:
                                    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                            }

                        }
                        break;

                    default:
                        return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
                }
            }
        }

        // Locate 'data'
        ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
        if ((ptr + sizeof(RIFFChunk)) > wavEnd)
        {
            return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        }

        auto dataChunk = FindChunk(ptr, riffChunk->size, FOURCC_DATA_TAG);
        if (!dataChunk || !dataChunk->size)
        {
            return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }

        ptr = reinterpret_cast<const uint8_t*>(dataChunk) + sizeof(RIFFChunk);
        if (ptr + dataChunk->size > wavEnd)
        {
            return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
        }

        *pwfx = reinterpret_cast<const WAVEFORMATEX*>(wf);
        *pdata = ptr;
        *dataSize = dataChunk->size;
        return S_OK;
    }