1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-09 20:12:21 +02:00
Beef/BeefySysLib/sound/Win32/AkDefaultIOHookBlocking.cpp
2019-08-23 11:56:54 -07:00

324 lines
No EOL
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