mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-09 20:12:21 +02:00
324 lines
11 KiB
C++
324 lines
11 KiB
C++
![]() |
#include "BFPlatform.h"
|
||
|
|
||
|
#ifdef BF_WWISE_ENABLED
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// AkDefaultIOHookBlocking.cpp
|
||
|
//
|
||
|
// Default blocking low level IO hook (AK::StreamMgr::IAkIOHookBlocking)
|
||
|
// and file system (AK::StreamMgr::IAkFileLocationResolver) implementation
|
||
|
// on Windows. It can be used as a standalone implementation of the
|
||
|
// Low-Level I/O system.
|
||
|
//
|
||
|
// AK::StreamMgr::IAkFileLocationResolver:
|
||
|
// Resolves file location using simple path concatenation logic
|
||
|
// (implemented in ../Common/CAkFileLocationBase). It can be used as a
|
||
|
// standalone Low-Level IO system, or as part of a multi device system.
|
||
|
// In the latter case, you should manage multiple devices by implementing
|
||
|
// AK::StreamMgr::IAkFileLocationResolver elsewhere (you may take a look
|
||
|
// at class CAkDefaultLowLevelIODispatcher).
|
||
|
//
|
||
|
// AK::StreamMgr::IAkIOHookBlocking:
|
||
|
// Uses platform API for I/O. Calls to ::ReadFile() and ::WriteFile()
|
||
|
// block because files are opened without the FILE_FLAG_OVERLAPPED flag.
|
||
|
// The AK::StreamMgr::IAkIOHookBlocking interface is meant to be used with
|
||
|
// AK_SCHEDULER_BLOCKING streaming devices.
|
||
|
//
|
||
|
// Init() creates a streaming device (by calling AK::StreamMgr::CreateDevice()).
|
||
|
// AkDeviceSettings::uSchedulerTypeFlags is set inside to AK_SCHEDULER_BLOCKING.
|
||
|
// If there was no AK::StreamMgr::IAkFileLocationResolver previously registered
|
||
|
// to the Stream Manager, this object registers itself as the File Location Resolver.
|
||
|
//
|
||
|
// Copyright (c) 2006 Audiokinetic Inc. / All Rights Reserved
|
||
|
//
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "Common.h"
|
||
|
#include "AkDefaultIOHookBlocking.h"
|
||
|
#include "AkFileHelpers.h"
|
||
|
|
||
|
|
||
|
#define WIN32_BLOCKING_DEVICE_NAME (AKTEXT("Win32 Blocking")) // Default blocking device name.
|
||
|
|
||
|
CAkDefaultIOHookBlocking::CAkDefaultIOHookBlocking()
|
||
|
: m_deviceID( AK_INVALID_DEVICE_ID )
|
||
|
, m_bAsyncOpen( false )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CAkDefaultIOHookBlocking::~CAkDefaultIOHookBlocking()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Initialization/termination. Init() registers this object as the one and
|
||
|
// only File Location Resolver if none were registered before. Then
|
||
|
// it creates a streaming device with scheduler type AK_SCHEDULER_BLOCKING.
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Init(
|
||
|
const AkDeviceSettings & in_deviceSettings, // Device settings.
|
||
|
bool in_bAsyncOpen/*=false*/ // If true, files are opened asynchronously when possible.
|
||
|
)
|
||
|
{
|
||
|
if ( in_deviceSettings.uSchedulerTypeFlags != AK_SCHEDULER_BLOCKING )
|
||
|
{
|
||
|
AKASSERT( !"CAkDefaultIOHookBlocking I/O hook only works with AK_SCHEDULER_BLOCKING devices" );
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
|
||
|
m_bAsyncOpen = in_bAsyncOpen;
|
||
|
|
||
|
// If the Stream Manager's File Location Resolver was not set yet, set this object as the
|
||
|
// File Location Resolver (this I/O hook is also able to resolve file location).
|
||
|
if ( !AK::StreamMgr::GetFileLocationResolver() )
|
||
|
AK::StreamMgr::SetFileLocationResolver( this );
|
||
|
|
||
|
// Create a device in the Stream Manager, specifying this as the hook.
|
||
|
m_deviceID = AK::StreamMgr::CreateDevice( in_deviceSettings, this );
|
||
|
if ( m_deviceID != AK_INVALID_DEVICE_ID )
|
||
|
return AK_Success;
|
||
|
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
|
||
|
void CAkDefaultIOHookBlocking::Term()
|
||
|
{
|
||
|
if ( AK::StreamMgr::GetFileLocationResolver() == this )
|
||
|
AK::StreamMgr::SetFileLocationResolver( NULL );
|
||
|
AK::StreamMgr::DestroyDevice( m_deviceID );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// IAkFileLocationAware interface.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
// Returns a file descriptor for a given file name (string).
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Open(
|
||
|
const AkOSChar* in_pszFileName, // File name.
|
||
|
AkOpenMode in_eOpenMode, // Open mode.
|
||
|
AkFileSystemFlags * in_pFlags, // Special flags. Can pass NULL.
|
||
|
bool & io_bSyncOpen, // If true, the file must be opened synchronously. Otherwise it is left at the File Location Resolver's discretion. Return false if Open needs to be deferred.
|
||
|
AkFileDesc & out_fileDesc // Returned file descriptor.
|
||
|
)
|
||
|
{
|
||
|
// We normally consider that calls to ::CreateFile() on a hard drive are fast enough to execute in the
|
||
|
// client thread. If you want files to be opened asynchronously when it is possible, this device should
|
||
|
// be initialized with the flag in_bAsyncOpen set to true.
|
||
|
if ( io_bSyncOpen || !m_bAsyncOpen )
|
||
|
{
|
||
|
io_bSyncOpen = true;
|
||
|
|
||
|
// Get the full file path, using path concatenation logic.
|
||
|
AkOSChar szFullFilePath[AK_MAX_PATH];
|
||
|
if ( GetFullFilePath( in_pszFileName, in_pFlags, in_eOpenMode, szFullFilePath ) == AK_Success )
|
||
|
{
|
||
|
// Open the file without FILE_FLAG_OVERLAPPED and FILE_FLAG_NO_BUFFERING flags.
|
||
|
AKRESULT eResult = CAkFileHelpers::OpenFile(
|
||
|
szFullFilePath,
|
||
|
in_eOpenMode,
|
||
|
false,
|
||
|
false,
|
||
|
out_fileDesc.hFile );
|
||
|
if ( eResult == AK_Success )
|
||
|
{
|
||
|
#ifdef AK_USE_METRO_API
|
||
|
FILE_STANDARD_INFO info;
|
||
|
::GetFileInformationByHandleEx( out_fileDesc.hFile, FileStandardInfo, &info, sizeof(info) );
|
||
|
out_fileDesc.iFileSize = info.EndOfFile.QuadPart;
|
||
|
#else
|
||
|
ULARGE_INTEGER Temp;
|
||
|
Temp.LowPart = ::GetFileSize( out_fileDesc.hFile,(LPDWORD)&Temp.HighPart );
|
||
|
out_fileDesc.iFileSize = Temp.QuadPart;
|
||
|
#endif
|
||
|
out_fileDesc.uSector = 0;
|
||
|
out_fileDesc.deviceID = m_deviceID;
|
||
|
out_fileDesc.pCustomParam = NULL;
|
||
|
out_fileDesc.uCustomParamSize = 0;
|
||
|
}
|
||
|
return eResult;
|
||
|
}
|
||
|
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The client allows us to perform asynchronous opening.
|
||
|
// We only need to specify the deviceID, and leave the boolean to false.
|
||
|
out_fileDesc.iFileSize = 0;
|
||
|
out_fileDesc.uSector = 0;
|
||
|
out_fileDesc.deviceID = m_deviceID;
|
||
|
out_fileDesc.pCustomParam = NULL;
|
||
|
out_fileDesc.uCustomParamSize = 0;
|
||
|
return AK_Success;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns a file descriptor for a given file ID.
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Open(
|
||
|
AkFileID in_fileID, // File ID.
|
||
|
AkOpenMode in_eOpenMode, // Open mode.
|
||
|
AkFileSystemFlags * in_pFlags, // Special flags. Can pass NULL.
|
||
|
bool & io_bSyncOpen, // If true, the file must be opened synchronously. Otherwise it is left at the File Location Resolver's discretion. Return false if Open needs to be deferred.
|
||
|
AkFileDesc & out_fileDesc // Returned file descriptor.
|
||
|
)
|
||
|
{
|
||
|
// We normally consider that calls to ::CreateFile() on a hard drive are fast enough to execute in the
|
||
|
// client thread. If you want files to be opened asynchronously when it is possible, this device should
|
||
|
// be initialized with the flag in_bAsyncOpen set to true.
|
||
|
if ( io_bSyncOpen || !m_bAsyncOpen )
|
||
|
{
|
||
|
io_bSyncOpen = true;
|
||
|
|
||
|
// Get the full file path, using path concatenation logic.
|
||
|
AkOSChar szFullFilePath[AK_MAX_PATH];
|
||
|
if ( GetFullFilePath( in_fileID, in_pFlags, in_eOpenMode, szFullFilePath ) == AK_Success )
|
||
|
{
|
||
|
// Open the file without FILE_FLAG_OVERLAPPED and FILE_FLAG_NO_BUFFERING flags.
|
||
|
AKRESULT eResult = CAkFileHelpers::OpenFile(
|
||
|
szFullFilePath,
|
||
|
in_eOpenMode,
|
||
|
false,
|
||
|
false,
|
||
|
out_fileDesc.hFile );
|
||
|
if ( eResult == AK_Success )
|
||
|
{
|
||
|
#ifdef AK_USE_METRO_API
|
||
|
FILE_STANDARD_INFO info;
|
||
|
::GetFileInformationByHandleEx( out_fileDesc.hFile, FileStandardInfo, &info, sizeof(info) );
|
||
|
out_fileDesc.iFileSize = info.EndOfFile.QuadPart;
|
||
|
#else
|
||
|
ULARGE_INTEGER Temp;
|
||
|
Temp.LowPart = ::GetFileSize( out_fileDesc.hFile,(LPDWORD)&Temp.HighPart );
|
||
|
out_fileDesc.iFileSize = Temp.QuadPart;
|
||
|
#endif
|
||
|
out_fileDesc.uSector = 0;
|
||
|
out_fileDesc.deviceID = m_deviceID;
|
||
|
out_fileDesc.pCustomParam = NULL;
|
||
|
out_fileDesc.uCustomParamSize = 0;
|
||
|
}
|
||
|
return eResult;
|
||
|
}
|
||
|
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The client allows us to perform asynchronous opening.
|
||
|
// We only need to specify the deviceID, and leave the boolean to false.
|
||
|
out_fileDesc.iFileSize = 0;
|
||
|
out_fileDesc.uSector = 0;
|
||
|
out_fileDesc.deviceID = m_deviceID;
|
||
|
out_fileDesc.pCustomParam = NULL;
|
||
|
out_fileDesc.uCustomParamSize = 0;
|
||
|
return AK_Success;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// IAkIOHookBlocking implementation.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
// Reads data from a file (synchronous).
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Read(
|
||
|
AkFileDesc & in_fileDesc, // File descriptor.
|
||
|
const AkIoHeuristics & /*in_heuristics*/, // Heuristics for this data transfer (not used in this implementation).
|
||
|
void * out_pBuffer, // Buffer to be filled with data.
|
||
|
AkIOTransferInfo & io_transferInfo // Synchronous data transfer info.
|
||
|
)
|
||
|
{
|
||
|
AKASSERT( out_pBuffer &&
|
||
|
in_fileDesc.hFile != INVALID_HANDLE_VALUE );
|
||
|
|
||
|
OVERLAPPED overlapped;
|
||
|
overlapped.Offset = (DWORD)( io_transferInfo.uFilePosition & 0xFFFFFFFF );
|
||
|
overlapped.OffsetHigh = (DWORD)( ( io_transferInfo.uFilePosition >> 32 ) & 0xFFFFFFFF );
|
||
|
overlapped.hEvent = NULL;
|
||
|
|
||
|
AkUInt32 uSizeTransferred;
|
||
|
|
||
|
if ( ::ReadFile(
|
||
|
in_fileDesc.hFile,
|
||
|
out_pBuffer,
|
||
|
io_transferInfo.uRequestedSize,
|
||
|
&uSizeTransferred,
|
||
|
&overlapped ) )
|
||
|
{
|
||
|
AKASSERT( uSizeTransferred == io_transferInfo.uRequestedSize );
|
||
|
return AK_Success;
|
||
|
}
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
|
||
|
// Writes data to a file (synchronous).
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Write(
|
||
|
AkFileDesc & in_fileDesc, // File descriptor.
|
||
|
const AkIoHeuristics & /*in_heuristics*/, // Heuristics for this data transfer (not used in this implementation).
|
||
|
void * in_pData, // Data to be written.
|
||
|
AkIOTransferInfo & io_transferInfo // Synchronous data transfer info.
|
||
|
)
|
||
|
{
|
||
|
AKASSERT( in_pData &&
|
||
|
in_fileDesc.hFile != INVALID_HANDLE_VALUE );
|
||
|
|
||
|
OVERLAPPED overlapped;
|
||
|
overlapped.Offset = (DWORD)( io_transferInfo.uFilePosition & 0xFFFFFFFF );
|
||
|
overlapped.OffsetHigh = (DWORD)( ( io_transferInfo.uFilePosition >> 32 ) & 0xFFFFFFFF );
|
||
|
overlapped.hEvent = NULL;
|
||
|
|
||
|
AkUInt32 uSizeTransferred;
|
||
|
|
||
|
if ( ::WriteFile(
|
||
|
in_fileDesc.hFile,
|
||
|
in_pData,
|
||
|
io_transferInfo.uRequestedSize,
|
||
|
&uSizeTransferred,
|
||
|
&overlapped ) )
|
||
|
{
|
||
|
AKASSERT( uSizeTransferred == io_transferInfo.uRequestedSize );
|
||
|
return AK_Success;
|
||
|
}
|
||
|
return AK_Fail;
|
||
|
}
|
||
|
|
||
|
// Cleans up a file.
|
||
|
AKRESULT CAkDefaultIOHookBlocking::Close(
|
||
|
AkFileDesc & in_fileDesc // File descriptor.
|
||
|
)
|
||
|
{
|
||
|
return CAkFileHelpers::CloseFile( in_fileDesc.hFile );
|
||
|
}
|
||
|
|
||
|
// Returns the block size for the file or its storage device.
|
||
|
AkUInt32 CAkDefaultIOHookBlocking::GetBlockSize(
|
||
|
AkFileDesc & /*in_fileDesc*/ // File descriptor.
|
||
|
)
|
||
|
{
|
||
|
// No constraint on block size (file seeking).
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Returns a description for the streaming device above this low-level hook.
|
||
|
void CAkDefaultIOHookBlocking::GetDeviceDesc(
|
||
|
AkDeviceDesc &
|
||
|
#ifndef AK_OPTIMIZED
|
||
|
out_deviceDesc // Description of associated low-level I/O device.
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
#ifndef AK_OPTIMIZED
|
||
|
AKASSERT( m_deviceID != AK_INVALID_DEVICE_ID || !"Low-Level device was not initialized" );
|
||
|
out_deviceDesc.deviceID = m_deviceID;
|
||
|
out_deviceDesc.bCanRead = true;
|
||
|
out_deviceDesc.bCanWrite = true;
|
||
|
AKPLATFORM::SafeStrCpy( out_deviceDesc.szDeviceName, WIN32_BLOCKING_DEVICE_NAME, AK_MONITOR_DEVICENAME_MAXLENGTH );
|
||
|
out_deviceDesc.uStringSize = (AkUInt32)wcslen( out_deviceDesc.szDeviceName ) + 1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Returns custom profiling data: 1 if file opens are asynchronous, 0 otherwise.
|
||
|
AkUInt32 CAkDefaultIOHookBlocking::GetDeviceData()
|
||
|
{
|
||
|
return ( m_bAsyncOpen ) ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
#endif
|