in Audio/WaveBankReader.cpp [505:914]
HRESULT WaveBankReader::Impl::Open(const wchar_t* szFileName) noexcept(false)
{
Close();
Clear();
m_prepared = false;
m_event.reset(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE));
if (!m_event)
{
return HRESULT_FROM_WIN32(GetLastError());
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(CREATEFILE2_EXTENDED_PARAMETERS), 0, 0, 0, {}, nullptr };
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)
std::ignore = 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)
{
DebugTrace("INFO: \"%ls\" is a big-endian (Xbox 360) wave bank\n", szFileName);
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)
std::ignore = 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)
std::ignore = 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] = {};
strncpy_s(name, &temp[n], sizeof(name));
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)
std::ignore = 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)
std::ignore = 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, 0, 0, {}, nullptr };
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 = nullptr;
#ifdef DIRECTX_ENABLE_XMA2
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))
{
DebugTrace("ERROR: ApuAlloc failed. Did you allocate a large enough heap with ApuCreateHeap for all your XMA wave data?\n");
return hr;
}
dest = m_xmaMemory;
}
else
#endif // XMA2
{
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;
}